diff --git a/activity_dashboard_mngmnt/README.rst b/activity_dashboard_mngmnt/README.rst new file mode 100644 index 000000000..37f5988e2 --- /dev/null +++ b/activity_dashboard_mngmnt/README.rst @@ -0,0 +1,18 @@ +Activity Management v16 +====================== +Activity Management + +Installation +============ + - www.odoo.com/documentation/16.0/setup/install.html + - Install our custom addon + +Configuration +============= + + No additional configurations needed + +Credits +======= + Developer: Megha v15 @ cybrosys, Contact: odoo@cybrosys.com + Amaya Aravind EV v16 @ cybrosys, Contact: odoo@cybrosys.com diff --git a/activity_dashboard_mngmnt/__init__.py b/activity_dashboard_mngmnt/__init__.py new file mode 100644 index 000000000..7d16d55b6 --- /dev/null +++ b/activity_dashboard_mngmnt/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +################################################################################### +# Activity Management +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2022-TODAY Cybrosys Technologies (). +# Author: Megha K () +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### + +from . import models diff --git a/activity_dashboard_mngmnt/__manifest__.py b/activity_dashboard_mngmnt/__manifest__.py new file mode 100644 index 000000000..65f2932a3 --- /dev/null +++ b/activity_dashboard_mngmnt/__manifest__.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +################################################################################### +# Activity Management +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2022-TODAY Cybrosys Technologies (). +# Author: Megha K () +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### + +{ + 'name': 'Activity Management', + 'version': '16.0.1.0.0', + 'category': 'Tools', + 'summary': 'Advance Activity Management and Dashboard View', + 'author': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'website': 'https://www.cybrosys.com', + 'description': "Advance Activity Management and Dashboard View", + 'depends': ['base', 'mail'], + 'images': ['static/description/banner.png'], + 'data': [ + 'security/ir.model.access.csv', + 'views/my_activity.xml', + 'views/activity_tag.xml', + 'views/activity_dashbord.xml' + ], + 'assets': { + 'web.assets_backend': [ + 'activity_dashboard_mngmnt/static/src/css/dashboard.css', + 'activity_dashboard_mngmnt/static/src/css/style.scss', + 'activity_dashboard_mngmnt/static/src/css/material-gauge.css', + 'activity_dashboard_mngmnt/static/src/xml/activity_dashboard_view.xml', + 'activity_dashboard_mngmnt/static/src/js/activity_dashboard.js', + ], + }, + 'installable': True, + 'application': False, + 'auto_install': False, + 'license': 'AGPL-3', +} diff --git a/activity_dashboard_mngmnt/doc/RELEASE_NOTES.md b/activity_dashboard_mngmnt/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..c1e80861f --- /dev/null +++ b/activity_dashboard_mngmnt/doc/RELEASE_NOTES.md @@ -0,0 +1,6 @@ +## Module + +#### 16.12.2022 +#### Version 16.0.1.0.0 +##### ADD +- Initial commit for activity_dashboard_mngmnt \ No newline at end of file diff --git a/activity_dashboard_mngmnt/models/__init__.py b/activity_dashboard_mngmnt/models/__init__.py new file mode 100644 index 000000000..93a2c2803 --- /dev/null +++ b/activity_dashboard_mngmnt/models/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +################################################################################### +# Freight Management +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2022-TODAY Cybrosys Technologies (). +# Author: Megha K () +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### + +from . import mail_activity +from . import activity_type diff --git a/activity_dashboard_mngmnt/models/activity_type.py b/activity_dashboard_mngmnt/models/activity_type.py new file mode 100644 index 000000000..04fe2fffe --- /dev/null +++ b/activity_dashboard_mngmnt/models/activity_type.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +################################################################################### +# Activity Management +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2022-TODAY Cybrosys Technologies (). +# Author: Megha K () +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### + +from random import randint +from odoo import fields, models + + +class ActivityTag(models.Model): + _name = "activity.tag" + _description = "Activity Tag" + + def _get_default_color(self): + return randint(1, 11) + + name = fields.Char('Tag Name', required=True, translate=True) + color = fields.Integer('Color', default=_get_default_color) + + _sql_constraints = [ + ('name_uniq', 'unique (name)', "Tag name already exists !"), + ] diff --git a/activity_dashboard_mngmnt/models/mail_activity.py b/activity_dashboard_mngmnt/models/mail_activity.py new file mode 100644 index 000000000..bee010c04 --- /dev/null +++ b/activity_dashboard_mngmnt/models/mail_activity.py @@ -0,0 +1,179 @@ +# -*- coding: utf-8 -*- +################################################################################### +# Activity Management +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2022-TODAY Cybrosys Technologies (). +# Author: Megha K () +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### + +from datetime import date, datetime +from collections import defaultdict +import pytz + +from odoo import models, fields, api, _, Command +from odoo.exceptions import UserError + + +class MailActivity(models.Model): + _inherit = "mail.activity" + + state = fields.Selection([ + ('overdue', 'Overdue'), ('today', 'Today'), ('planned', 'Planned'), + ('done', 'Done'), ('cancel', 'Cancelled')], 'State', + compute='_compute_state', store=True) + active = fields.Boolean('Active', default=True) + + type = fields.Selection( + [('overdue', 'Overdue'), ('today', 'Today'), ('planned', 'Planned'), + ('done', 'Done'), ('cancel', 'Cancelled')]) + activity_type = fields.Many2many('activity.tag') + + def activity_cancel(self): + """cancel activity""" + for rec in self: + if rec.state == 'cancel': + raise UserError( + _("You Cant Cancelled this activity %s") % rec.res_name) + else: + rec.action_cancel() + + def activity_done(self): + """done activity""" + for rec in self: + if rec.state == 'done': + raise UserError( + _("You Cant Cancelled this activity %s") % rec.res_name) + else: + rec._action_done() + + def get_activity_count(self): + """get the activity count details""" + activity = self.env['mail.activity'] + all = activity.search([]) + planned = activity.search([('state', '=', 'planned')]) + overdue = activity.search([('state', '=', 'overdue')]) + today = activity.search([('state', '=', 'today')]) + done = activity.search([('state', '=', 'done'), ('active', '=', False)]) + cancel = activity.search([('state', '=', 'cancel')]) + return { + 'len_all': len(all), + 'len_overdue': len(overdue), + 'len_planned': len(planned), + 'len_today': len(today), + 'len_done': len(done), + 'len_cancel': len(cancel) + } + + def get_activity(self, id): + activity = self.env['mail.activity'].search([('id', '=', id)]) + return { + 'model': activity.res_model, + 'res_id': activity.res_id + } + + def _action_done(self, feedback=False, attachment_ids=None): + """action done function: rewrite the function""" + messages = self.env['mail.message'] + next_activities_values = [] + + attachments = self.env['ir.attachment'].search_read([ + ('res_model', '=', self._name), + ('res_id', 'in', self.ids), + ], ['id', 'res_id']) + + activity_attachments = defaultdict(list) + for attachment in attachments: + activity_id = attachment['res_id'] + activity_attachments[activity_id].append(attachment['id']) + + for activity in self: + if activity.chaining_type == 'trigger': + vals = activity.with_context( + activity_previous_deadline=activity.date_deadline)._prepare_next_activity_values() + next_activities_values.append(vals) + + # post message on activity, before deleting it + record = self.env[activity.res_model].browse(activity.res_id) + record.message_post_with_view( + 'mail.message_activity_done', + values={ + 'activity': activity, + 'feedback': feedback, + 'display_assignee': activity.user_id != self.env.user + }, + subtype_id=self.env['ir.model.data']._xmlid_to_res_id( + 'mail.mt_activities'), + mail_activity_type_id=activity.activity_type_id.id, + attachment_ids=[Command.link(attachment_id) for attachment_id in + attachment_ids] if attachment_ids else [], + ) + + activity_message = record.message_ids[0] + message_attachments = self.env['ir.attachment'].browse( + activity_attachments[activity.id]) + if message_attachments: + message_attachments.write({ + 'res_id': activity_message.id, + 'res_model': activity_message._name, + }) + activity_message.attachment_ids = message_attachments + messages |= activity_message + + next_activities = self.env['mail.activity'].create( + next_activities_values) + for rec in self: + rec.state = 'done' + rec.active = False + + return messages, next_activities + + @api.model + def _compute_state_from_date(self, date_deadline, tz=False): + """Compute the state""" + date_deadline = fields.Date.from_string(date_deadline) + today_default = date.today() + today = today_default + if tz: + today_utc = pytz.utc.localize(datetime.utcnow()) + today_tz = today_utc.astimezone(pytz.timezone(tz)) + today = date(year=today_tz.year, month=today_tz.month, + day=today_tz.day) + diff = (date_deadline - today) + for rec in self: + if rec.state == 'done': + return 'done' + elif rec.type == 'cancel': + return 'cancel' + else: + if diff.days == 0: + return 'today' + elif diff.days < 0: + return 'overdue' + else: + return 'planned' + + def action_cancel(self): + """cancel activities""" + for rec in self: + rec.state = 'cancel' + + @api.depends('state') + def _onchange_state(self): + """change state and type""" + for rec in self: + rec.type = rec.state diff --git a/activity_dashboard_mngmnt/security/ir.model.access.csv b/activity_dashboard_mngmnt/security/ir.model.access.csv new file mode 100644 index 000000000..5098712b6 --- /dev/null +++ b/activity_dashboard_mngmnt/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_activity_tag,activity.tag,model_activity_tag,base.group_user,1,1,1,1 diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/check.png b/activity_dashboard_mngmnt/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/check.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/chevron.png b/activity_dashboard_mngmnt/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/chevron.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/cogs.png b/activity_dashboard_mngmnt/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/cogs.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/consultation.png b/activity_dashboard_mngmnt/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/consultation.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/ecom-black.png b/activity_dashboard_mngmnt/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/ecom-black.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/education-black.png b/activity_dashboard_mngmnt/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/education-black.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/hotel-black.png b/activity_dashboard_mngmnt/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/hotel-black.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/license.png b/activity_dashboard_mngmnt/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/license.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/lifebuoy.png b/activity_dashboard_mngmnt/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/lifebuoy.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/manufacturing-black.png b/activity_dashboard_mngmnt/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/manufacturing-black.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/pos-black.png b/activity_dashboard_mngmnt/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/pos-black.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/puzzle.png b/activity_dashboard_mngmnt/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/puzzle.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/restaurant-black.png b/activity_dashboard_mngmnt/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/restaurant-black.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/service-black.png b/activity_dashboard_mngmnt/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/service-black.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/trading-black.png b/activity_dashboard_mngmnt/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/trading-black.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/training.png b/activity_dashboard_mngmnt/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/training.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/update.png b/activity_dashboard_mngmnt/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/update.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/user.png b/activity_dashboard_mngmnt/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/user.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/icons/wrench.png b/activity_dashboard_mngmnt/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/icons/wrench.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/misc/categories.png b/activity_dashboard_mngmnt/static/description/assets/misc/categories.png new file mode 100644 index 000000000..bedf1e0b1 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/misc/categories.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/misc/check-box.png b/activity_dashboard_mngmnt/static/description/assets/misc/check-box.png new file mode 100644 index 000000000..42caf24b9 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/misc/check-box.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/misc/compass.png b/activity_dashboard_mngmnt/static/description/assets/misc/compass.png new file mode 100644 index 000000000..d5fed8faa Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/misc/compass.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/misc/corporate.png b/activity_dashboard_mngmnt/static/description/assets/misc/corporate.png new file mode 100644 index 000000000..2eb13edbf Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/misc/corporate.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/misc/customer-support.png b/activity_dashboard_mngmnt/static/description/assets/misc/customer-support.png new file mode 100644 index 000000000..79efc72ed Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/misc/customer-support.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/misc/cybrosys-logo.png b/activity_dashboard_mngmnt/static/description/assets/misc/cybrosys-logo.png new file mode 100644 index 000000000..cc3cc0ccf Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/misc/cybrosys-logo.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/misc/features.png b/activity_dashboard_mngmnt/static/description/assets/misc/features.png new file mode 100644 index 000000000..b41769f77 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/misc/features.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/misc/logo.png b/activity_dashboard_mngmnt/static/description/assets/misc/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/misc/logo.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/misc/pictures.png b/activity_dashboard_mngmnt/static/description/assets/misc/pictures.png new file mode 100644 index 000000000..56d255fe9 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/misc/pictures.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/misc/pie-chart.png b/activity_dashboard_mngmnt/static/description/assets/misc/pie-chart.png new file mode 100644 index 000000000..426e05244 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/misc/pie-chart.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/misc/right-arrow.png b/activity_dashboard_mngmnt/static/description/assets/misc/right-arrow.png new file mode 100644 index 000000000..730984a06 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/misc/right-arrow.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/misc/star.png b/activity_dashboard_mngmnt/static/description/assets/misc/star.png new file mode 100644 index 000000000..2eb9ab29f Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/misc/star.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/misc/support.png b/activity_dashboard_mngmnt/static/description/assets/misc/support.png new file mode 100644 index 000000000..4f18b8b82 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/misc/support.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/misc/whatsapp.png b/activity_dashboard_mngmnt/static/description/assets/misc/whatsapp.png new file mode 100644 index 000000000..d513a5356 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/misc/whatsapp.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/modules/1.png b/activity_dashboard_mngmnt/static/description/assets/modules/1.png new file mode 100644 index 000000000..5238bdeab Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/modules/1.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/modules/2.png b/activity_dashboard_mngmnt/static/description/assets/modules/2.png new file mode 100644 index 000000000..1ae7cfe3b Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/modules/2.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/modules/3.png b/activity_dashboard_mngmnt/static/description/assets/modules/3.png new file mode 100644 index 000000000..3c3ff1afb Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/modules/3.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/modules/4.png b/activity_dashboard_mngmnt/static/description/assets/modules/4.png new file mode 100644 index 000000000..3fae4631e Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/modules/4.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/modules/5.gif b/activity_dashboard_mngmnt/static/description/assets/modules/5.gif new file mode 100644 index 000000000..2a5f8e659 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/modules/5.gif differ diff --git a/activity_dashboard_mngmnt/static/description/assets/modules/6.png b/activity_dashboard_mngmnt/static/description/assets/modules/6.png new file mode 100644 index 000000000..7f2815273 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/modules/6.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/screenshots/1.png b/activity_dashboard_mngmnt/static/description/assets/screenshots/1.png new file mode 100644 index 000000000..5ce3c0e51 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/screenshots/1.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/screenshots/2.png b/activity_dashboard_mngmnt/static/description/assets/screenshots/2.png new file mode 100644 index 000000000..789c0b9db Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/screenshots/2.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/screenshots/3.png b/activity_dashboard_mngmnt/static/description/assets/screenshots/3.png new file mode 100644 index 000000000..9f4c5c7f2 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/screenshots/3.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/screenshots/4.png b/activity_dashboard_mngmnt/static/description/assets/screenshots/4.png new file mode 100644 index 000000000..0ae22e21f Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/screenshots/4.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/screenshots/5.png b/activity_dashboard_mngmnt/static/description/assets/screenshots/5.png new file mode 100644 index 000000000..f31b3b8bc Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/screenshots/5.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/screenshots/6-1.png b/activity_dashboard_mngmnt/static/description/assets/screenshots/6-1.png new file mode 100644 index 000000000..de664b923 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/screenshots/6-1.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/screenshots/6.png b/activity_dashboard_mngmnt/static/description/assets/screenshots/6.png new file mode 100644 index 000000000..96aab827a Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/screenshots/6.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/screenshots/7.png b/activity_dashboard_mngmnt/static/description/assets/screenshots/7.png new file mode 100644 index 000000000..574d87225 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/screenshots/7.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/screenshots/8.png b/activity_dashboard_mngmnt/static/description/assets/screenshots/8.png new file mode 100644 index 000000000..da7d94fcd Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/screenshots/8.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/screenshots/9.png b/activity_dashboard_mngmnt/static/description/assets/screenshots/9.png new file mode 100644 index 000000000..45f9b28e7 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/screenshots/9.png differ diff --git a/activity_dashboard_mngmnt/static/description/assets/screenshots/hero.gif b/activity_dashboard_mngmnt/static/description/assets/screenshots/hero.gif new file mode 100644 index 000000000..0eae68487 Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/assets/screenshots/hero.gif differ diff --git a/activity_dashboard_mngmnt/static/description/banner.png b/activity_dashboard_mngmnt/static/description/banner.png new file mode 100644 index 000000000..29a17f2be Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/banner.png differ diff --git a/activity_dashboard_mngmnt/static/description/icon.png b/activity_dashboard_mngmnt/static/description/icon.png new file mode 100644 index 000000000..8716623ab Binary files /dev/null and b/activity_dashboard_mngmnt/static/description/icon.png differ diff --git a/activity_dashboard_mngmnt/static/description/index.html b/activity_dashboard_mngmnt/static/description/index.html new file mode 100644 index 000000000..11b2a03c8 --- /dev/null +++ b/activity_dashboard_mngmnt/static/description/index.html @@ -0,0 +1,588 @@ +
+ +
+ +
+
+ Community +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+ +
+
+
+ +

+ Activity Management

+

Advance Activity Management and Dashboard View

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

Explore This + Module

+
+ + + + +
+
+ +
+

Overview +

+
+
+
+ This module helps you to manage all activity. +
+
+ + + +
+
+ +
+

Features +

+
+
+
+
+ + Activity Dashboard. +
+
+ + Manage origin of activity +
+
+ + Activity tags. +
+
+
+ + Mass cancel and Done +
+
+ + +
+
+ + + +
+
+ +
+

Screenshots +

+
+
+
+ +
+

Activity Dashboard +

+

This is Activity Dashboard.

+ +
+ +
+

Planned Activities +

+

View the planned activities table.

+ +
+ +
+

Activities +

+

Details of completed, overdue and today's activities.

+ +
+ +
+

View the Activity +

+

We can view the activity By clicking the view button.

+ +
+ +
+

View the Origin of Activity +

+

We can view the origin of the activity By clicking the view button.

+ +
+ +
+

Origin of activity +

+ + + +
+ +
+

Activity Tag +

+

We can create manage the Activity Tags.

+ + +
+ +
+

Mass Done +

+

We can make Mass Done.

+ +
+ +
+

Mass Cancel +

+

We can make Mass Cancel.

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

Related + Products +

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

Our Services +

+
+ +
+
+
+
+ +
+
+ Odoo + Customization
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Support
+
+ + +
+
+ +
+
+ Hire + Odoo + Developer
+
+ +
+
+ +
+
+ Odoo + Integration
+
+ +
+
+ +
+
+ Odoo + Migration
+
+ + +
+
+ +
+
+ Odoo + Consultancy
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Licensing Consultancy
+
+
+ +
+ + + + + +
+
+ +
+

Our + Industries +

+
+ +
+
+
+
+ +
+ Trading +
+

+ Easily procure + and + sell your products

+
+
+ +
+
+ +
+ POS +
+

+ Easy + configuration + and convivial experience

+
+
+ +
+
+ +
+ Education +
+

+ A platform for + educational management

+
+
+ +
+
+ +
+ Manufacturing +
+

+ Plan, track and + schedule your operations

+
+
+ +
+
+ +
+ E-commerce & Website +
+

+ Mobile + friendly, + awe-inspiring product pages

+
+
+ +
+
+ +
+ Service Management +
+

+ Keep track of + services and invoice

+
+
+ +
+
+ +
+ Restaurant +
+

+ Run your bar or + restaurant methodically

+
+
+ +
+
+ +
+ Hotel Management +
+

+ An + all-inclusive + hotel management application

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

Support +

+
+
+
+
+
+
+ +
+
+

Need Help?

+

Got questions or need help? Get in touch.

+ +

+ odoo@cybrosys.com

+
+
+
+
+
+
+
+ +
+
+

WhatsApp

+

Say hi to us on WhatsApp!

+ +

+91 86068 + 27707

+
+
+
+
+
+
+
+ +
+
+
+ \ No newline at end of file diff --git a/activity_dashboard_mngmnt/static/src/css/dashboard.css b/activity_dashboard_mngmnt/static/src/css/dashboard.css new file mode 100644 index 000000000..335e5831e --- /dev/null +++ b/activity_dashboard_mngmnt/static/src/css/dashboard.css @@ -0,0 +1,34 @@ +p, span, a, ul, li, button { + font-size: inherit; + font-weight: inherit; + line-height: inherit; +} + +strong { + font-weight: 600; +} + +h1, h2, h3, h4, h5, h6 { + line-height: 1.5em; + font-weight: 300; +} + +strong { + font-weight: 400; +} + +.sub_title { + font-size: 14px; +} + +.sub_title div span { + font-weight: 600; +} + +.chart #canvas_graph { + height: 400px !important; +} + +.highcharts-background { + fill: none; +} \ No newline at end of file diff --git a/activity_dashboard_mngmnt/static/src/css/material-gauge.css b/activity_dashboard_mngmnt/static/src/css/material-gauge.css new file mode 100644 index 000000000..7f553df9a --- /dev/null +++ b/activity_dashboard_mngmnt/static/src/css/material-gauge.css @@ -0,0 +1,194 @@ +/* + * #### Gauge Component + * + * The standard markup for the component is: + * + *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * No + * + * Yes + *
+ *
+ */ + +/* + * First define all of the relevant rules that aren't dependent + * on the size of the gauge. We want to collect the size-depenent + * rules in one place to make it easier to adjust the size. + */ + +.gauge { + position: relative; +} + +.gauge__container { + margin: 0; + padding: 0; + position: absolute; + left: 50%; + overflow: hidden; + text-align: center; + -webkit-transform: translateX(-50%); + -moz-transform: translateX(-50%); + -ms-transform: translateX(-50%); + -o-transform: translateX(-50%); + transform: translateX(-50%); +} + +.gauge__background { + z-index: 0; + position: absolute; + background-color: #d8f0de; + top: 0; + border-radius: 300px 300px 0 0; +} + +.gauge__data { + z-index: 1; + position: absolute; + background-color: #00c29d; + margin-left: auto; + margin-right: auto; + border-radius: 300px 300px 0 0; + -webkit-transform-origin: center bottom; + -moz-transform-origin: center bottom; + -ms-transform-origin: center bottom; + -o-transform-origin: center bottom; + transform-origin: center bottom; +} + +.gauge__center { + z-index: 2; + position: absolute; + background-color: #f9f9f9; + margin-right: auto; + border-radius: 300px 300px 0 0; +} + +.gauge__marker { + z-index: 3; + background-color: #fff; + position: absolute; + width: 1px; +} + +.gauge__needle { + z-index: 4; + background-color: #21242c; + height: 3px; + position: absolute; + -webkit-transform-origin: left center; + -moz-transform-origin: left center; + -ms-transform-origin: left center; + -o-transform-origin: left center; + transform-origin: left center; +} + +.gauge__labels { + display: table; + margin: 0 auto; + position: relative; +} + +.gauge__label--low { + display: table-cell; + text-align: center; + color: #00c29d; +} + +.gauge__label--spacer { + display: table-cell; +} + +.gauge__label--high { + display: table-cell; + text-align: center; + color: #979f99; +} + +/* + * Now define the rules that depend on the size of + * the gauge. We start with sizing for a small mobile + * device. + */ + +.gauge { height: calc(120px + 3em); } +.gauge__container { width: 240px; height: 120px; } +.gauge__marker { height: 120px; left: 119.5px; } +.gauge__background { width: 240px; height: 120px; } +.gauge__center { width: 144px; height: 72px; top: 48px; margin-left: 48px; } +.gauge__data { width: 240px; height: 120px; } +.gauge__needle { left: 120px; top: 117px; width: 120px; } +.gauge__labels { top: 120px; width: 240px; } +.gauge__label--low { width: 48px; } +.gauge__label--spacer { width: 144px; } +.gauge__label--high { width: 48px; } + +/* + * Increase the gauge size slightly on larger viewports. + */ + + @media only screen and (min-width: 400px) { + .gauge { height: calc(150px + 3em); } + .gauge__container { width: 300px; height: 150px; } + .gauge__marker { height: 150px; left: 149.5px; } + .gauge__background { width: 300px; height: 150px; } + .gauge__center { width: 180px; height: 90px; top: 60px; margin-left: 60px; } + .gauge__data { width: 300px; height: 150px; } + .gauge__needle { left: 150px; top: 147px; width: 150px; } + .gauge__labels { top: 160px; width: 300px; font-size: 20px;} + .gauge__label--low { width: 60px; } + .gauge__label--spacer { width: 180px; } + .gauge__label--high { width: 60px; } +} + +/* + * As an option, the `gauge--liveupdate` class can be added + * to the main gauge element. When this class is present, + * we add a transition that animates any changes to the gauge + * value. Currently, the app does not use this option because + * all the inputs that can change gauge values are present + * on tab panels that are different from the gauge itself. + * Therefore, users won't be able to see any gauge changes + * when they make input changes. The code is available, though, + * should this change. + */ + +.gauge--liveupdate .gauge__data, +.gauge--liveupdate .gauge__needle { + -webkit-transition: all 1s ease-in-out; + -moz-transition: all 1s ease-in-out; + -ms-transition: all 1s ease-in-out; + -o-transition: all 1s ease-in-out; + transition: all 1s ease-in-out; +} + +/* + * For a given gauge value, x, ranging from 0.0 to 1.0, set + * the `transform: rotate()` property according to the + * following equation: `-0.5 + 0.5x turns` The default + * properties below represent an x value of 0. + */ + +.gauge__data { + -webkit-transform: rotate(-.50turn); + -moz-transform: rotate(-.50turn); + -ms-transform: rotate(-.50turn); + -o-transform: rotate(-.50turn); + transform: rotate(-.50turn); +} +.gauge__needle { + -webkit-transform: rotate(-.50turn); + -moz-transform: rotate(-.50turn); + -ms-transform: rotate(-.50turn); + -o-transform: rotate(-.50turn); + transform: rotate(-.50turn); +} diff --git a/activity_dashboard_mngmnt/static/src/css/style.scss b/activity_dashboard_mngmnt/static/src/css/style.scss new file mode 100644 index 000000000..d861d3e66 --- /dev/null +++ b/activity_dashboard_mngmnt/static/src/css/style.scss @@ -0,0 +1,64 @@ +.o_action_manager { + direction: ltr; + webkit-box-flex: 1; + webkit-flex: 1 1 auto; + flex: 1 1 auto; + overflow: scroll !important; +} +.activity-dashboard-card { + border-radius: 0.3rem; + display: flex; + justify-content: center; + padding: 1.7rem 1.5rem 1.5rem 1.5rem; + margin: 1rem auto; + height: 100px; +} +.activity-dashboard-card__icon-container { + height: 50px; + width: 50px; + border-radius: 50%; +} +.my_activity { + background-color: #229db9; + border-radius: 10px; + font-size: 35px; +} +.all_activity{ + background: #88dfdf; + border-radius: 10px; + font-size: 35px; +} +.planned_activity { + background-color: #e8c5ac; + border-radius: 10px; + font-size: 35px; +} +.completed_activity{ + background: #a7a1f4;; + border-radius: 10px; + font-size: 35px; +} + +.today_activity{ + background: #f3bdf4; + border-radius: 10px; + font-size: 35px; +} +.overdue_activity { + background-color: #a2d2fb; + border-radius: 10px; + font-size: 35px; +} +.cancelled_activity{ + background: #beeecc; + border-radius: 10px; + font-size: 35px; +} +.activity_type { + background-color: #ecb9b9;; + border-radius: 10px; + font-size: 35px; +} +.dashboard_main_section { + margin: 20px; +} \ No newline at end of file diff --git a/activity_dashboard_mngmnt/static/src/js/activity_dashboard.js b/activity_dashboard_mngmnt/static/src/js/activity_dashboard.js new file mode 100644 index 000000000..ecf7375fb --- /dev/null +++ b/activity_dashboard_mngmnt/static/src/js/activity_dashboard.js @@ -0,0 +1,255 @@ +odoo.define('activity_dashboard_mngmnt.activity_dashboard', function (require) { + 'use strict'; + var AbstractAction = require('web.AbstractAction'); + var core = require('web.core'); + var rpc = require('web.rpc'); + var QWeb = core.qweb; + var session = require('web.session'); + var user = session.user_id + + var ActivityDashboard = AbstractAction.extend({ + template: 'ActivityDashboard', + events: { + 'click .all_activity': 'all_activity', + 'click .planned_activity': 'planned_activity', + 'click .completed_activity': 'completed_activity', + 'click .today_activity': 'today_activity', + 'click .overdue_activity': 'overdue_activity', + 'click .cancelled_activity': 'cancelled_activity', + 'click .activity_type': 'activity_type', + 'click .click-view': 'click_view', + 'click .click-origin-view': 'click_origin_view' + }, + init: function(parent, context) { + this._super(parent, context); + this.upcoming_events = []; + this.dashboards_templates = ['LoginUser', 'ManageActivity', 'ActivityTable']; + this.login_employee = []; + }, + + start: function() { + var self = this; + this.set("title", 'Dashboard'); + return this._super().then(function() { + self.render_dashboards(); + }); + }, + + render_dashboards: function () { + var self = this; + this._rpc({ + model: 'mail.activity', + method : 'get_activity_count', + args: [[]] + }).then(function(result){ + self.$('.table_view').html(QWeb.render('ManageActivity', { + len_all: result.len_all, + len_planned: result.len_planned, + len_done: result.len_done, + len_today: result.len_today, + len_overdue: result.len_overdue, + len_cancel: result.len_cancel + })); + }); +// this.$('.table_view').html(QWeb.render('ManageActivity')); + self.results = '' + self._rpc({ + model: 'mail.activity', + method : 'search_read', + domain: [["state", "=", 'done'], ["active", "=", false]], + }).then(function(done_activity){ + self._rpc({ + model: 'mail.activity', + method : 'search_read', + domain: [["state", "=", 'planned']], + }).then(function(planned_activity){ + self._rpc({ + model: 'mail.activity', + method : 'search_read', + domain: [["state", "=", 'today']], + }).then(function(today_activity){ + self._rpc({ + model: 'mail.activity', + method : 'search_read', + domain: [["state", "=", 'overdue']], + }).then(function(overdue_activity){ + self.$('.table_view_activity').html(QWeb.render('ActivityTable', { + done_activity: done_activity, + planned_activity: planned_activity, + today_activity: today_activity, + overdue_activity: overdue_activity + })); + }); + }); + }); + }); + }, + click_view: function(e){ + var id = e.target.value + this.do_action({ + type: 'ir.actions.act_window', + name: 'All Activity', + res_model: 'mail.activity', + domain: [['id', '=', id]], + views: [[false, 'list'], [false, 'form']], + view_mode: 'list,form', + target: 'current' + }); + }, + click_origin_view: function(e){ + var id_ = e.target.value; + var self = this; + this._rpc({ + model: 'mail.activity', + method : 'get_activity', + args: [[],parseInt(id_)], + }).then(function(result){this + self.do_action({ + type: 'ir.actions.act_window', + name: 'Activity Origin', + res_model: result.model, + domain: [['id', '=', result.res_id]], + views: [[false, 'list'], [false, 'form']], + view_mode: 'list,form', + target: 'current' + }); + }); + }, + all_activity: function(e) { + var self = this; + e.stopPropagation(); + e.preventDefault(); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + this.do_action({ + type: 'ir.actions.act_window', + name: 'All Activity', + res_model: 'mail.activity', + // res_id: [1], + domain: [], + views: [[false, 'list'], [false, 'form']], + view_mode: 'list', + target: 'current' + }); + }, + + planned_activity: function(e) { + var self = this; + e.stopPropagation(); + e.preventDefault(); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + this.do_action({ + type: 'ir.actions.act_window', + name: 'Planned Activity', + res_model: 'mail.activity', + domain: [['state', '=', 'planned']], + views: [[false, 'list'], [false, 'form']], + view_mode: 'list', + target: 'current' + }); + }, + + completed_activity: function(e) { + var self = this; + e.stopPropagation(); + e.preventDefault(); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + this.do_action({ + type: 'ir.actions.act_window', + name: 'Completed Activity', + res_model: 'mail.activity', + domain: [['state', '=', 'done'], ['active', '=', false]], + views: [[false, 'list'], [false, 'form']], + view_mode: 'list', + target: 'current' + }); + }, + + today_activity: function(e) { + var self = this; + e.stopPropagation(); + e.preventDefault(); + + this.do_action({ + type: 'ir.actions.act_window', + name: "Today's Activities", + res_model: 'mail.activity', + domain: [['state', '=', 'today']], + views: [[false, 'list'], [false, 'form']], + view_mode: 'list', + target: 'current' + }); + }, + + overdue_activity: function(e) { + var self = this; + e.stopPropagation(); + e.preventDefault(); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + this.do_action({ + type: 'ir.actions.act_window', + name: 'Overdue Activity', + res_model: 'mail.activity', + domain: [['state', '=', 'overdue']], + views: [[false, 'list'], [false, 'form']], + view_mode: 'list', + target: 'current' + }); + + }, + + cancelled_activity: function(e) { + var self = this; + e.stopPropagation(); + e.preventDefault(); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + this.do_action({ + type: 'ir.actions.act_window', + name: "Today's Activity", + res_model: 'mail.activity', + domain: [['state', '=', 'cancel']], + views: [[false, 'list'], [false, 'form']], + view_mode: 'list', + target: 'current' + }); + }, + + activity_type: function(e) { + var self = this; + e.stopPropagation(); + e.preventDefault(); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + var action = { + type: 'ir.actions.act_window', + name: 'Activity Type', + res_model: 'activity.type', + domain: [['state', 'in', ['today']]], + views: [[false, 'list'], [false, 'form']], + view_mode: 'list', + target: 'current' + } + this.do_action({ + type: 'ir.actions.act_window', + name: "Today's Activity", + res_model: 'mail.activity.type', + views: [[false, 'list'], [false, 'form']], + view_mode: 'list', + target: 'current' + }); + }, + +}); + core.action_registry.add("activity_dashboard", ActivityDashboard); + return ActivityDashboard; +}); diff --git a/activity_dashboard_mngmnt/static/src/js/lib/Chart.bundle.js b/activity_dashboard_mngmnt/static/src/js/lib/Chart.bundle.js new file mode 100644 index 000000000..33a955afe --- /dev/null +++ b/activity_dashboard_mngmnt/static/src/js/lib/Chart.bundle.js @@ -0,0 +1,19288 @@ +/*! + * Chart.js v2.8.0 + * https://www.chartjs.org + * (c) 2019 Chart.js Contributors + * Released under the MIT License + */ +(function (global, factory) { +typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : +typeof define === 'function' && define.amd ? define(factory) : +(global.Chart = factory()); +}(this, (function () { 'use strict'; + +/* MIT license */ + +var conversions = { + rgb2hsl: rgb2hsl, + rgb2hsv: rgb2hsv, + rgb2hwb: rgb2hwb, + rgb2cmyk: rgb2cmyk, + rgb2keyword: rgb2keyword, + rgb2xyz: rgb2xyz, + rgb2lab: rgb2lab, + rgb2lch: rgb2lch, + + hsl2rgb: hsl2rgb, + hsl2hsv: hsl2hsv, + hsl2hwb: hsl2hwb, + hsl2cmyk: hsl2cmyk, + hsl2keyword: hsl2keyword, + + hsv2rgb: hsv2rgb, + hsv2hsl: hsv2hsl, + hsv2hwb: hsv2hwb, + hsv2cmyk: hsv2cmyk, + hsv2keyword: hsv2keyword, + + hwb2rgb: hwb2rgb, + hwb2hsl: hwb2hsl, + hwb2hsv: hwb2hsv, + hwb2cmyk: hwb2cmyk, + hwb2keyword: hwb2keyword, + + cmyk2rgb: cmyk2rgb, + cmyk2hsl: cmyk2hsl, + cmyk2hsv: cmyk2hsv, + cmyk2hwb: cmyk2hwb, + cmyk2keyword: cmyk2keyword, + + keyword2rgb: keyword2rgb, + keyword2hsl: keyword2hsl, + keyword2hsv: keyword2hsv, + keyword2hwb: keyword2hwb, + keyword2cmyk: keyword2cmyk, + keyword2lab: keyword2lab, + keyword2xyz: keyword2xyz, + + xyz2rgb: xyz2rgb, + xyz2lab: xyz2lab, + xyz2lch: xyz2lch, + + lab2xyz: lab2xyz, + lab2rgb: lab2rgb, + lab2lch: lab2lch, + + lch2lab: lch2lab, + lch2xyz: lch2xyz, + lch2rgb: lch2rgb +}; + + +function rgb2hsl(rgb) { + var r = rgb[0]/255, + g = rgb[1]/255, + b = rgb[2]/255, + min = Math.min(r, g, b), + max = Math.max(r, g, b), + delta = max - min, + h, s, l; + + if (max == min) + h = 0; + else if (r == max) + h = (g - b) / delta; + else if (g == max) + h = 2 + (b - r) / delta; + else if (b == max) + h = 4 + (r - g)/ delta; + + h = Math.min(h * 60, 360); + + if (h < 0) + h += 360; + + l = (min + max) / 2; + + if (max == min) + s = 0; + else if (l <= 0.5) + s = delta / (max + min); + else + s = delta / (2 - max - min); + + return [h, s * 100, l * 100]; +} + +function rgb2hsv(rgb) { + var r = rgb[0], + g = rgb[1], + b = rgb[2], + min = Math.min(r, g, b), + max = Math.max(r, g, b), + delta = max - min, + h, s, v; + + if (max == 0) + s = 0; + else + s = (delta/max * 1000)/10; + + if (max == min) + h = 0; + else if (r == max) + h = (g - b) / delta; + else if (g == max) + h = 2 + (b - r) / delta; + else if (b == max) + h = 4 + (r - g) / delta; + + h = Math.min(h * 60, 360); + + if (h < 0) + h += 360; + + v = ((max / 255) * 1000) / 10; + + return [h, s, v]; +} + +function rgb2hwb(rgb) { + var r = rgb[0], + g = rgb[1], + b = rgb[2], + h = rgb2hsl(rgb)[0], + w = 1/255 * Math.min(r, Math.min(g, b)), + b = 1 - 1/255 * Math.max(r, Math.max(g, b)); + + return [h, w * 100, b * 100]; +} + +function rgb2cmyk(rgb) { + var r = rgb[0] / 255, + g = rgb[1] / 255, + b = rgb[2] / 255, + c, m, y, k; + + k = Math.min(1 - r, 1 - g, 1 - b); + c = (1 - r - k) / (1 - k) || 0; + m = (1 - g - k) / (1 - k) || 0; + y = (1 - b - k) / (1 - k) || 0; + return [c * 100, m * 100, y * 100, k * 100]; +} + +function rgb2keyword(rgb) { + return reverseKeywords[JSON.stringify(rgb)]; +} + +function rgb2xyz(rgb) { + var r = rgb[0] / 255, + g = rgb[1] / 255, + b = rgb[2] / 255; + + // assume sRGB + r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); + g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); + b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); + + var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); + var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); + var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); + + return [x * 100, y *100, z * 100]; +} + +function rgb2lab(rgb) { + var xyz = rgb2xyz(rgb), + x = xyz[0], + y = xyz[1], + z = xyz[2], + l, a, b; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); + + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); + + return [l, a, b]; +} + +function rgb2lch(args) { + return lab2lch(rgb2lab(args)); +} + +function hsl2rgb(hsl) { + var h = hsl[0] / 360, + s = hsl[1] / 100, + l = hsl[2] / 100, + t1, t2, t3, rgb, val; + + if (s == 0) { + val = l * 255; + return [val, val, val]; + } + + if (l < 0.5) + t2 = l * (1 + s); + else + t2 = l + s - l * s; + t1 = 2 * l - t2; + + rgb = [0, 0, 0]; + for (var i = 0; i < 3; i++) { + t3 = h + 1 / 3 * - (i - 1); + t3 < 0 && t3++; + t3 > 1 && t3--; + + if (6 * t3 < 1) + val = t1 + (t2 - t1) * 6 * t3; + else if (2 * t3 < 1) + val = t2; + else if (3 * t3 < 2) + val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; + else + val = t1; + + rgb[i] = val * 255; + } + + return rgb; +} + +function hsl2hsv(hsl) { + var h = hsl[0], + s = hsl[1] / 100, + l = hsl[2] / 100, + sv, v; + + if(l === 0) { + // no need to do calc on black + // also avoids divide by 0 error + return [0, 0, 0]; + } + + l *= 2; + s *= (l <= 1) ? l : 2 - l; + v = (l + s) / 2; + sv = (2 * s) / (l + s); + return [h, sv * 100, v * 100]; +} + +function hsl2hwb(args) { + return rgb2hwb(hsl2rgb(args)); +} + +function hsl2cmyk(args) { + return rgb2cmyk(hsl2rgb(args)); +} + +function hsl2keyword(args) { + return rgb2keyword(hsl2rgb(args)); +} + + +function hsv2rgb(hsv) { + var h = hsv[0] / 60, + s = hsv[1] / 100, + v = hsv[2] / 100, + hi = Math.floor(h) % 6; + + var f = h - Math.floor(h), + p = 255 * v * (1 - s), + q = 255 * v * (1 - (s * f)), + t = 255 * v * (1 - (s * (1 - f))), + v = 255 * v; + + switch(hi) { + case 0: + return [v, t, p]; + case 1: + return [q, v, p]; + case 2: + return [p, v, t]; + case 3: + return [p, q, v]; + case 4: + return [t, p, v]; + case 5: + return [v, p, q]; + } +} + +function hsv2hsl(hsv) { + var h = hsv[0], + s = hsv[1] / 100, + v = hsv[2] / 100, + sl, l; + + l = (2 - s) * v; + sl = s * v; + sl /= (l <= 1) ? l : 2 - l; + sl = sl || 0; + l /= 2; + return [h, sl * 100, l * 100]; +} + +function hsv2hwb(args) { + return rgb2hwb(hsv2rgb(args)) +} + +function hsv2cmyk(args) { + return rgb2cmyk(hsv2rgb(args)); +} + +function hsv2keyword(args) { + return rgb2keyword(hsv2rgb(args)); +} + +// http://dev.w3.org/csswg/css-color/#hwb-to-rgb +function hwb2rgb(hwb) { + var h = hwb[0] / 360, + wh = hwb[1] / 100, + bl = hwb[2] / 100, + ratio = wh + bl, + i, v, f, n; + + // wh + bl cant be > 1 + if (ratio > 1) { + wh /= ratio; + bl /= ratio; + } + + i = Math.floor(6 * h); + v = 1 - bl; + f = 6 * h - i; + if ((i & 0x01) != 0) { + f = 1 - f; + } + n = wh + f * (v - wh); // linear interpolation + + switch (i) { + default: + case 6: + case 0: r = v; g = n; b = wh; break; + case 1: r = n; g = v; b = wh; break; + case 2: r = wh; g = v; b = n; break; + case 3: r = wh; g = n; b = v; break; + case 4: r = n; g = wh; b = v; break; + case 5: r = v; g = wh; b = n; break; + } + + return [r * 255, g * 255, b * 255]; +} + +function hwb2hsl(args) { + return rgb2hsl(hwb2rgb(args)); +} + +function hwb2hsv(args) { + return rgb2hsv(hwb2rgb(args)); +} + +function hwb2cmyk(args) { + return rgb2cmyk(hwb2rgb(args)); +} + +function hwb2keyword(args) { + return rgb2keyword(hwb2rgb(args)); +} + +function cmyk2rgb(cmyk) { + var c = cmyk[0] / 100, + m = cmyk[1] / 100, + y = cmyk[2] / 100, + k = cmyk[3] / 100, + r, g, b; + + r = 1 - Math.min(1, c * (1 - k) + k); + g = 1 - Math.min(1, m * (1 - k) + k); + b = 1 - Math.min(1, y * (1 - k) + k); + return [r * 255, g * 255, b * 255]; +} + +function cmyk2hsl(args) { + return rgb2hsl(cmyk2rgb(args)); +} + +function cmyk2hsv(args) { + return rgb2hsv(cmyk2rgb(args)); +} + +function cmyk2hwb(args) { + return rgb2hwb(cmyk2rgb(args)); +} + +function cmyk2keyword(args) { + return rgb2keyword(cmyk2rgb(args)); +} + + +function xyz2rgb(xyz) { + var x = xyz[0] / 100, + y = xyz[1] / 100, + z = xyz[2] / 100, + r, g, b; + + r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); + g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); + b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); + + // assume sRGB + r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) + : r = (r * 12.92); + + g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) + : g = (g * 12.92); + + b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) + : b = (b * 12.92); + + r = Math.min(Math.max(0, r), 1); + g = Math.min(Math.max(0, g), 1); + b = Math.min(Math.max(0, b), 1); + + return [r * 255, g * 255, b * 255]; +} + +function xyz2lab(xyz) { + var x = xyz[0], + y = xyz[1], + z = xyz[2], + l, a, b; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); + + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); + + return [l, a, b]; +} + +function xyz2lch(args) { + return lab2lch(xyz2lab(args)); +} + +function lab2xyz(lab) { + var l = lab[0], + a = lab[1], + b = lab[2], + x, y, z, y2; + + if (l <= 8) { + y = (l * 100) / 903.3; + y2 = (7.787 * (y / 100)) + (16 / 116); + } else { + y = 100 * Math.pow((l + 16) / 116, 3); + y2 = Math.pow(y / 100, 1/3); + } + + x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); + + z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); + + return [x, y, z]; +} + +function lab2lch(lab) { + var l = lab[0], + a = lab[1], + b = lab[2], + hr, h, c; + + hr = Math.atan2(b, a); + h = hr * 360 / 2 / Math.PI; + if (h < 0) { + h += 360; + } + c = Math.sqrt(a * a + b * b); + return [l, c, h]; +} + +function lab2rgb(args) { + return xyz2rgb(lab2xyz(args)); +} + +function lch2lab(lch) { + var l = lch[0], + c = lch[1], + h = lch[2], + a, b, hr; + + hr = h / 360 * 2 * Math.PI; + a = c * Math.cos(hr); + b = c * Math.sin(hr); + return [l, a, b]; +} + +function lch2xyz(args) { + return lab2xyz(lch2lab(args)); +} + +function lch2rgb(args) { + return lab2rgb(lch2lab(args)); +} + +function keyword2rgb(keyword) { + return cssKeywords[keyword]; +} + +function keyword2hsl(args) { + return rgb2hsl(keyword2rgb(args)); +} + +function keyword2hsv(args) { + return rgb2hsv(keyword2rgb(args)); +} + +function keyword2hwb(args) { + return rgb2hwb(keyword2rgb(args)); +} + +function keyword2cmyk(args) { + return rgb2cmyk(keyword2rgb(args)); +} + +function keyword2lab(args) { + return rgb2lab(keyword2rgb(args)); +} + +function keyword2xyz(args) { + return rgb2xyz(keyword2rgb(args)); +} + +var cssKeywords = { + aliceblue: [240,248,255], + antiquewhite: [250,235,215], + aqua: [0,255,255], + aquamarine: [127,255,212], + azure: [240,255,255], + beige: [245,245,220], + bisque: [255,228,196], + black: [0,0,0], + blanchedalmond: [255,235,205], + blue: [0,0,255], + blueviolet: [138,43,226], + brown: [165,42,42], + burlywood: [222,184,135], + cadetblue: [95,158,160], + chartreuse: [127,255,0], + chocolate: [210,105,30], + coral: [255,127,80], + cornflowerblue: [100,149,237], + cornsilk: [255,248,220], + crimson: [220,20,60], + cyan: [0,255,255], + darkblue: [0,0,139], + darkcyan: [0,139,139], + darkgoldenrod: [184,134,11], + darkgray: [169,169,169], + darkgreen: [0,100,0], + darkgrey: [169,169,169], + darkkhaki: [189,183,107], + darkmagenta: [139,0,139], + darkolivegreen: [85,107,47], + darkorange: [255,140,0], + darkorchid: [153,50,204], + darkred: [139,0,0], + darksalmon: [233,150,122], + darkseagreen: [143,188,143], + darkslateblue: [72,61,139], + darkslategray: [47,79,79], + darkslategrey: [47,79,79], + darkturquoise: [0,206,209], + darkviolet: [148,0,211], + deeppink: [255,20,147], + deepskyblue: [0,191,255], + dimgray: [105,105,105], + dimgrey: [105,105,105], + dodgerblue: [30,144,255], + firebrick: [178,34,34], + floralwhite: [255,250,240], + forestgreen: [34,139,34], + fuchsia: [255,0,255], + gainsboro: [220,220,220], + ghostwhite: [248,248,255], + gold: [255,215,0], + goldenrod: [218,165,32], + gray: [128,128,128], + green: [0,128,0], + greenyellow: [173,255,47], + grey: [128,128,128], + honeydew: [240,255,240], + hotpink: [255,105,180], + indianred: [205,92,92], + indigo: [75,0,130], + ivory: [255,255,240], + khaki: [240,230,140], + lavender: [230,230,250], + lavenderblush: [255,240,245], + lawngreen: [124,252,0], + lemonchiffon: [255,250,205], + lightblue: [173,216,230], + lightcoral: [240,128,128], + lightcyan: [224,255,255], + lightgoldenrodyellow: [250,250,210], + lightgray: [211,211,211], + lightgreen: [144,238,144], + lightgrey: [211,211,211], + lightpink: [255,182,193], + lightsalmon: [255,160,122], + lightseagreen: [32,178,170], + lightskyblue: [135,206,250], + lightslategray: [119,136,153], + lightslategrey: [119,136,153], + lightsteelblue: [176,196,222], + lightyellow: [255,255,224], + lime: [0,255,0], + limegreen: [50,205,50], + linen: [250,240,230], + magenta: [255,0,255], + maroon: [128,0,0], + mediumaquamarine: [102,205,170], + mediumblue: [0,0,205], + mediumorchid: [186,85,211], + mediumpurple: [147,112,219], + mediumseagreen: [60,179,113], + mediumslateblue: [123,104,238], + mediumspringgreen: [0,250,154], + mediumturquoise: [72,209,204], + mediumvioletred: [199,21,133], + midnightblue: [25,25,112], + mintcream: [245,255,250], + mistyrose: [255,228,225], + moccasin: [255,228,181], + navajowhite: [255,222,173], + navy: [0,0,128], + oldlace: [253,245,230], + olive: [128,128,0], + olivedrab: [107,142,35], + orange: [255,165,0], + orangered: [255,69,0], + orchid: [218,112,214], + palegoldenrod: [238,232,170], + palegreen: [152,251,152], + paleturquoise: [175,238,238], + palevioletred: [219,112,147], + papayawhip: [255,239,213], + peachpuff: [255,218,185], + peru: [205,133,63], + pink: [255,192,203], + plum: [221,160,221], + powderblue: [176,224,230], + purple: [128,0,128], + rebeccapurple: [102, 51, 153], + red: [255,0,0], + rosybrown: [188,143,143], + royalblue: [65,105,225], + saddlebrown: [139,69,19], + salmon: [250,128,114], + sandybrown: [244,164,96], + seagreen: [46,139,87], + seashell: [255,245,238], + sienna: [160,82,45], + silver: [192,192,192], + skyblue: [135,206,235], + slateblue: [106,90,205], + slategray: [112,128,144], + slategrey: [112,128,144], + snow: [255,250,250], + springgreen: [0,255,127], + steelblue: [70,130,180], + tan: [210,180,140], + teal: [0,128,128], + thistle: [216,191,216], + tomato: [255,99,71], + turquoise: [64,224,208], + violet: [238,130,238], + wheat: [245,222,179], + white: [255,255,255], + whitesmoke: [245,245,245], + yellow: [255,255,0], + yellowgreen: [154,205,50] +}; + +var reverseKeywords = {}; +for (var key in cssKeywords) { + reverseKeywords[JSON.stringify(cssKeywords[key])] = key; +} + +var convert = function() { + return new Converter(); +}; + +for (var func in conversions) { + // export Raw versions + convert[func + "Raw"] = (function(func) { + // accept array or plain args + return function(arg) { + if (typeof arg == "number") + arg = Array.prototype.slice.call(arguments); + return conversions[func](arg); + } + })(func); + + var pair = /(\w+)2(\w+)/.exec(func), + from = pair[1], + to = pair[2]; + + // export rgb2hsl and ["rgb"]["hsl"] + convert[from] = convert[from] || {}; + + convert[from][to] = convert[func] = (function(func) { + return function(arg) { + if (typeof arg == "number") + arg = Array.prototype.slice.call(arguments); + + var val = conversions[func](arg); + if (typeof val == "string" || val === undefined) + return val; // keyword + + for (var i = 0; i < val.length; i++) + val[i] = Math.round(val[i]); + return val; + } + })(func); +} + + +/* Converter does lazy conversion and caching */ +var Converter = function() { + this.convs = {}; +}; + +/* Either get the values for a space or + set the values for a space, depending on args */ +Converter.prototype.routeSpace = function(space, args) { + var values = args[0]; + if (values === undefined) { + // color.rgb() + return this.getValues(space); + } + // color.rgb(10, 10, 10) + if (typeof values == "number") { + values = Array.prototype.slice.call(args); + } + + return this.setValues(space, values); +}; + +/* Set the values for a space, invalidating cache */ +Converter.prototype.setValues = function(space, values) { + this.space = space; + this.convs = {}; + this.convs[space] = values; + return this; +}; + +/* Get the values for a space. If there's already + a conversion for the space, fetch it, otherwise + compute it */ +Converter.prototype.getValues = function(space) { + var vals = this.convs[space]; + if (!vals) { + var fspace = this.space, + from = this.convs[fspace]; + vals = convert[fspace][space](from); + + this.convs[space] = vals; + } + return vals; +}; + +["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { + Converter.prototype[space] = function(vals) { + return this.routeSpace(space, arguments); + }; +}); + +var colorConvert = convert; + +var colorName = { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255, 235, 205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategray": [47, 79, 79], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "grey": [128, 128, 128], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "rebeccapurple": [102, 51, 153], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50] +}; + +/* MIT license */ + + +var colorString = { + getRgba: getRgba, + getHsla: getHsla, + getRgb: getRgb, + getHsl: getHsl, + getHwb: getHwb, + getAlpha: getAlpha, + + hexString: hexString, + rgbString: rgbString, + rgbaString: rgbaString, + percentString: percentString, + percentaString: percentaString, + hslString: hslString, + hslaString: hslaString, + hwbString: hwbString, + keyword: keyword +}; + +function getRgba(string) { + if (!string) { + return; + } + var abbr = /^#([a-fA-F0-9]{3,4})$/i, + hex = /^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i, + rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, + per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, + keyword = /(\w+)/; + + var rgb = [0, 0, 0], + a = 1, + match = string.match(abbr), + hexAlpha = ""; + if (match) { + match = match[1]; + hexAlpha = match[3]; + for (var i = 0; i < rgb.length; i++) { + rgb[i] = parseInt(match[i] + match[i], 16); + } + if (hexAlpha) { + a = Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100; + } + } + else if (match = string.match(hex)) { + hexAlpha = match[2]; + match = match[1]; + for (var i = 0; i < rgb.length; i++) { + rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16); + } + if (hexAlpha) { + a = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100; + } + } + else if (match = string.match(rgba)) { + for (var i = 0; i < rgb.length; i++) { + rgb[i] = parseInt(match[i + 1]); + } + a = parseFloat(match[4]); + } + else if (match = string.match(per)) { + for (var i = 0; i < rgb.length; i++) { + rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); + } + a = parseFloat(match[4]); + } + else if (match = string.match(keyword)) { + if (match[1] == "transparent") { + return [0, 0, 0, 0]; + } + rgb = colorName[match[1]]; + if (!rgb) { + return; + } + } + + for (var i = 0; i < rgb.length; i++) { + rgb[i] = scale(rgb[i], 0, 255); + } + if (!a && a != 0) { + a = 1; + } + else { + a = scale(a, 0, 1); + } + rgb[3] = a; + return rgb; +} + +function getHsla(string) { + if (!string) { + return; + } + var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; + var match = string.match(hsl); + if (match) { + var alpha = parseFloat(match[4]); + var h = scale(parseInt(match[1]), 0, 360), + s = scale(parseFloat(match[2]), 0, 100), + l = scale(parseFloat(match[3]), 0, 100), + a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); + return [h, s, l, a]; + } +} + +function getHwb(string) { + if (!string) { + return; + } + var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; + var match = string.match(hwb); + if (match) { + var alpha = parseFloat(match[4]); + var h = scale(parseInt(match[1]), 0, 360), + w = scale(parseFloat(match[2]), 0, 100), + b = scale(parseFloat(match[3]), 0, 100), + a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); + return [h, w, b, a]; + } +} + +function getRgb(string) { + var rgba = getRgba(string); + return rgba && rgba.slice(0, 3); +} + +function getHsl(string) { + var hsla = getHsla(string); + return hsla && hsla.slice(0, 3); +} + +function getAlpha(string) { + var vals = getRgba(string); + if (vals) { + return vals[3]; + } + else if (vals = getHsla(string)) { + return vals[3]; + } + else if (vals = getHwb(string)) { + return vals[3]; + } +} + +// generators +function hexString(rgba, a) { + var a = (a !== undefined && rgba.length === 3) ? a : rgba[3]; + return "#" + hexDouble(rgba[0]) + + hexDouble(rgba[1]) + + hexDouble(rgba[2]) + + ( + (a >= 0 && a < 1) + ? hexDouble(Math.round(a * 255)) + : "" + ); +} + +function rgbString(rgba, alpha) { + if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { + return rgbaString(rgba, alpha); + } + return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")"; +} + +function rgbaString(rgba, alpha) { + if (alpha === undefined) { + alpha = (rgba[3] !== undefined ? rgba[3] : 1); + } + return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + + ", " + alpha + ")"; +} + +function percentString(rgba, alpha) { + if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { + return percentaString(rgba, alpha); + } + var r = Math.round(rgba[0]/255 * 100), + g = Math.round(rgba[1]/255 * 100), + b = Math.round(rgba[2]/255 * 100); + + return "rgb(" + r + "%, " + g + "%, " + b + "%)"; +} + +function percentaString(rgba, alpha) { + var r = Math.round(rgba[0]/255 * 100), + g = Math.round(rgba[1]/255 * 100), + b = Math.round(rgba[2]/255 * 100); + return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")"; +} + +function hslString(hsla, alpha) { + if (alpha < 1 || (hsla[3] && hsla[3] < 1)) { + return hslaString(hsla, alpha); + } + return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)"; +} + +function hslaString(hsla, alpha) { + if (alpha === undefined) { + alpha = (hsla[3] !== undefined ? hsla[3] : 1); + } + return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " + + alpha + ")"; +} + +// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax +// (hwb have alpha optional & 1 is default value) +function hwbString(hwb, alpha) { + if (alpha === undefined) { + alpha = (hwb[3] !== undefined ? hwb[3] : 1); + } + return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%" + + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")"; +} + +function keyword(rgb) { + return reverseNames[rgb.slice(0, 3)]; +} + +// helpers +function scale(num, min, max) { + return Math.min(Math.max(min, num), max); +} + +function hexDouble(num) { + var str = num.toString(16).toUpperCase(); + return (str.length < 2) ? "0" + str : str; +} + + +//create a list of reverse color names +var reverseNames = {}; +for (var name in colorName) { + reverseNames[colorName[name]] = name; +} + +/* MIT license */ + + + +var Color = function (obj) { + if (obj instanceof Color) { + return obj; + } + if (!(this instanceof Color)) { + return new Color(obj); + } + + this.valid = false; + this.values = { + rgb: [0, 0, 0], + hsl: [0, 0, 0], + hsv: [0, 0, 0], + hwb: [0, 0, 0], + cmyk: [0, 0, 0, 0], + alpha: 1 + }; + + // parse Color() argument + var vals; + if (typeof obj === 'string') { + vals = colorString.getRgba(obj); + if (vals) { + this.setValues('rgb', vals); + } else if (vals = colorString.getHsla(obj)) { + this.setValues('hsl', vals); + } else if (vals = colorString.getHwb(obj)) { + this.setValues('hwb', vals); + } + } else if (typeof obj === 'object') { + vals = obj; + if (vals.r !== undefined || vals.red !== undefined) { + this.setValues('rgb', vals); + } else if (vals.l !== undefined || vals.lightness !== undefined) { + this.setValues('hsl', vals); + } else if (vals.v !== undefined || vals.value !== undefined) { + this.setValues('hsv', vals); + } else if (vals.w !== undefined || vals.whiteness !== undefined) { + this.setValues('hwb', vals); + } else if (vals.c !== undefined || vals.cyan !== undefined) { + this.setValues('cmyk', vals); + } + } +}; + +Color.prototype = { + isValid: function () { + return this.valid; + }, + rgb: function () { + return this.setSpace('rgb', arguments); + }, + hsl: function () { + return this.setSpace('hsl', arguments); + }, + hsv: function () { + return this.setSpace('hsv', arguments); + }, + hwb: function () { + return this.setSpace('hwb', arguments); + }, + cmyk: function () { + return this.setSpace('cmyk', arguments); + }, + + rgbArray: function () { + return this.values.rgb; + }, + hslArray: function () { + return this.values.hsl; + }, + hsvArray: function () { + return this.values.hsv; + }, + hwbArray: function () { + var values = this.values; + if (values.alpha !== 1) { + return values.hwb.concat([values.alpha]); + } + return values.hwb; + }, + cmykArray: function () { + return this.values.cmyk; + }, + rgbaArray: function () { + var values = this.values; + return values.rgb.concat([values.alpha]); + }, + hslaArray: function () { + var values = this.values; + return values.hsl.concat([values.alpha]); + }, + alpha: function (val) { + if (val === undefined) { + return this.values.alpha; + } + this.setValues('alpha', val); + return this; + }, + + red: function (val) { + return this.setChannel('rgb', 0, val); + }, + green: function (val) { + return this.setChannel('rgb', 1, val); + }, + blue: function (val) { + return this.setChannel('rgb', 2, val); + }, + hue: function (val) { + if (val) { + val %= 360; + val = val < 0 ? 360 + val : val; + } + return this.setChannel('hsl', 0, val); + }, + saturation: function (val) { + return this.setChannel('hsl', 1, val); + }, + lightness: function (val) { + return this.setChannel('hsl', 2, val); + }, + saturationv: function (val) { + return this.setChannel('hsv', 1, val); + }, + whiteness: function (val) { + return this.setChannel('hwb', 1, val); + }, + blackness: function (val) { + return this.setChannel('hwb', 2, val); + }, + value: function (val) { + return this.setChannel('hsv', 2, val); + }, + cyan: function (val) { + return this.setChannel('cmyk', 0, val); + }, + magenta: function (val) { + return this.setChannel('cmyk', 1, val); + }, + yellow: function (val) { + return this.setChannel('cmyk', 2, val); + }, + black: function (val) { + return this.setChannel('cmyk', 3, val); + }, + + hexString: function () { + return colorString.hexString(this.values.rgb); + }, + rgbString: function () { + return colorString.rgbString(this.values.rgb, this.values.alpha); + }, + rgbaString: function () { + return colorString.rgbaString(this.values.rgb, this.values.alpha); + }, + percentString: function () { + return colorString.percentString(this.values.rgb, this.values.alpha); + }, + hslString: function () { + return colorString.hslString(this.values.hsl, this.values.alpha); + }, + hslaString: function () { + return colorString.hslaString(this.values.hsl, this.values.alpha); + }, + hwbString: function () { + return colorString.hwbString(this.values.hwb, this.values.alpha); + }, + keyword: function () { + return colorString.keyword(this.values.rgb, this.values.alpha); + }, + + rgbNumber: function () { + var rgb = this.values.rgb; + return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; + }, + + luminosity: function () { + // http://www.w3.org/TR/WCAG20/#relativeluminancedef + var rgb = this.values.rgb; + var lum = []; + for (var i = 0; i < rgb.length; i++) { + var chan = rgb[i] / 255; + lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4); + } + return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2]; + }, + + contrast: function (color2) { + // http://www.w3.org/TR/WCAG20/#contrast-ratiodef + var lum1 = this.luminosity(); + var lum2 = color2.luminosity(); + if (lum1 > lum2) { + return (lum1 + 0.05) / (lum2 + 0.05); + } + return (lum2 + 0.05) / (lum1 + 0.05); + }, + + level: function (color2) { + var contrastRatio = this.contrast(color2); + if (contrastRatio >= 7.1) { + return 'AAA'; + } + + return (contrastRatio >= 4.5) ? 'AA' : ''; + }, + + dark: function () { + // YIQ equation from http://24ways.org/2010/calculating-color-contrast + var rgb = this.values.rgb; + var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; + return yiq < 128; + }, + + light: function () { + return !this.dark(); + }, + + negate: function () { + var rgb = []; + for (var i = 0; i < 3; i++) { + rgb[i] = 255 - this.values.rgb[i]; + } + this.setValues('rgb', rgb); + return this; + }, + + lighten: function (ratio) { + var hsl = this.values.hsl; + hsl[2] += hsl[2] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + darken: function (ratio) { + var hsl = this.values.hsl; + hsl[2] -= hsl[2] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + saturate: function (ratio) { + var hsl = this.values.hsl; + hsl[1] += hsl[1] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + desaturate: function (ratio) { + var hsl = this.values.hsl; + hsl[1] -= hsl[1] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + whiten: function (ratio) { + var hwb = this.values.hwb; + hwb[1] += hwb[1] * ratio; + this.setValues('hwb', hwb); + return this; + }, + + blacken: function (ratio) { + var hwb = this.values.hwb; + hwb[2] += hwb[2] * ratio; + this.setValues('hwb', hwb); + return this; + }, + + greyscale: function () { + var rgb = this.values.rgb; + // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale + var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; + this.setValues('rgb', [val, val, val]); + return this; + }, + + clearer: function (ratio) { + var alpha = this.values.alpha; + this.setValues('alpha', alpha - (alpha * ratio)); + return this; + }, + + opaquer: function (ratio) { + var alpha = this.values.alpha; + this.setValues('alpha', alpha + (alpha * ratio)); + return this; + }, + + rotate: function (degrees) { + var hsl = this.values.hsl; + var hue = (hsl[0] + degrees) % 360; + hsl[0] = hue < 0 ? 360 + hue : hue; + this.setValues('hsl', hsl); + return this; + }, + + /** + * Ported from sass implementation in C + * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 + */ + mix: function (mixinColor, weight) { + var color1 = this; + var color2 = mixinColor; + var p = weight === undefined ? 0.5 : weight; + + var w = 2 * p - 1; + var a = color1.alpha() - color2.alpha(); + + var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; + var w2 = 1 - w1; + + return this + .rgb( + w1 * color1.red() + w2 * color2.red(), + w1 * color1.green() + w2 * color2.green(), + w1 * color1.blue() + w2 * color2.blue() + ) + .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); + }, + + toJSON: function () { + return this.rgb(); + }, + + clone: function () { + // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, + // making the final build way to big to embed in Chart.js. So let's do it manually, + // assuming that values to clone are 1 dimension arrays containing only numbers, + // except 'alpha' which is a number. + var result = new Color(); + var source = this.values; + var target = result.values; + var value, type; + + for (var prop in source) { + if (source.hasOwnProperty(prop)) { + value = source[prop]; + type = ({}).toString.call(value); + if (type === '[object Array]') { + target[prop] = value.slice(0); + } else if (type === '[object Number]') { + target[prop] = value; + } else { + console.error('unexpected color value:', value); + } + } + } + + return result; + } +}; + +Color.prototype.spaces = { + rgb: ['red', 'green', 'blue'], + hsl: ['hue', 'saturation', 'lightness'], + hsv: ['hue', 'saturation', 'value'], + hwb: ['hue', 'whiteness', 'blackness'], + cmyk: ['cyan', 'magenta', 'yellow', 'black'] +}; + +Color.prototype.maxes = { + rgb: [255, 255, 255], + hsl: [360, 100, 100], + hsv: [360, 100, 100], + hwb: [360, 100, 100], + cmyk: [100, 100, 100, 100] +}; + +Color.prototype.getValues = function (space) { + var values = this.values; + var vals = {}; + + for (var i = 0; i < space.length; i++) { + vals[space.charAt(i)] = values[space][i]; + } + + if (values.alpha !== 1) { + vals.a = values.alpha; + } + + // {r: 255, g: 255, b: 255, a: 0.4} + return vals; +}; + +Color.prototype.setValues = function (space, vals) { + var values = this.values; + var spaces = this.spaces; + var maxes = this.maxes; + var alpha = 1; + var i; + + this.valid = true; + + if (space === 'alpha') { + alpha = vals; + } else if (vals.length) { + // [10, 10, 10] + values[space] = vals.slice(0, space.length); + alpha = vals[space.length]; + } else if (vals[space.charAt(0)] !== undefined) { + // {r: 10, g: 10, b: 10} + for (i = 0; i < space.length; i++) { + values[space][i] = vals[space.charAt(i)]; + } + + alpha = vals.a; + } else if (vals[spaces[space][0]] !== undefined) { + // {red: 10, green: 10, blue: 10} + var chans = spaces[space]; + + for (i = 0; i < space.length; i++) { + values[space][i] = vals[chans[i]]; + } + + alpha = vals.alpha; + } + + values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); + + if (space === 'alpha') { + return false; + } + + var capped; + + // cap values of the space prior converting all values + for (i = 0; i < space.length; i++) { + capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); + values[space][i] = Math.round(capped); + } + + // convert to all the other color spaces + for (var sname in spaces) { + if (sname !== space) { + values[sname] = colorConvert[space][sname](values[space]); + } + } + + return true; +}; + +Color.prototype.setSpace = function (space, args) { + var vals = args[0]; + + if (vals === undefined) { + // color.rgb() + return this.getValues(space); + } + + // color.rgb(10, 10, 10) + if (typeof vals === 'number') { + vals = Array.prototype.slice.call(args); + } + + this.setValues(space, vals); + return this; +}; + +Color.prototype.setChannel = function (space, index, val) { + var svalues = this.values[space]; + if (val === undefined) { + // color.red() + return svalues[index]; + } else if (val === svalues[index]) { + // color.red(color.red()) + return this; + } + + // color.red(100) + svalues[index] = val; + this.setValues(space, svalues); + + return this; +}; + +if (typeof window !== 'undefined') { + window.Color = Color; +} + +var chartjsColor = Color; + +/** + * @namespace Chart.helpers + */ +var helpers = { + /** + * An empty function that can be used, for example, for optional callback. + */ + noop: function() {}, + + /** + * Returns a unique id, sequentially generated from a global variable. + * @returns {number} + * @function + */ + uid: (function() { + var id = 0; + return function() { + return id++; + }; + }()), + + /** + * Returns true if `value` is neither null nor undefined, else returns false. + * @param {*} value - The value to test. + * @returns {boolean} + * @since 2.7.0 + */ + isNullOrUndef: function(value) { + return value === null || typeof value === 'undefined'; + }, + + /** + * Returns true if `value` is an array (including typed arrays), else returns false. + * @param {*} value - The value to test. + * @returns {boolean} + * @function + */ + isArray: function(value) { + if (Array.isArray && Array.isArray(value)) { + return true; + } + var type = Object.prototype.toString.call(value); + if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') { + return true; + } + return false; + }, + + /** + * Returns true if `value` is an object (excluding null), else returns false. + * @param {*} value - The value to test. + * @returns {boolean} + * @since 2.7.0 + */ + isObject: function(value) { + return value !== null && Object.prototype.toString.call(value) === '[object Object]'; + }, + + /** + * Returns true if `value` is a finite number, else returns false + * @param {*} value - The value to test. + * @returns {boolean} + */ + isFinite: function(value) { + return (typeof value === 'number' || value instanceof Number) && isFinite(value); + }, + + /** + * Returns `value` if defined, else returns `defaultValue`. + * @param {*} value - The value to return if defined. + * @param {*} defaultValue - The value to return if `value` is undefined. + * @returns {*} + */ + valueOrDefault: function(value, defaultValue) { + return typeof value === 'undefined' ? defaultValue : value; + }, + + /** + * Returns value at the given `index` in array if defined, else returns `defaultValue`. + * @param {Array} value - The array to lookup for value at `index`. + * @param {number} index - The index in `value` to lookup for value. + * @param {*} defaultValue - The value to return if `value[index]` is undefined. + * @returns {*} + */ + valueAtIndexOrDefault: function(value, index, defaultValue) { + return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue); + }, + + /** + * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the + * value returned by `fn`. If `fn` is not a function, this method returns undefined. + * @param {function} fn - The function to call. + * @param {Array|undefined|null} args - The arguments with which `fn` should be called. + * @param {object} [thisArg] - The value of `this` provided for the call to `fn`. + * @returns {*} + */ + callback: function(fn, args, thisArg) { + if (fn && typeof fn.call === 'function') { + return fn.apply(thisArg, args); + } + }, + + /** + * Note(SB) for performance sake, this method should only be used when loopable type + * is unknown or in none intensive code (not called often and small loopable). Else + * it's preferable to use a regular for() loop and save extra function calls. + * @param {object|Array} loopable - The object or array to be iterated. + * @param {function} fn - The function to call for each item. + * @param {object} [thisArg] - The value of `this` provided for the call to `fn`. + * @param {boolean} [reverse] - If true, iterates backward on the loopable. + */ + each: function(loopable, fn, thisArg, reverse) { + var i, len, keys; + if (helpers.isArray(loopable)) { + len = loopable.length; + if (reverse) { + for (i = len - 1; i >= 0; i--) { + fn.call(thisArg, loopable[i], i); + } + } else { + for (i = 0; i < len; i++) { + fn.call(thisArg, loopable[i], i); + } + } + } else if (helpers.isObject(loopable)) { + keys = Object.keys(loopable); + len = keys.length; + for (i = 0; i < len; i++) { + fn.call(thisArg, loopable[keys[i]], keys[i]); + } + } + }, + + /** + * Returns true if the `a0` and `a1` arrays have the same content, else returns false. + * @see https://stackoverflow.com/a/14853974 + * @param {Array} a0 - The array to compare + * @param {Array} a1 - The array to compare + * @returns {boolean} + */ + arrayEquals: function(a0, a1) { + var i, ilen, v0, v1; + + if (!a0 || !a1 || a0.length !== a1.length) { + return false; + } + + for (i = 0, ilen = a0.length; i < ilen; ++i) { + v0 = a0[i]; + v1 = a1[i]; + + if (v0 instanceof Array && v1 instanceof Array) { + if (!helpers.arrayEquals(v0, v1)) { + return false; + } + } else if (v0 !== v1) { + // NOTE: two different object instances will never be equal: {x:20} != {x:20} + return false; + } + } + + return true; + }, + + /** + * Returns a deep copy of `source` without keeping references on objects and arrays. + * @param {*} source - The value to clone. + * @returns {*} + */ + clone: function(source) { + if (helpers.isArray(source)) { + return source.map(helpers.clone); + } + + if (helpers.isObject(source)) { + var target = {}; + var keys = Object.keys(source); + var klen = keys.length; + var k = 0; + + for (; k < klen; ++k) { + target[keys[k]] = helpers.clone(source[keys[k]]); + } + + return target; + } + + return source; + }, + + /** + * The default merger when Chart.helpers.merge is called without merger option. + * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback. + * @private + */ + _merger: function(key, target, source, options) { + var tval = target[key]; + var sval = source[key]; + + if (helpers.isObject(tval) && helpers.isObject(sval)) { + helpers.merge(tval, sval, options); + } else { + target[key] = helpers.clone(sval); + } + }, + + /** + * Merges source[key] in target[key] only if target[key] is undefined. + * @private + */ + _mergerIf: function(key, target, source) { + var tval = target[key]; + var sval = source[key]; + + if (helpers.isObject(tval) && helpers.isObject(sval)) { + helpers.mergeIf(tval, sval); + } else if (!target.hasOwnProperty(key)) { + target[key] = helpers.clone(sval); + } + }, + + /** + * Recursively deep copies `source` properties into `target` with the given `options`. + * IMPORTANT: `target` is not cloned and will be updated with `source` properties. + * @param {object} target - The target object in which all sources are merged into. + * @param {object|object[]} source - Object(s) to merge into `target`. + * @param {object} [options] - Merging options: + * @param {function} [options.merger] - The merge method (key, target, source, options) + * @returns {object} The `target` object. + */ + merge: function(target, source, options) { + var sources = helpers.isArray(source) ? source : [source]; + var ilen = sources.length; + var merge, i, keys, klen, k; + + if (!helpers.isObject(target)) { + return target; + } + + options = options || {}; + merge = options.merger || helpers._merger; + + for (i = 0; i < ilen; ++i) { + source = sources[i]; + if (!helpers.isObject(source)) { + continue; + } + + keys = Object.keys(source); + for (k = 0, klen = keys.length; k < klen; ++k) { + merge(keys[k], target, source, options); + } + } + + return target; + }, + + /** + * Recursively deep copies `source` properties into `target` *only* if not defined in target. + * IMPORTANT: `target` is not cloned and will be updated with `source` properties. + * @param {object} target - The target object in which all sources are merged into. + * @param {object|object[]} source - Object(s) to merge into `target`. + * @returns {object} The `target` object. + */ + mergeIf: function(target, source) { + return helpers.merge(target, source, {merger: helpers._mergerIf}); + }, + + /** + * Applies the contents of two or more objects together into the first object. + * @param {object} target - The target object in which all objects are merged into. + * @param {object} arg1 - Object containing additional properties to merge in target. + * @param {object} argN - Additional objects containing properties to merge in target. + * @returns {object} The `target` object. + */ + extend: function(target) { + var setFn = function(value, key) { + target[key] = value; + }; + for (var i = 1, ilen = arguments.length; i < ilen; ++i) { + helpers.each(arguments[i], setFn); + } + return target; + }, + + /** + * Basic javascript inheritance based on the model created in Backbone.js + */ + inherits: function(extensions) { + var me = this; + var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() { + return me.apply(this, arguments); + }; + + var Surrogate = function() { + this.constructor = ChartElement; + }; + + Surrogate.prototype = me.prototype; + ChartElement.prototype = new Surrogate(); + ChartElement.extend = helpers.inherits; + + if (extensions) { + helpers.extend(ChartElement.prototype, extensions); + } + + ChartElement.__super__ = me.prototype; + return ChartElement; + } +}; + +var helpers_core = helpers; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.callback instead. + * @function Chart.helpers.callCallback + * @deprecated since version 2.6.0 + * @todo remove at version 3 + * @private + */ +helpers.callCallback = helpers.callback; + +/** + * Provided for backward compatibility, use Array.prototype.indexOf instead. + * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+ + * @function Chart.helpers.indexOf + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.indexOf = function(array, item, fromIndex) { + return Array.prototype.indexOf.call(array, item, fromIndex); +}; + +/** + * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead. + * @function Chart.helpers.getValueOrDefault + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.getValueOrDefault = helpers.valueOrDefault; + +/** + * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead. + * @function Chart.helpers.getValueAtIndexOrDefault + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault; + +/** + * Easing functions adapted from Robert Penner's easing equations. + * @namespace Chart.helpers.easingEffects + * @see http://www.robertpenner.com/easing/ + */ +var effects = { + linear: function(t) { + return t; + }, + + easeInQuad: function(t) { + return t * t; + }, + + easeOutQuad: function(t) { + return -t * (t - 2); + }, + + easeInOutQuad: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t; + } + return -0.5 * ((--t) * (t - 2) - 1); + }, + + easeInCubic: function(t) { + return t * t * t; + }, + + easeOutCubic: function(t) { + return (t = t - 1) * t * t + 1; + }, + + easeInOutCubic: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t * t; + } + return 0.5 * ((t -= 2) * t * t + 2); + }, + + easeInQuart: function(t) { + return t * t * t * t; + }, + + easeOutQuart: function(t) { + return -((t = t - 1) * t * t * t - 1); + }, + + easeInOutQuart: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t * t * t; + } + return -0.5 * ((t -= 2) * t * t * t - 2); + }, + + easeInQuint: function(t) { + return t * t * t * t * t; + }, + + easeOutQuint: function(t) { + return (t = t - 1) * t * t * t * t + 1; + }, + + easeInOutQuint: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t * t * t * t; + } + return 0.5 * ((t -= 2) * t * t * t * t + 2); + }, + + easeInSine: function(t) { + return -Math.cos(t * (Math.PI / 2)) + 1; + }, + + easeOutSine: function(t) { + return Math.sin(t * (Math.PI / 2)); + }, + + easeInOutSine: function(t) { + return -0.5 * (Math.cos(Math.PI * t) - 1); + }, + + easeInExpo: function(t) { + return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)); + }, + + easeOutExpo: function(t) { + return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1; + }, + + easeInOutExpo: function(t) { + if (t === 0) { + return 0; + } + if (t === 1) { + return 1; + } + if ((t /= 0.5) < 1) { + return 0.5 * Math.pow(2, 10 * (t - 1)); + } + return 0.5 * (-Math.pow(2, -10 * --t) + 2); + }, + + easeInCirc: function(t) { + if (t >= 1) { + return t; + } + return -(Math.sqrt(1 - t * t) - 1); + }, + + easeOutCirc: function(t) { + return Math.sqrt(1 - (t = t - 1) * t); + }, + + easeInOutCirc: function(t) { + if ((t /= 0.5) < 1) { + return -0.5 * (Math.sqrt(1 - t * t) - 1); + } + return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); + }, + + easeInElastic: function(t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) { + return 0; + } + if (t === 1) { + return 1; + } + if (!p) { + p = 0.3; + } + if (a < 1) { + a = 1; + s = p / 4; + } else { + s = p / (2 * Math.PI) * Math.asin(1 / a); + } + return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); + }, + + easeOutElastic: function(t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) { + return 0; + } + if (t === 1) { + return 1; + } + if (!p) { + p = 0.3; + } + if (a < 1) { + a = 1; + s = p / 4; + } else { + s = p / (2 * Math.PI) * Math.asin(1 / a); + } + return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1; + }, + + easeInOutElastic: function(t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) { + return 0; + } + if ((t /= 0.5) === 2) { + return 1; + } + if (!p) { + p = 0.45; + } + if (a < 1) { + a = 1; + s = p / 4; + } else { + s = p / (2 * Math.PI) * Math.asin(1 / a); + } + if (t < 1) { + return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); + } + return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1; + }, + easeInBack: function(t) { + var s = 1.70158; + return t * t * ((s + 1) * t - s); + }, + + easeOutBack: function(t) { + var s = 1.70158; + return (t = t - 1) * t * ((s + 1) * t + s) + 1; + }, + + easeInOutBack: function(t) { + var s = 1.70158; + if ((t /= 0.5) < 1) { + return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); + } + return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); + }, + + easeInBounce: function(t) { + return 1 - effects.easeOutBounce(1 - t); + }, + + easeOutBounce: function(t) { + if (t < (1 / 2.75)) { + return 7.5625 * t * t; + } + if (t < (2 / 2.75)) { + return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75; + } + if (t < (2.5 / 2.75)) { + return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375; + } + return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375; + }, + + easeInOutBounce: function(t) { + if (t < 0.5) { + return effects.easeInBounce(t * 2) * 0.5; + } + return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5; + } +}; + +var helpers_easing = { + effects: effects +}; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.easing.effects instead. + * @function Chart.helpers.easingEffects + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers_core.easingEffects = effects; + +var PI = Math.PI; +var RAD_PER_DEG = PI / 180; +var DOUBLE_PI = PI * 2; +var HALF_PI = PI / 2; +var QUARTER_PI = PI / 4; +var TWO_THIRDS_PI = PI * 2 / 3; + +/** + * @namespace Chart.helpers.canvas + */ +var exports$1 = { + /** + * Clears the entire canvas associated to the given `chart`. + * @param {Chart} chart - The chart for which to clear the canvas. + */ + clear: function(chart) { + chart.ctx.clearRect(0, 0, chart.width, chart.height); + }, + + /** + * Creates a "path" for a rectangle with rounded corners at position (x, y) with a + * given size (width, height) and the same `radius` for all corners. + * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context. + * @param {number} x - The x axis of the coordinate for the rectangle starting point. + * @param {number} y - The y axis of the coordinate for the rectangle starting point. + * @param {number} width - The rectangle's width. + * @param {number} height - The rectangle's height. + * @param {number} radius - The rounded amount (in pixels) for the four corners. + * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object? + */ + roundedRect: function(ctx, x, y, width, height, radius) { + if (radius) { + var r = Math.min(radius, height / 2, width / 2); + var left = x + r; + var top = y + r; + var right = x + width - r; + var bottom = y + height - r; + + ctx.moveTo(x, top); + if (left < right && top < bottom) { + ctx.arc(left, top, r, -PI, -HALF_PI); + ctx.arc(right, top, r, -HALF_PI, 0); + ctx.arc(right, bottom, r, 0, HALF_PI); + ctx.arc(left, bottom, r, HALF_PI, PI); + } else if (left < right) { + ctx.moveTo(left, y); + ctx.arc(right, top, r, -HALF_PI, HALF_PI); + ctx.arc(left, top, r, HALF_PI, PI + HALF_PI); + } else if (top < bottom) { + ctx.arc(left, top, r, -PI, 0); + ctx.arc(left, bottom, r, 0, PI); + } else { + ctx.arc(left, top, r, -PI, PI); + } + ctx.closePath(); + ctx.moveTo(x, y); + } else { + ctx.rect(x, y, width, height); + } + }, + + drawPoint: function(ctx, style, radius, x, y, rotation) { + var type, xOffset, yOffset, size, cornerRadius; + var rad = (rotation || 0) * RAD_PER_DEG; + + if (style && typeof style === 'object') { + type = style.toString(); + if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { + ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height); + return; + } + } + + if (isNaN(radius) || radius <= 0) { + return; + } + + ctx.beginPath(); + + switch (style) { + // Default includes circle + default: + ctx.arc(x, y, radius, 0, DOUBLE_PI); + ctx.closePath(); + break; + case 'triangle': + ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + rad += TWO_THIRDS_PI; + ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius); + ctx.closePath(); + break; + case 'rectRounded': + // NOTE: the rounded rect implementation changed to use `arc` instead of + // `quadraticCurveTo` since it generates better results when rect is + // almost a circle. 0.516 (instead of 0.5) produces results with visually + // closer proportion to the previous impl and it is inscribed in the + // circle with `radius`. For more details, see the following PRs: + // https://github.com/chartjs/Chart.js/issues/5597 + // https://github.com/chartjs/Chart.js/issues/5858 + cornerRadius = radius * 0.516; + size = radius - cornerRadius; + xOffset = Math.cos(rad + QUARTER_PI) * size; + yOffset = Math.sin(rad + QUARTER_PI) * size; + ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI); + ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad); + ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI); + ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI); + ctx.closePath(); + break; + case 'rect': + if (!rotation) { + size = Math.SQRT1_2 * radius; + ctx.rect(x - size, y - size, 2 * size, 2 * size); + break; + } + rad += QUARTER_PI; + /* falls through */ + case 'rectRot': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + yOffset, y - xOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.lineTo(x - yOffset, y + xOffset); + ctx.closePath(); + break; + case 'crossRot': + rad += QUARTER_PI; + /* falls through */ + case 'cross': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + break; + case 'star': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + rad += QUARTER_PI; + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x + yOffset, y - xOffset); + ctx.lineTo(x - yOffset, y + xOffset); + break; + case 'line': + xOffset = Math.cos(rad) * radius; + yOffset = Math.sin(rad) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + break; + case 'dash': + ctx.moveTo(x, y); + ctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius); + break; + } + + ctx.fill(); + ctx.stroke(); + }, + + /** + * Returns true if the point is inside the rectangle + * @param {object} point - The point to test + * @param {object} area - The rectangle + * @returns {boolean} + * @private + */ + _isPointInArea: function(point, area) { + var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error. + + return point.x > area.left - epsilon && point.x < area.right + epsilon && + point.y > area.top - epsilon && point.y < area.bottom + epsilon; + }, + + clipArea: function(ctx, area) { + ctx.save(); + ctx.beginPath(); + ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top); + ctx.clip(); + }, + + unclipArea: function(ctx) { + ctx.restore(); + }, + + lineTo: function(ctx, previous, target, flip) { + var stepped = target.steppedLine; + if (stepped) { + if (stepped === 'middle') { + var midpoint = (previous.x + target.x) / 2.0; + ctx.lineTo(midpoint, flip ? target.y : previous.y); + ctx.lineTo(midpoint, flip ? previous.y : target.y); + } else if ((stepped === 'after' && !flip) || (stepped !== 'after' && flip)) { + ctx.lineTo(previous.x, target.y); + } else { + ctx.lineTo(target.x, previous.y); + } + ctx.lineTo(target.x, target.y); + return; + } + + if (!target.tension) { + ctx.lineTo(target.x, target.y); + return; + } + + ctx.bezierCurveTo( + flip ? previous.controlPointPreviousX : previous.controlPointNextX, + flip ? previous.controlPointPreviousY : previous.controlPointNextY, + flip ? target.controlPointNextX : target.controlPointPreviousX, + flip ? target.controlPointNextY : target.controlPointPreviousY, + target.x, + target.y); + } +}; + +var helpers_canvas = exports$1; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.canvas.clear instead. + * @namespace Chart.helpers.clear + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers_core.clear = exports$1.clear; + +/** + * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead. + * @namespace Chart.helpers.drawRoundedRectangle + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers_core.drawRoundedRectangle = function(ctx) { + ctx.beginPath(); + exports$1.roundedRect.apply(exports$1, arguments); +}; + +var defaults = { + /** + * @private + */ + _set: function(scope, values) { + return helpers_core.merge(this[scope] || (this[scope] = {}), values); + } +}; + +defaults._set('global', { + defaultColor: 'rgba(0,0,0,0.1)', + defaultFontColor: '#666', + defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", + defaultFontSize: 12, + defaultFontStyle: 'normal', + defaultLineHeight: 1.2, + showLines: true +}); + +var core_defaults = defaults; + +var valueOrDefault = helpers_core.valueOrDefault; + +/** + * Converts the given font object into a CSS font string. + * @param {object} font - A font object. + * @return {string} The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font + * @private + */ +function toFontString(font) { + if (!font || helpers_core.isNullOrUndef(font.size) || helpers_core.isNullOrUndef(font.family)) { + return null; + } + + return (font.style ? font.style + ' ' : '') + + (font.weight ? font.weight + ' ' : '') + + font.size + 'px ' + + font.family; +} + +/** + * @alias Chart.helpers.options + * @namespace + */ +var helpers_options = { + /** + * Converts the given line height `value` in pixels for a specific font `size`. + * @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). + * @param {number} size - The font size (in pixels) used to resolve relative `value`. + * @returns {number} The effective line height in pixels (size * 1.2 if value is invalid). + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height + * @since 2.7.0 + */ + toLineHeight: function(value, size) { + var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); + if (!matches || matches[1] === 'normal') { + return size * 1.2; + } + + value = +matches[2]; + + switch (matches[3]) { + case 'px': + return value; + case '%': + value /= 100; + break; + default: + break; + } + + return size * value; + }, + + /** + * Converts the given value into a padding object with pre-computed width/height. + * @param {number|object} value - If a number, set the value to all TRBL component, + * else, if and object, use defined properties and sets undefined ones to 0. + * @returns {object} The padding values (top, right, bottom, left, width, height) + * @since 2.7.0 + */ + toPadding: function(value) { + var t, r, b, l; + + if (helpers_core.isObject(value)) { + t = +value.top || 0; + r = +value.right || 0; + b = +value.bottom || 0; + l = +value.left || 0; + } else { + t = r = b = l = +value || 0; + } + + return { + top: t, + right: r, + bottom: b, + left: l, + height: t + b, + width: l + r + }; + }, + + /** + * Parses font options and returns the font object. + * @param {object} options - A object that contains font options to be parsed. + * @return {object} The font object. + * @todo Support font.* options and renamed to toFont(). + * @private + */ + _parseFont: function(options) { + var globalDefaults = core_defaults.global; + var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize); + var font = { + family: valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily), + lineHeight: helpers_core.options.toLineHeight(valueOrDefault(options.lineHeight, globalDefaults.defaultLineHeight), size), + size: size, + style: valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle), + weight: null, + string: '' + }; + + font.string = toFontString(font); + return font; + }, + + /** + * Evaluates the given `inputs` sequentially and returns the first defined value. + * @param {Array} inputs - An array of values, falling back to the last value. + * @param {object} [context] - If defined and the current value is a function, the value + * is called with `context` as first argument and the result becomes the new input. + * @param {number} [index] - If defined and the current value is an array, the value + * at `index` become the new input. + * @since 2.7.0 + */ + resolve: function(inputs, context, index) { + var i, ilen, value; + + for (i = 0, ilen = inputs.length; i < ilen; ++i) { + value = inputs[i]; + if (value === undefined) { + continue; + } + if (context !== undefined && typeof value === 'function') { + value = value(context); + } + if (index !== undefined && helpers_core.isArray(value)) { + value = value[index]; + } + if (value !== undefined) { + return value; + } + } + } +}; + +var helpers$1 = helpers_core; +var easing = helpers_easing; +var canvas = helpers_canvas; +var options = helpers_options; +helpers$1.easing = easing; +helpers$1.canvas = canvas; +helpers$1.options = options; + +function interpolate(start, view, model, ease) { + var keys = Object.keys(model); + var i, ilen, key, actual, origin, target, type, c0, c1; + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + + target = model[key]; + + // if a value is added to the model after pivot() has been called, the view + // doesn't contain it, so let's initialize the view to the target value. + if (!view.hasOwnProperty(key)) { + view[key] = target; + } + + actual = view[key]; + + if (actual === target || key[0] === '_') { + continue; + } + + if (!start.hasOwnProperty(key)) { + start[key] = actual; + } + + origin = start[key]; + + type = typeof target; + + if (type === typeof origin) { + if (type === 'string') { + c0 = chartjsColor(origin); + if (c0.valid) { + c1 = chartjsColor(target); + if (c1.valid) { + view[key] = c1.mix(c0, ease).rgbString(); + continue; + } + } + } else if (helpers$1.isFinite(origin) && helpers$1.isFinite(target)) { + view[key] = origin + (target - origin) * ease; + continue; + } + } + + view[key] = target; + } +} + +var Element = function(configuration) { + helpers$1.extend(this, configuration); + this.initialize.apply(this, arguments); +}; + +helpers$1.extend(Element.prototype, { + + initialize: function() { + this.hidden = false; + }, + + pivot: function() { + var me = this; + if (!me._view) { + me._view = helpers$1.clone(me._model); + } + me._start = {}; + return me; + }, + + transition: function(ease) { + var me = this; + var model = me._model; + var start = me._start; + var view = me._view; + + // No animation -> No Transition + if (!model || ease === 1) { + me._view = model; + me._start = null; + return me; + } + + if (!view) { + view = me._view = {}; + } + + if (!start) { + start = me._start = {}; + } + + interpolate(start, view, model, ease); + + return me; + }, + + tooltipPosition: function() { + return { + x: this._model.x, + y: this._model.y + }; + }, + + hasValue: function() { + return helpers$1.isNumber(this._model.x) && helpers$1.isNumber(this._model.y); + } +}); + +Element.extend = helpers$1.inherits; + +var core_element = Element; + +var exports$2 = core_element.extend({ + chart: null, // the animation associated chart instance + currentStep: 0, // the current animation step + numSteps: 60, // default number of steps + easing: '', // the easing to use for this animation + render: null, // render function used by the animation service + + onAnimationProgress: null, // user specified callback to fire on each step of the animation + onAnimationComplete: null, // user specified callback to fire when the animation finishes +}); + +var core_animation = exports$2; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.Animation instead + * @prop Chart.Animation#animationObject + * @deprecated since version 2.6.0 + * @todo remove at version 3 + */ +Object.defineProperty(exports$2.prototype, 'animationObject', { + get: function() { + return this; + } +}); + +/** + * Provided for backward compatibility, use Chart.Animation#chart instead + * @prop Chart.Animation#chartInstance + * @deprecated since version 2.6.0 + * @todo remove at version 3 + */ +Object.defineProperty(exports$2.prototype, 'chartInstance', { + get: function() { + return this.chart; + }, + set: function(value) { + this.chart = value; + } +}); + +core_defaults._set('global', { + animation: { + duration: 1000, + easing: 'easeOutQuart', + onProgress: helpers$1.noop, + onComplete: helpers$1.noop + } +}); + +var core_animations = { + animations: [], + request: null, + + /** + * @param {Chart} chart - The chart to animate. + * @param {Chart.Animation} animation - The animation that we will animate. + * @param {number} duration - The animation duration in ms. + * @param {boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions + */ + addAnimation: function(chart, animation, duration, lazy) { + var animations = this.animations; + var i, ilen; + + animation.chart = chart; + animation.startTime = Date.now(); + animation.duration = duration; + + if (!lazy) { + chart.animating = true; + } + + for (i = 0, ilen = animations.length; i < ilen; ++i) { + if (animations[i].chart === chart) { + animations[i] = animation; + return; + } + } + + animations.push(animation); + + // If there are no animations queued, manually kickstart a digest, for lack of a better word + if (animations.length === 1) { + this.requestAnimationFrame(); + } + }, + + cancelAnimation: function(chart) { + var index = helpers$1.findIndex(this.animations, function(animation) { + return animation.chart === chart; + }); + + if (index !== -1) { + this.animations.splice(index, 1); + chart.animating = false; + } + }, + + requestAnimationFrame: function() { + var me = this; + if (me.request === null) { + // Skip animation frame requests until the active one is executed. + // This can happen when processing mouse events, e.g. 'mousemove' + // and 'mouseout' events will trigger multiple renders. + me.request = helpers$1.requestAnimFrame.call(window, function() { + me.request = null; + me.startDigest(); + }); + } + }, + + /** + * @private + */ + startDigest: function() { + var me = this; + + me.advance(); + + // Do we have more stuff to animate? + if (me.animations.length > 0) { + me.requestAnimationFrame(); + } + }, + + /** + * @private + */ + advance: function() { + var animations = this.animations; + var animation, chart, numSteps, nextStep; + var i = 0; + + // 1 animation per chart, so we are looping charts here + while (i < animations.length) { + animation = animations[i]; + chart = animation.chart; + numSteps = animation.numSteps; + + // Make sure that currentStep starts at 1 + // https://github.com/chartjs/Chart.js/issues/6104 + nextStep = Math.floor((Date.now() - animation.startTime) / animation.duration * numSteps) + 1; + animation.currentStep = Math.min(nextStep, numSteps); + + helpers$1.callback(animation.render, [chart, animation], chart); + helpers$1.callback(animation.onAnimationProgress, [animation], chart); + + if (animation.currentStep >= numSteps) { + helpers$1.callback(animation.onAnimationComplete, [animation], chart); + chart.animating = false; + animations.splice(i, 1); + } else { + ++i; + } + } + } +}; + +var resolve = helpers$1.options.resolve; + +var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; + +/** + * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', + * 'unshift') and notify the listener AFTER the array has been altered. Listeners are + * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. + */ +function listenArrayEvents(array, listener) { + if (array._chartjs) { + array._chartjs.listeners.push(listener); + return; + } + + Object.defineProperty(array, '_chartjs', { + configurable: true, + enumerable: false, + value: { + listeners: [listener] + } + }); + + arrayEvents.forEach(function(key) { + var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); + var base = array[key]; + + Object.defineProperty(array, key, { + configurable: true, + enumerable: false, + value: function() { + var args = Array.prototype.slice.call(arguments); + var res = base.apply(this, args); + + helpers$1.each(array._chartjs.listeners, function(object) { + if (typeof object[method] === 'function') { + object[method].apply(object, args); + } + }); + + return res; + } + }); + }); +} + +/** + * Removes the given array event listener and cleanup extra attached properties (such as + * the _chartjs stub and overridden methods) if array doesn't have any more listeners. + */ +function unlistenArrayEvents(array, listener) { + var stub = array._chartjs; + if (!stub) { + return; + } + + var listeners = stub.listeners; + var index = listeners.indexOf(listener); + if (index !== -1) { + listeners.splice(index, 1); + } + + if (listeners.length > 0) { + return; + } + + arrayEvents.forEach(function(key) { + delete array[key]; + }); + + delete array._chartjs; +} + +// Base class for all dataset controllers (line, bar, etc) +var DatasetController = function(chart, datasetIndex) { + this.initialize(chart, datasetIndex); +}; + +helpers$1.extend(DatasetController.prototype, { + + /** + * Element type used to generate a meta dataset (e.g. Chart.element.Line). + * @type {Chart.core.element} + */ + datasetElementType: null, + + /** + * Element type used to generate a meta data (e.g. Chart.element.Point). + * @type {Chart.core.element} + */ + dataElementType: null, + + initialize: function(chart, datasetIndex) { + var me = this; + me.chart = chart; + me.index = datasetIndex; + me.linkScales(); + me.addElements(); + }, + + updateIndex: function(datasetIndex) { + this.index = datasetIndex; + }, + + linkScales: function() { + var me = this; + var meta = me.getMeta(); + var dataset = me.getDataset(); + + if (meta.xAxisID === null || !(meta.xAxisID in me.chart.scales)) { + meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; + } + if (meta.yAxisID === null || !(meta.yAxisID in me.chart.scales)) { + meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; + } + }, + + getDataset: function() { + return this.chart.data.datasets[this.index]; + }, + + getMeta: function() { + return this.chart.getDatasetMeta(this.index); + }, + + getScaleForId: function(scaleID) { + return this.chart.scales[scaleID]; + }, + + /** + * @private + */ + _getValueScaleId: function() { + return this.getMeta().yAxisID; + }, + + /** + * @private + */ + _getIndexScaleId: function() { + return this.getMeta().xAxisID; + }, + + /** + * @private + */ + _getValueScale: function() { + return this.getScaleForId(this._getValueScaleId()); + }, + + /** + * @private + */ + _getIndexScale: function() { + return this.getScaleForId(this._getIndexScaleId()); + }, + + reset: function() { + this.update(true); + }, + + /** + * @private + */ + destroy: function() { + if (this._data) { + unlistenArrayEvents(this._data, this); + } + }, + + createMetaDataset: function() { + var me = this; + var type = me.datasetElementType; + return type && new type({ + _chart: me.chart, + _datasetIndex: me.index + }); + }, + + createMetaData: function(index) { + var me = this; + var type = me.dataElementType; + return type && new type({ + _chart: me.chart, + _datasetIndex: me.index, + _index: index + }); + }, + + addElements: function() { + var me = this; + var meta = me.getMeta(); + var data = me.getDataset().data || []; + var metaData = meta.data; + var i, ilen; + + for (i = 0, ilen = data.length; i < ilen; ++i) { + metaData[i] = metaData[i] || me.createMetaData(i); + } + + meta.dataset = meta.dataset || me.createMetaDataset(); + }, + + addElementAndReset: function(index) { + var element = this.createMetaData(index); + this.getMeta().data.splice(index, 0, element); + this.updateElement(element, index, true); + }, + + buildOrUpdateElements: function() { + var me = this; + var dataset = me.getDataset(); + var data = dataset.data || (dataset.data = []); + + // In order to correctly handle data addition/deletion animation (an thus simulate + // real-time charts), we need to monitor these data modifications and synchronize + // the internal meta data accordingly. + if (me._data !== data) { + if (me._data) { + // This case happens when the user replaced the data array instance. + unlistenArrayEvents(me._data, me); + } + + if (data && Object.isExtensible(data)) { + listenArrayEvents(data, me); + } + me._data = data; + } + + // Re-sync meta data in case the user replaced the data array or if we missed + // any updates and so make sure that we handle number of datapoints changing. + me.resyncElements(); + }, + + update: helpers$1.noop, + + transition: function(easingValue) { + var meta = this.getMeta(); + var elements = meta.data || []; + var ilen = elements.length; + var i = 0; + + for (; i < ilen; ++i) { + elements[i].transition(easingValue); + } + + if (meta.dataset) { + meta.dataset.transition(easingValue); + } + }, + + draw: function() { + var meta = this.getMeta(); + var elements = meta.data || []; + var ilen = elements.length; + var i = 0; + + if (meta.dataset) { + meta.dataset.draw(); + } + + for (; i < ilen; ++i) { + elements[i].draw(); + } + }, + + removeHoverStyle: function(element) { + helpers$1.merge(element._model, element.$previousStyle || {}); + delete element.$previousStyle; + }, + + setHoverStyle: function(element) { + var dataset = this.chart.data.datasets[element._datasetIndex]; + var index = element._index; + var custom = element.custom || {}; + var model = element._model; + var getHoverColor = helpers$1.getHoverColor; + + element.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth + }; + + model.backgroundColor = resolve([custom.hoverBackgroundColor, dataset.hoverBackgroundColor, getHoverColor(model.backgroundColor)], undefined, index); + model.borderColor = resolve([custom.hoverBorderColor, dataset.hoverBorderColor, getHoverColor(model.borderColor)], undefined, index); + model.borderWidth = resolve([custom.hoverBorderWidth, dataset.hoverBorderWidth, model.borderWidth], undefined, index); + }, + + /** + * @private + */ + resyncElements: function() { + var me = this; + var meta = me.getMeta(); + var data = me.getDataset().data; + var numMeta = meta.data.length; + var numData = data.length; + + if (numData < numMeta) { + meta.data.splice(numData, numMeta - numData); + } else if (numData > numMeta) { + me.insertElements(numMeta, numData - numMeta); + } + }, + + /** + * @private + */ + insertElements: function(start, count) { + for (var i = 0; i < count; ++i) { + this.addElementAndReset(start + i); + } + }, + + /** + * @private + */ + onDataPush: function() { + var count = arguments.length; + this.insertElements(this.getDataset().data.length - count, count); + }, + + /** + * @private + */ + onDataPop: function() { + this.getMeta().data.pop(); + }, + + /** + * @private + */ + onDataShift: function() { + this.getMeta().data.shift(); + }, + + /** + * @private + */ + onDataSplice: function(start, count) { + this.getMeta().data.splice(start, count); + this.insertElements(start, arguments.length - 2); + }, + + /** + * @private + */ + onDataUnshift: function() { + this.insertElements(0, arguments.length); + } +}); + +DatasetController.extend = helpers$1.inherits; + +var core_datasetController = DatasetController; + +core_defaults._set('global', { + elements: { + arc: { + backgroundColor: core_defaults.global.defaultColor, + borderColor: '#fff', + borderWidth: 2, + borderAlign: 'center' + } + } +}); + +var element_arc = core_element.extend({ + inLabelRange: function(mouseX) { + var vm = this._view; + + if (vm) { + return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); + } + return false; + }, + + inRange: function(chartX, chartY) { + var vm = this._view; + + if (vm) { + var pointRelativePosition = helpers$1.getAngleFromPoint(vm, {x: chartX, y: chartY}); + var angle = pointRelativePosition.angle; + var distance = pointRelativePosition.distance; + + // Sanitise angle range + var startAngle = vm.startAngle; + var endAngle = vm.endAngle; + while (endAngle < startAngle) { + endAngle += 2.0 * Math.PI; + } + while (angle > endAngle) { + angle -= 2.0 * Math.PI; + } + while (angle < startAngle) { + angle += 2.0 * Math.PI; + } + + // Check if within the range of the open/close angle + var betweenAngles = (angle >= startAngle && angle <= endAngle); + var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); + + return (betweenAngles && withinRadius); + } + return false; + }, + + getCenterPoint: function() { + var vm = this._view; + var halfAngle = (vm.startAngle + vm.endAngle) / 2; + var halfRadius = (vm.innerRadius + vm.outerRadius) / 2; + return { + x: vm.x + Math.cos(halfAngle) * halfRadius, + y: vm.y + Math.sin(halfAngle) * halfRadius + }; + }, + + getArea: function() { + var vm = this._view; + return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2)); + }, + + tooltipPosition: function() { + var vm = this._view; + var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2); + var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; + + return { + x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), + y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) + }; + }, + + draw: function() { + var ctx = this._chart.ctx; + var vm = this._view; + var sA = vm.startAngle; + var eA = vm.endAngle; + var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0; + var angleMargin; + + ctx.save(); + + ctx.beginPath(); + ctx.arc(vm.x, vm.y, Math.max(vm.outerRadius - pixelMargin, 0), sA, eA); + ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); + ctx.closePath(); + + ctx.fillStyle = vm.backgroundColor; + ctx.fill(); + + if (vm.borderWidth) { + if (vm.borderAlign === 'inner') { + // Draw an inner border by cliping the arc and drawing a double-width border + // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders + ctx.beginPath(); + angleMargin = pixelMargin / vm.outerRadius; + ctx.arc(vm.x, vm.y, vm.outerRadius, sA - angleMargin, eA + angleMargin); + if (vm.innerRadius > pixelMargin) { + angleMargin = pixelMargin / vm.innerRadius; + ctx.arc(vm.x, vm.y, vm.innerRadius - pixelMargin, eA + angleMargin, sA - angleMargin, true); + } else { + ctx.arc(vm.x, vm.y, pixelMargin, eA + Math.PI / 2, sA - Math.PI / 2); + } + ctx.closePath(); + ctx.clip(); + + ctx.beginPath(); + ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); + ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); + ctx.closePath(); + + ctx.lineWidth = vm.borderWidth * 2; + ctx.lineJoin = 'round'; + } else { + ctx.lineWidth = vm.borderWidth; + ctx.lineJoin = 'bevel'; + } + + ctx.strokeStyle = vm.borderColor; + ctx.stroke(); + } + + ctx.restore(); + } +}); + +var valueOrDefault$1 = helpers$1.valueOrDefault; + +var defaultColor = core_defaults.global.defaultColor; + +core_defaults._set('global', { + elements: { + line: { + tension: 0.4, + backgroundColor: defaultColor, + borderWidth: 3, + borderColor: defaultColor, + borderCapStyle: 'butt', + borderDash: [], + borderDashOffset: 0.0, + borderJoinStyle: 'miter', + capBezierPoints: true, + fill: true, // do we fill in the area between the line and its base axis + } + } +}); + +var element_line = core_element.extend({ + draw: function() { + var me = this; + var vm = me._view; + var ctx = me._chart.ctx; + var spanGaps = vm.spanGaps; + var points = me._children.slice(); // clone array + var globalDefaults = core_defaults.global; + var globalOptionLineElements = globalDefaults.elements.line; + var lastDrawnIndex = -1; + var index, current, previous, currentVM; + + // If we are looping, adding the first point again + if (me._loop && points.length) { + points.push(points[0]); + } + + ctx.save(); + + // Stroke Line Options + ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; + + // IE 9 and 10 do not support line dash + if (ctx.setLineDash) { + ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); + } + + ctx.lineDashOffset = valueOrDefault$1(vm.borderDashOffset, globalOptionLineElements.borderDashOffset); + ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; + ctx.lineWidth = valueOrDefault$1(vm.borderWidth, globalOptionLineElements.borderWidth); + ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; + + // Stroke Line + ctx.beginPath(); + lastDrawnIndex = -1; + + for (index = 0; index < points.length; ++index) { + current = points[index]; + previous = helpers$1.previousItem(points, index); + currentVM = current._view; + + // First point moves to it's starting position no matter what + if (index === 0) { + if (!currentVM.skip) { + ctx.moveTo(currentVM.x, currentVM.y); + lastDrawnIndex = index; + } + } else { + previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; + + if (!currentVM.skip) { + if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { + // There was a gap and this is the first point after the gap + ctx.moveTo(currentVM.x, currentVM.y); + } else { + // Line to next point + helpers$1.canvas.lineTo(ctx, previous._view, current._view); + } + lastDrawnIndex = index; + } + } + } + + ctx.stroke(); + ctx.restore(); + } +}); + +var valueOrDefault$2 = helpers$1.valueOrDefault; + +var defaultColor$1 = core_defaults.global.defaultColor; + +core_defaults._set('global', { + elements: { + point: { + radius: 3, + pointStyle: 'circle', + backgroundColor: defaultColor$1, + borderColor: defaultColor$1, + borderWidth: 1, + // Hover + hitRadius: 1, + hoverRadius: 4, + hoverBorderWidth: 1 + } + } +}); + +function xRange(mouseX) { + var vm = this._view; + return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false; +} + +function yRange(mouseY) { + var vm = this._view; + return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false; +} + +var element_point = core_element.extend({ + inRange: function(mouseX, mouseY) { + var vm = this._view; + return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; + }, + + inLabelRange: xRange, + inXRange: xRange, + inYRange: yRange, + + getCenterPoint: function() { + var vm = this._view; + return { + x: vm.x, + y: vm.y + }; + }, + + getArea: function() { + return Math.PI * Math.pow(this._view.radius, 2); + }, + + tooltipPosition: function() { + var vm = this._view; + return { + x: vm.x, + y: vm.y, + padding: vm.radius + vm.borderWidth + }; + }, + + draw: function(chartArea) { + var vm = this._view; + var ctx = this._chart.ctx; + var pointStyle = vm.pointStyle; + var rotation = vm.rotation; + var radius = vm.radius; + var x = vm.x; + var y = vm.y; + var globalDefaults = core_defaults.global; + var defaultColor = globalDefaults.defaultColor; // eslint-disable-line no-shadow + + if (vm.skip) { + return; + } + + // Clipping for Points. + if (chartArea === undefined || helpers$1.canvas._isPointInArea(vm, chartArea)) { + ctx.strokeStyle = vm.borderColor || defaultColor; + ctx.lineWidth = valueOrDefault$2(vm.borderWidth, globalDefaults.elements.point.borderWidth); + ctx.fillStyle = vm.backgroundColor || defaultColor; + helpers$1.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation); + } + } +}); + +var defaultColor$2 = core_defaults.global.defaultColor; + +core_defaults._set('global', { + elements: { + rectangle: { + backgroundColor: defaultColor$2, + borderColor: defaultColor$2, + borderSkipped: 'bottom', + borderWidth: 0 + } + } +}); + +function isVertical(vm) { + return vm && vm.width !== undefined; +} + +/** + * Helper function to get the bounds of the bar regardless of the orientation + * @param bar {Chart.Element.Rectangle} the bar + * @return {Bounds} bounds of the bar + * @private + */ +function getBarBounds(vm) { + var x1, x2, y1, y2, half; + + if (isVertical(vm)) { + half = vm.width / 2; + x1 = vm.x - half; + x2 = vm.x + half; + y1 = Math.min(vm.y, vm.base); + y2 = Math.max(vm.y, vm.base); + } else { + half = vm.height / 2; + x1 = Math.min(vm.x, vm.base); + x2 = Math.max(vm.x, vm.base); + y1 = vm.y - half; + y2 = vm.y + half; + } + + return { + left: x1, + top: y1, + right: x2, + bottom: y2 + }; +} + +function swap(orig, v1, v2) { + return orig === v1 ? v2 : orig === v2 ? v1 : orig; +} + +function parseBorderSkipped(vm) { + var edge = vm.borderSkipped; + var res = {}; + + if (!edge) { + return res; + } + + if (vm.horizontal) { + if (vm.base > vm.x) { + edge = swap(edge, 'left', 'right'); + } + } else if (vm.base < vm.y) { + edge = swap(edge, 'bottom', 'top'); + } + + res[edge] = true; + return res; +} + +function parseBorderWidth(vm, maxW, maxH) { + var value = vm.borderWidth; + var skip = parseBorderSkipped(vm); + var t, r, b, l; + + if (helpers$1.isObject(value)) { + t = +value.top || 0; + r = +value.right || 0; + b = +value.bottom || 0; + l = +value.left || 0; + } else { + t = r = b = l = +value || 0; + } + + return { + t: skip.top || (t < 0) ? 0 : t > maxH ? maxH : t, + r: skip.right || (r < 0) ? 0 : r > maxW ? maxW : r, + b: skip.bottom || (b < 0) ? 0 : b > maxH ? maxH : b, + l: skip.left || (l < 0) ? 0 : l > maxW ? maxW : l + }; +} + +function boundingRects(vm) { + var bounds = getBarBounds(vm); + var width = bounds.right - bounds.left; + var height = bounds.bottom - bounds.top; + var border = parseBorderWidth(vm, width / 2, height / 2); + + return { + outer: { + x: bounds.left, + y: bounds.top, + w: width, + h: height + }, + inner: { + x: bounds.left + border.l, + y: bounds.top + border.t, + w: width - border.l - border.r, + h: height - border.t - border.b + } + }; +} + +function inRange(vm, x, y) { + var skipX = x === null; + var skipY = y === null; + var bounds = !vm || (skipX && skipY) ? false : getBarBounds(vm); + + return bounds + && (skipX || x >= bounds.left && x <= bounds.right) + && (skipY || y >= bounds.top && y <= bounds.bottom); +} + +var element_rectangle = core_element.extend({ + draw: function() { + var ctx = this._chart.ctx; + var vm = this._view; + var rects = boundingRects(vm); + var outer = rects.outer; + var inner = rects.inner; + + ctx.fillStyle = vm.backgroundColor; + ctx.fillRect(outer.x, outer.y, outer.w, outer.h); + + if (outer.w === inner.w && outer.h === inner.h) { + return; + } + + ctx.save(); + ctx.beginPath(); + ctx.rect(outer.x, outer.y, outer.w, outer.h); + ctx.clip(); + ctx.fillStyle = vm.borderColor; + ctx.rect(inner.x, inner.y, inner.w, inner.h); + ctx.fill('evenodd'); + ctx.restore(); + }, + + height: function() { + var vm = this._view; + return vm.base - vm.y; + }, + + inRange: function(mouseX, mouseY) { + return inRange(this._view, mouseX, mouseY); + }, + + inLabelRange: function(mouseX, mouseY) { + var vm = this._view; + return isVertical(vm) + ? inRange(vm, mouseX, null) + : inRange(vm, null, mouseY); + }, + + inXRange: function(mouseX) { + return inRange(this._view, mouseX, null); + }, + + inYRange: function(mouseY) { + return inRange(this._view, null, mouseY); + }, + + getCenterPoint: function() { + var vm = this._view; + var x, y; + if (isVertical(vm)) { + x = vm.x; + y = (vm.y + vm.base) / 2; + } else { + x = (vm.x + vm.base) / 2; + y = vm.y; + } + + return {x: x, y: y}; + }, + + getArea: function() { + var vm = this._view; + + return isVertical(vm) + ? vm.width * Math.abs(vm.y - vm.base) + : vm.height * Math.abs(vm.x - vm.base); + }, + + tooltipPosition: function() { + var vm = this._view; + return { + x: vm.x, + y: vm.y + }; + } +}); + +var elements = {}; +var Arc = element_arc; +var Line = element_line; +var Point = element_point; +var Rectangle = element_rectangle; +elements.Arc = Arc; +elements.Line = Line; +elements.Point = Point; +elements.Rectangle = Rectangle; + +var resolve$1 = helpers$1.options.resolve; + +core_defaults._set('bar', { + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'category', + categoryPercentage: 0.8, + barPercentage: 0.9, + offset: true, + gridLines: { + offsetGridLines: true + } + }], + + yAxes: [{ + type: 'linear' + }] + } +}); + +/** + * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap. + * @private + */ +function computeMinSampleSize(scale, pixels) { + var min = scale.isHorizontal() ? scale.width : scale.height; + var ticks = scale.getTicks(); + var prev, curr, i, ilen; + + for (i = 1, ilen = pixels.length; i < ilen; ++i) { + min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1])); + } + + for (i = 0, ilen = ticks.length; i < ilen; ++i) { + curr = scale.getPixelForTick(i); + min = i > 0 ? Math.min(min, curr - prev) : min; + prev = curr; + } + + return min; +} + +/** + * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null, + * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This + * mode currently always generates bars equally sized (until we introduce scriptable options?). + * @private + */ +function computeFitCategoryTraits(index, ruler, options) { + var thickness = options.barThickness; + var count = ruler.stackCount; + var curr = ruler.pixels[index]; + var size, ratio; + + if (helpers$1.isNullOrUndef(thickness)) { + size = ruler.min * options.categoryPercentage; + ratio = options.barPercentage; + } else { + // When bar thickness is enforced, category and bar percentages are ignored. + // Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%') + // and deprecate barPercentage since this value is ignored when thickness is absolute. + size = thickness * count; + ratio = 1; + } + + return { + chunk: size / count, + ratio: ratio, + start: curr - (size / 2) + }; +} + +/** + * Computes an "optimal" category that globally arranges bars side by side (no gap when + * percentage options are 1), based on the previous and following categories. This mode + * generates bars with different widths when data are not evenly spaced. + * @private + */ +function computeFlexCategoryTraits(index, ruler, options) { + var pixels = ruler.pixels; + var curr = pixels[index]; + var prev = index > 0 ? pixels[index - 1] : null; + var next = index < pixels.length - 1 ? pixels[index + 1] : null; + var percent = options.categoryPercentage; + var start, size; + + if (prev === null) { + // first data: its size is double based on the next point or, + // if it's also the last data, we use the scale size. + prev = curr - (next === null ? ruler.end - ruler.start : next - curr); + } + + if (next === null) { + // last data: its size is also double based on the previous point. + next = curr + curr - prev; + } + + start = curr - (curr - Math.min(prev, next)) / 2 * percent; + size = Math.abs(next - prev) / 2 * percent; + + return { + chunk: size / ruler.stackCount, + ratio: options.barPercentage, + start: start + }; +} + +var controller_bar = core_datasetController.extend({ + + dataElementType: elements.Rectangle, + + initialize: function() { + var me = this; + var meta; + + core_datasetController.prototype.initialize.apply(me, arguments); + + meta = me.getMeta(); + meta.stack = me.getDataset().stack; + meta.bar = true; + }, + + update: function(reset) { + var me = this; + var rects = me.getMeta().data; + var i, ilen; + + me._ruler = me.getRuler(); + + for (i = 0, ilen = rects.length; i < ilen; ++i) { + me.updateElement(rects[i], i, reset); + } + }, + + updateElement: function(rectangle, index, reset) { + var me = this; + var meta = me.getMeta(); + var dataset = me.getDataset(); + var options = me._resolveElementOptions(rectangle, index); + + rectangle._xScale = me.getScaleForId(meta.xAxisID); + rectangle._yScale = me.getScaleForId(meta.yAxisID); + rectangle._datasetIndex = me.index; + rectangle._index = index; + rectangle._model = { + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderSkipped: options.borderSkipped, + borderWidth: options.borderWidth, + datasetLabel: dataset.label, + label: me.chart.data.labels[index] + }; + + me._updateElementGeometry(rectangle, index, reset); + + rectangle.pivot(); + }, + + /** + * @private + */ + _updateElementGeometry: function(rectangle, index, reset) { + var me = this; + var model = rectangle._model; + var vscale = me._getValueScale(); + var base = vscale.getBasePixel(); + var horizontal = vscale.isHorizontal(); + var ruler = me._ruler || me.getRuler(); + var vpixels = me.calculateBarValuePixels(me.index, index); + var ipixels = me.calculateBarIndexPixels(me.index, index, ruler); + + model.horizontal = horizontal; + model.base = reset ? base : vpixels.base; + model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; + model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; + model.height = horizontal ? ipixels.size : undefined; + model.width = horizontal ? undefined : ipixels.size; + }, + + /** + * Returns the stacks based on groups and bar visibility. + * @param {number} [last] - The dataset index + * @returns {string[]} The list of stack IDs + * @private + */ + _getStacks: function(last) { + var me = this; + var chart = me.chart; + var scale = me._getIndexScale(); + var stacked = scale.options.stacked; + var ilen = last === undefined ? chart.data.datasets.length : last + 1; + var stacks = []; + var i, meta; + + for (i = 0; i < ilen; ++i) { + meta = chart.getDatasetMeta(i); + if (meta.bar && chart.isDatasetVisible(i) && + (stacked === false || + (stacked === true && stacks.indexOf(meta.stack) === -1) || + (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { + stacks.push(meta.stack); + } + } + + return stacks; + }, + + /** + * Returns the effective number of stacks based on groups and bar visibility. + * @private + */ + getStackCount: function() { + return this._getStacks().length; + }, + + /** + * Returns the stack index for the given dataset based on groups and bar visibility. + * @param {number} [datasetIndex] - The dataset index + * @param {string} [name] - The stack name to find + * @returns {number} The stack index + * @private + */ + getStackIndex: function(datasetIndex, name) { + var stacks = this._getStacks(datasetIndex); + var index = (name !== undefined) + ? stacks.indexOf(name) + : -1; // indexOf returns -1 if element is not present + + return (index === -1) + ? stacks.length - 1 + : index; + }, + + /** + * @private + */ + getRuler: function() { + var me = this; + var scale = me._getIndexScale(); + var stackCount = me.getStackCount(); + var datasetIndex = me.index; + var isHorizontal = scale.isHorizontal(); + var start = isHorizontal ? scale.left : scale.top; + var end = start + (isHorizontal ? scale.width : scale.height); + var pixels = []; + var i, ilen, min; + + for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { + pixels.push(scale.getPixelForValue(null, i, datasetIndex)); + } + + min = helpers$1.isNullOrUndef(scale.options.barThickness) + ? computeMinSampleSize(scale, pixels) + : -1; + + return { + min: min, + pixels: pixels, + start: start, + end: end, + stackCount: stackCount, + scale: scale + }; + }, + + /** + * Note: pixel values are not clamped to the scale area. + * @private + */ + calculateBarValuePixels: function(datasetIndex, index) { + var me = this; + var chart = me.chart; + var meta = me.getMeta(); + var scale = me._getValueScale(); + var isHorizontal = scale.isHorizontal(); + var datasets = chart.data.datasets; + var value = +scale.getRightValue(datasets[datasetIndex].data[index]); + var minBarLength = scale.options.minBarLength; + var stacked = scale.options.stacked; + var stack = meta.stack; + var start = 0; + var i, imeta, ivalue, base, head, size; + + if (stacked || (stacked === undefined && stack !== undefined)) { + for (i = 0; i < datasetIndex; ++i) { + imeta = chart.getDatasetMeta(i); + + if (imeta.bar && + imeta.stack === stack && + imeta.controller._getValueScaleId() === scale.id && + chart.isDatasetVisible(i)) { + + ivalue = +scale.getRightValue(datasets[i].data[index]); + if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { + start += ivalue; + } + } + } + } + + base = scale.getPixelForValue(start); + head = scale.getPixelForValue(start + value); + size = head - base; + + if (minBarLength !== undefined && Math.abs(size) < minBarLength) { + size = minBarLength; + if (value >= 0 && !isHorizontal || value < 0 && isHorizontal) { + head = base - minBarLength; + } else { + head = base + minBarLength; + } + } + + return { + size: size, + base: base, + head: head, + center: head + size / 2 + }; + }, + + /** + * @private + */ + calculateBarIndexPixels: function(datasetIndex, index, ruler) { + var me = this; + var options = ruler.scale.options; + var range = options.barThickness === 'flex' + ? computeFlexCategoryTraits(index, ruler, options) + : computeFitCategoryTraits(index, ruler, options); + + var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack); + var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); + var size = Math.min( + helpers$1.valueOrDefault(options.maxBarThickness, Infinity), + range.chunk * range.ratio); + + return { + base: center - size / 2, + head: center + size / 2, + center: center, + size: size + }; + }, + + draw: function() { + var me = this; + var chart = me.chart; + var scale = me._getValueScale(); + var rects = me.getMeta().data; + var dataset = me.getDataset(); + var ilen = rects.length; + var i = 0; + + helpers$1.canvas.clipArea(chart.ctx, chart.chartArea); + + for (; i < ilen; ++i) { + if (!isNaN(scale.getRightValue(dataset.data[i]))) { + rects[i].draw(); + } + } + + helpers$1.canvas.unclipArea(chart.ctx); + }, + + /** + * @private + */ + _resolveElementOptions: function(rectangle, index) { + var me = this; + var chart = me.chart; + var datasets = chart.data.datasets; + var dataset = datasets[me.index]; + var custom = rectangle.custom || {}; + var options = chart.options.elements.rectangle; + var values = {}; + var i, ilen, key; + + // Scriptable options + var context = { + chart: chart, + dataIndex: index, + dataset: dataset, + datasetIndex: me.index + }; + + var keys = [ + 'backgroundColor', + 'borderColor', + 'borderSkipped', + 'borderWidth' + ]; + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + values[key] = resolve$1([ + custom[key], + dataset[key], + options[key] + ], context, index); + } + + return values; + } +}); + +var valueOrDefault$3 = helpers$1.valueOrDefault; +var resolve$2 = helpers$1.options.resolve; + +core_defaults._set('bubble', { + hover: { + mode: 'single' + }, + + scales: { + xAxes: [{ + type: 'linear', // bubble should probably use a linear scale by default + position: 'bottom', + id: 'x-axis-0' // need an ID so datasets can reference the scale + }], + yAxes: [{ + type: 'linear', + position: 'left', + id: 'y-axis-0' + }] + }, + + tooltips: { + callbacks: { + title: function() { + // Title doesn't make sense for scatter since we format the data as a point + return ''; + }, + label: function(item, data) { + var datasetLabel = data.datasets[item.datasetIndex].label || ''; + var dataPoint = data.datasets[item.datasetIndex].data[item.index]; + return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; + } + } + } +}); + +var controller_bubble = core_datasetController.extend({ + /** + * @protected + */ + dataElementType: elements.Point, + + /** + * @protected + */ + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var points = meta.data; + + // Update Points + helpers$1.each(points, function(point, index) { + me.updateElement(point, index, reset); + }); + }, + + /** + * @protected + */ + updateElement: function(point, index, reset) { + var me = this; + var meta = me.getMeta(); + var custom = point.custom || {}; + var xScale = me.getScaleForId(meta.xAxisID); + var yScale = me.getScaleForId(meta.yAxisID); + var options = me._resolveElementOptions(point, index); + var data = me.getDataset().data[index]; + var dsIndex = me.index; + + var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); + var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); + + point._xScale = xScale; + point._yScale = yScale; + point._options = options; + point._datasetIndex = dsIndex; + point._index = index; + point._model = { + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + hitRadius: options.hitRadius, + pointStyle: options.pointStyle, + rotation: options.rotation, + radius: reset ? 0 : options.radius, + skip: custom.skip || isNaN(x) || isNaN(y), + x: x, + y: y, + }; + + point.pivot(); + }, + + /** + * @protected + */ + setHoverStyle: function(point) { + var model = point._model; + var options = point._options; + var getHoverColor = helpers$1.getHoverColor; + + point.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + radius: model.radius + }; + + model.backgroundColor = valueOrDefault$3(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault$3(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault$3(options.hoverBorderWidth, options.borderWidth); + model.radius = options.radius + options.hoverRadius; + }, + + /** + * @private + */ + _resolveElementOptions: function(point, index) { + var me = this; + var chart = me.chart; + var datasets = chart.data.datasets; + var dataset = datasets[me.index]; + var custom = point.custom || {}; + var options = chart.options.elements.point; + var data = dataset.data[index]; + var values = {}; + var i, ilen, key; + + // Scriptable options + var context = { + chart: chart, + dataIndex: index, + dataset: dataset, + datasetIndex: me.index + }; + + var keys = [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + 'hoverRadius', + 'hitRadius', + 'pointStyle', + 'rotation' + ]; + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + values[key] = resolve$2([ + custom[key], + dataset[key], + options[key] + ], context, index); + } + + // Custom radius resolution + values.radius = resolve$2([ + custom.radius, + data ? data.r : undefined, + dataset.radius, + options.radius + ], context, index); + + return values; + } +}); + +var resolve$3 = helpers$1.options.resolve; +var valueOrDefault$4 = helpers$1.valueOrDefault; + +core_defaults._set('doughnut', { + animation: { + // Boolean - Whether we animate the rotation of the Doughnut + animateRotate: true, + // Boolean - Whether we animate scaling the Doughnut from the centre + animateScale: false + }, + hover: { + mode: 'single' + }, + legendCallback: function(chart) { + var text = []; + text.push('
    '); + + var data = chart.data; + var datasets = data.datasets; + var labels = data.labels; + + if (datasets.length) { + for (var i = 0; i < datasets[0].data.length; ++i) { + text.push('
  • '); + if (labels[i]) { + text.push(labels[i]); + } + text.push('
  • '); + } + } + + 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 arcOpts = chart.options.elements.arc; + var fill = resolve$3([custom.backgroundColor, ds.backgroundColor, arcOpts.backgroundColor], undefined, i); + var stroke = resolve$3([custom.borderColor, ds.borderColor, arcOpts.borderColor], undefined, i); + var bw = resolve$3([custom.borderWidth, ds.borderWidth, arcOpts.borderWidth], undefined, i); + + return { + text: label, + fillStyle: fill, + strokeStyle: stroke, + lineWidth: bw, + hidden: isNaN(ds.data[i]) || meta.data[i].hidden, + + // Extra data used for toggling the correct item + index: i + }; + }); + } + return []; + } + }, + + onClick: function(e, legendItem) { + var index = legendItem.index; + var chart = this.chart; + var i, ilen, meta; + + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + meta = chart.getDatasetMeta(i); + // toggle visibility of index if exists + if (meta.data[index]) { + meta.data[index].hidden = !meta.data[index].hidden; + } + } + + chart.update(); + } + }, + + // The percentage of the chart that we cut out of the middle. + cutoutPercentage: 50, + + // The rotation of the chart, where the first data arc begins. + rotation: Math.PI * -0.5, + + // The total circumference of the chart. + circumference: Math.PI * 2.0, + + // Need to override these to give a nice default + tooltips: { + callbacks: { + title: function() { + return ''; + }, + label: function(tooltipItem, data) { + var dataLabel = data.labels[tooltipItem.index]; + var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; + + if (helpers$1.isArray(dataLabel)) { + // show value on first line of multiline label + // need to clone because we are changing the value + dataLabel = dataLabel.slice(); + dataLabel[0] += value; + } else { + dataLabel += value; + } + + return dataLabel; + } + } + } +}); + +var controller_doughnut = core_datasetController.extend({ + + dataElementType: elements.Arc, + + linkScales: helpers$1.noop, + + // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly + getRingIndex: function(datasetIndex) { + var ringIndex = 0; + + for (var j = 0; j < datasetIndex; ++j) { + if (this.chart.isDatasetVisible(j)) { + ++ringIndex; + } + } + + return ringIndex; + }, + + update: function(reset) { + var me = this; + var chart = me.chart; + var chartArea = chart.chartArea; + var opts = chart.options; + var availableWidth = chartArea.right - chartArea.left; + var availableHeight = chartArea.bottom - chartArea.top; + var minSize = Math.min(availableWidth, availableHeight); + var offset = {x: 0, y: 0}; + var meta = me.getMeta(); + var arcs = meta.data; + var cutoutPercentage = opts.cutoutPercentage; + var circumference = opts.circumference; + var chartWeight = me._getRingWeight(me.index); + var i, ilen; + + // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc + if (circumference < Math.PI * 2.0) { + var startAngle = opts.rotation % (Math.PI * 2.0); + startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); + var endAngle = startAngle + circumference; + var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; + var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; + var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); + var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); + var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); + var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); + var cutout = cutoutPercentage / 100.0; + var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))}; + var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))}; + var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; + minSize = Math.min(availableWidth / size.width, availableHeight / size.height); + offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; + } + + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + arcs[i]._options = me._resolveElementOptions(arcs[i], i); + } + + chart.borderWidth = me.getMaxBorderWidth(); + chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); + chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0); + chart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1); + chart.offsetX = offset.x * chart.outerRadius; + chart.offsetY = offset.y * chart.outerRadius; + + meta.total = me.calculateTotal(); + + me.outerRadius = chart.outerRadius - chart.radiusLength * me._getRingWeightOffset(me.index); + me.innerRadius = Math.max(me.outerRadius - chart.radiusLength * chartWeight, 0); + + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + me.updateElement(arcs[i], i, reset); + } + }, + + updateElement: function(arc, index, reset) { + var me = this; + var chart = me.chart; + var chartArea = chart.chartArea; + var opts = chart.options; + var animationOpts = opts.animation; + var centerX = (chartArea.left + chartArea.right) / 2; + var centerY = (chartArea.top + chartArea.bottom) / 2; + var startAngle = opts.rotation; // non reset case handled later + var endAngle = opts.rotation; // non reset case handled later + var dataset = me.getDataset(); + var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)); + var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; + var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; + var options = arc._options || {}; + + helpers$1.extend(arc, { + // Utility + _datasetIndex: me.index, + _index: index, + + // Desired view properties + _model: { + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + borderAlign: options.borderAlign, + x: centerX + chart.offsetX, + y: centerY + chart.offsetY, + startAngle: startAngle, + endAngle: endAngle, + circumference: circumference, + outerRadius: outerRadius, + innerRadius: innerRadius, + label: helpers$1.valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) + } + }); + + var model = arc._model; + + // Set correct angles if not resetting + if (!reset || !animationOpts.animateRotate) { + if (index === 0) { + model.startAngle = opts.rotation; + } else { + model.startAngle = me.getMeta().data[index - 1]._model.endAngle; + } + + model.endAngle = model.startAngle + model.circumference; + } + + arc.pivot(); + }, + + calculateTotal: function() { + var dataset = this.getDataset(); + var meta = this.getMeta(); + var total = 0; + var value; + + helpers$1.each(meta.data, function(element, index) { + value = dataset.data[index]; + if (!isNaN(value) && !element.hidden) { + total += Math.abs(value); + } + }); + + /* if (total === 0) { + total = NaN; + }*/ + + return total; + }, + + calculateCircumference: function(value) { + var total = this.getMeta().total; + if (total > 0 && !isNaN(value)) { + return (Math.PI * 2.0) * (Math.abs(value) / total); + } + return 0; + }, + + // gets the max border or hover width to properly scale pie charts + getMaxBorderWidth: function(arcs) { + var me = this; + var max = 0; + var chart = me.chart; + var i, ilen, meta, arc, controller, options, borderWidth, hoverWidth; + + if (!arcs) { + // Find the outmost visible dataset + for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { + if (chart.isDatasetVisible(i)) { + meta = chart.getDatasetMeta(i); + arcs = meta.data; + if (i !== me.index) { + controller = meta.controller; + } + break; + } + } + } + + if (!arcs) { + return 0; + } + + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + arc = arcs[i]; + options = controller ? controller._resolveElementOptions(arc, i) : arc._options; + if (options.borderAlign !== 'inner') { + borderWidth = options.borderWidth; + hoverWidth = options.hoverBorderWidth; + + max = borderWidth > max ? borderWidth : max; + max = hoverWidth > max ? hoverWidth : max; + } + } + return max; + }, + + /** + * @protected + */ + setHoverStyle: function(arc) { + var model = arc._model; + var options = arc._options; + var getHoverColor = helpers$1.getHoverColor; + + arc.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + }; + + model.backgroundColor = valueOrDefault$4(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault$4(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault$4(options.hoverBorderWidth, options.borderWidth); + }, + + /** + * @private + */ + _resolveElementOptions: function(arc, index) { + var me = this; + var chart = me.chart; + var dataset = me.getDataset(); + var custom = arc.custom || {}; + var options = chart.options.elements.arc; + var values = {}; + var i, ilen, key; + + // Scriptable options + var context = { + chart: chart, + dataIndex: index, + dataset: dataset, + datasetIndex: me.index + }; + + var keys = [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'borderAlign', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + ]; + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + values[key] = resolve$3([ + custom[key], + dataset[key], + options[key] + ], context, index); + } + + return values; + }, + + /** + * Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly + * @private + */ + _getRingWeightOffset: function(datasetIndex) { + var ringWeightOffset = 0; + + for (var i = 0; i < datasetIndex; ++i) { + if (this.chart.isDatasetVisible(i)) { + ringWeightOffset += this._getRingWeight(i); + } + } + + return ringWeightOffset; + }, + + /** + * @private + */ + _getRingWeight: function(dataSetIndex) { + return Math.max(valueOrDefault$4(this.chart.data.datasets[dataSetIndex].weight, 1), 0); + }, + + /** + * Returns the sum of all visibile data set weights. This value can be 0. + * @private + */ + _getVisibleDatasetWeightTotal: function() { + return this._getRingWeightOffset(this.chart.data.datasets.length); + } +}); + +core_defaults._set('horizontalBar', { + hover: { + mode: 'index', + axis: 'y' + }, + + scales: { + xAxes: [{ + type: 'linear', + position: 'bottom' + }], + + yAxes: [{ + type: 'category', + position: 'left', + categoryPercentage: 0.8, + barPercentage: 0.9, + offset: true, + gridLines: { + offsetGridLines: true + } + }] + }, + + elements: { + rectangle: { + borderSkipped: 'left' + } + }, + + tooltips: { + mode: 'index', + axis: 'y' + } +}); + +var controller_horizontalBar = controller_bar.extend({ + /** + * @private + */ + _getValueScaleId: function() { + return this.getMeta().xAxisID; + }, + + /** + * @private + */ + _getIndexScaleId: function() { + return this.getMeta().yAxisID; + } +}); + +var valueOrDefault$5 = helpers$1.valueOrDefault; +var resolve$4 = helpers$1.options.resolve; +var isPointInArea = helpers$1.canvas._isPointInArea; + +core_defaults._set('line', { + showLines: true, + spanGaps: false, + + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'category', + id: 'x-axis-0' + }], + yAxes: [{ + type: 'linear', + id: 'y-axis-0' + }] + } +}); + +function lineEnabled(dataset, options) { + return valueOrDefault$5(dataset.showLine, options.showLines); +} + +var controller_line = core_datasetController.extend({ + + datasetElementType: elements.Line, + + dataElementType: elements.Point, + + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var line = meta.dataset; + var points = meta.data || []; + var scale = me.getScaleForId(meta.yAxisID); + var dataset = me.getDataset(); + var showLine = lineEnabled(dataset, me.chart.options); + var i, ilen; + + // Update Line + if (showLine) { + // Compatibility: If the properties are defined with only the old name, use those values + if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { + dataset.lineTension = dataset.tension; + } + + // Utility + line._scale = scale; + line._datasetIndex = me.index; + // Data + line._children = points; + // Model + line._model = me._resolveLineOptions(line); + + line.pivot(); + } + + // Update Points + for (i = 0, ilen = points.length; i < ilen; ++i) { + me.updateElement(points[i], i, reset); + } + + if (showLine && line._model.tension !== 0) { + me.updateBezierControlPoints(); + } + + // Now pivot the point for animation + for (i = 0, ilen = points.length; i < ilen; ++i) { + points[i].pivot(); + } + }, + + updateElement: function(point, index, reset) { + var me = this; + var meta = me.getMeta(); + var custom = point.custom || {}; + var dataset = me.getDataset(); + var datasetIndex = me.index; + var value = dataset.data[index]; + var yScale = me.getScaleForId(meta.yAxisID); + var xScale = me.getScaleForId(meta.xAxisID); + var lineModel = meta.dataset._model; + var x, y; + + var options = me._resolvePointOptions(point, index); + + x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); + y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); + + // Utility + point._xScale = xScale; + point._yScale = yScale; + point._options = options; + point._datasetIndex = datasetIndex; + point._index = index; + + // Desired view properties + point._model = { + x: x, + y: y, + skip: custom.skip || isNaN(x) || isNaN(y), + // Appearance + radius: options.radius, + pointStyle: options.pointStyle, + rotation: options.rotation, + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + tension: valueOrDefault$5(custom.tension, lineModel ? lineModel.tension : 0), + steppedLine: lineModel ? lineModel.steppedLine : false, + // Tooltip + hitRadius: options.hitRadius + }; + }, + + /** + * @private + */ + _resolvePointOptions: function(element, index) { + var me = this; + var chart = me.chart; + var dataset = chart.data.datasets[me.index]; + var custom = element.custom || {}; + var options = chart.options.elements.point; + var values = {}; + var i, ilen, key; + + // Scriptable options + var context = { + chart: chart, + dataIndex: index, + dataset: dataset, + datasetIndex: me.index + }; + + var ELEMENT_OPTIONS = { + backgroundColor: 'pointBackgroundColor', + borderColor: 'pointBorderColor', + borderWidth: 'pointBorderWidth', + hitRadius: 'pointHitRadius', + hoverBackgroundColor: 'pointHoverBackgroundColor', + hoverBorderColor: 'pointHoverBorderColor', + hoverBorderWidth: 'pointHoverBorderWidth', + hoverRadius: 'pointHoverRadius', + pointStyle: 'pointStyle', + radius: 'pointRadius', + rotation: 'pointRotation' + }; + var keys = Object.keys(ELEMENT_OPTIONS); + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + values[key] = resolve$4([ + custom[key], + dataset[ELEMENT_OPTIONS[key]], + dataset[key], + options[key] + ], context, index); + } + + return values; + }, + + /** + * @private + */ + _resolveLineOptions: function(element) { + var me = this; + var chart = me.chart; + var dataset = chart.data.datasets[me.index]; + var custom = element.custom || {}; + var options = chart.options; + var elementOptions = options.elements.line; + var values = {}; + var i, ilen, key; + + var keys = [ + 'backgroundColor', + 'borderWidth', + 'borderColor', + 'borderCapStyle', + 'borderDash', + 'borderDashOffset', + 'borderJoinStyle', + 'fill', + 'cubicInterpolationMode' + ]; + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + values[key] = resolve$4([ + custom[key], + dataset[key], + elementOptions[key] + ]); + } + + // The default behavior of lines is to break at null values, according + // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 + // This option gives lines the ability to span gaps + values.spanGaps = valueOrDefault$5(dataset.spanGaps, options.spanGaps); + values.tension = valueOrDefault$5(dataset.lineTension, elementOptions.tension); + values.steppedLine = resolve$4([custom.steppedLine, dataset.steppedLine, elementOptions.stepped]); + + return values; + }, + + calculatePointY: function(value, index, datasetIndex) { + var me = this; + var chart = me.chart; + var meta = me.getMeta(); + var yScale = me.getScaleForId(meta.yAxisID); + var sumPos = 0; + var sumNeg = 0; + var i, ds, dsMeta; + + if (yScale.options.stacked) { + for (i = 0; i < datasetIndex; i++) { + ds = chart.data.datasets[i]; + dsMeta = chart.getDatasetMeta(i); + if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { + var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); + if (stackedRightValue < 0) { + sumNeg += stackedRightValue || 0; + } else { + sumPos += stackedRightValue || 0; + } + } + } + + var rightValue = Number(yScale.getRightValue(value)); + if (rightValue < 0) { + return yScale.getPixelForValue(sumNeg + rightValue); + } + return yScale.getPixelForValue(sumPos + rightValue); + } + + return yScale.getPixelForValue(value); + }, + + updateBezierControlPoints: function() { + var me = this; + var chart = me.chart; + var meta = me.getMeta(); + var lineModel = meta.dataset._model; + var area = chart.chartArea; + var points = meta.data || []; + var i, ilen, model, controlPoints; + + // Only consider points that are drawn in case the spanGaps option is used + if (lineModel.spanGaps) { + points = points.filter(function(pt) { + return !pt._model.skip; + }); + } + + function capControlPoint(pt, min, max) { + return Math.max(Math.min(pt, max), min); + } + + if (lineModel.cubicInterpolationMode === 'monotone') { + helpers$1.splineCurveMonotone(points); + } else { + for (i = 0, ilen = points.length; i < ilen; ++i) { + model = points[i]._model; + controlPoints = helpers$1.splineCurve( + helpers$1.previousItem(points, i)._model, + model, + helpers$1.nextItem(points, i)._model, + lineModel.tension + ); + model.controlPointPreviousX = controlPoints.previous.x; + model.controlPointPreviousY = controlPoints.previous.y; + model.controlPointNextX = controlPoints.next.x; + model.controlPointNextY = controlPoints.next.y; + } + } + + if (chart.options.elements.line.capBezierPoints) { + for (i = 0, ilen = points.length; i < ilen; ++i) { + model = points[i]._model; + if (isPointInArea(model, area)) { + if (i > 0 && isPointInArea(points[i - 1]._model, area)) { + model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); + model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); + } + if (i < points.length - 1 && isPointInArea(points[i + 1]._model, area)) { + model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); + model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); + } + } + } + } + }, + + draw: function() { + var me = this; + var chart = me.chart; + var meta = me.getMeta(); + var points = meta.data || []; + var area = chart.chartArea; + var ilen = points.length; + var halfBorderWidth; + var i = 0; + + if (lineEnabled(me.getDataset(), chart.options)) { + halfBorderWidth = (meta.dataset._model.borderWidth || 0) / 2; + + helpers$1.canvas.clipArea(chart.ctx, { + left: area.left, + right: area.right, + top: area.top - halfBorderWidth, + bottom: area.bottom + halfBorderWidth + }); + + meta.dataset.draw(); + + helpers$1.canvas.unclipArea(chart.ctx); + } + + // Draw the points + for (; i < ilen; ++i) { + points[i].draw(area); + } + }, + + /** + * @protected + */ + setHoverStyle: function(point) { + var model = point._model; + var options = point._options; + var getHoverColor = helpers$1.getHoverColor; + + point.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + radius: model.radius + }; + + model.backgroundColor = valueOrDefault$5(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault$5(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault$5(options.hoverBorderWidth, options.borderWidth); + model.radius = valueOrDefault$5(options.hoverRadius, options.radius); + }, +}); + +var resolve$5 = helpers$1.options.resolve; + +core_defaults._set('polarArea', { + scale: { + type: 'radialLinear', + angleLines: { + display: false + }, + gridLines: { + circular: true + }, + pointLabels: { + display: false + }, + ticks: { + beginAtZero: true + } + }, + + // Boolean - Whether to animate the rotation of the chart + animation: { + animateRotate: true, + animateScale: true + }, + + startAngle: -0.5 * Math.PI, + legendCallback: function(chart) { + var text = []; + text.push('
    '); + + var data = chart.data; + var datasets = data.datasets; + var labels = data.labels; + + if (datasets.length) { + for (var i = 0; i < datasets[0].data.length; ++i) { + text.push('
  • '); + if (labels[i]) { + text.push(labels[i]); + } + text.push('
  • '); + } + } + + 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 arcOpts = chart.options.elements.arc; + var fill = resolve$5([custom.backgroundColor, ds.backgroundColor, arcOpts.backgroundColor], undefined, i); + var stroke = resolve$5([custom.borderColor, ds.borderColor, arcOpts.borderColor], undefined, i); + var bw = resolve$5([custom.borderWidth, ds.borderWidth, arcOpts.borderWidth], undefined, i); + + return { + text: label, + fillStyle: fill, + strokeStyle: stroke, + lineWidth: bw, + hidden: isNaN(ds.data[i]) || meta.data[i].hidden, + + // Extra data used for toggling the correct item + index: i + }; + }); + } + return []; + } + }, + + onClick: function(e, legendItem) { + var index = legendItem.index; + var chart = this.chart; + var i, ilen, meta; + + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + meta = chart.getDatasetMeta(i); + meta.data[index].hidden = !meta.data[index].hidden; + } + + chart.update(); + } + }, + + // Need to override these to give a nice default + tooltips: { + callbacks: { + title: function() { + return ''; + }, + label: function(item, data) { + return data.labels[item.index] + ': ' + item.yLabel; + } + } + } +}); + +var controller_polarArea = core_datasetController.extend({ + + dataElementType: elements.Arc, + + linkScales: helpers$1.noop, + + update: function(reset) { + var me = this; + var dataset = me.getDataset(); + var meta = me.getMeta(); + var start = me.chart.options.startAngle || 0; + var starts = me._starts = []; + var angles = me._angles = []; + var arcs = meta.data; + var i, ilen, angle; + + me._updateRadius(); + + meta.count = me.countVisibleElements(); + + for (i = 0, ilen = dataset.data.length; i < ilen; i++) { + starts[i] = start; + angle = me._computeAngle(i); + angles[i] = angle; + start += angle; + } + + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + arcs[i]._options = me._resolveElementOptions(arcs[i], i); + me.updateElement(arcs[i], i, reset); + } + }, + + /** + * @private + */ + _updateRadius: function() { + var me = this; + var chart = me.chart; + var chartArea = chart.chartArea; + var opts = chart.options; + var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); + + chart.outerRadius = Math.max(minSize / 2, 0); + chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); + chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); + + me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); + me.innerRadius = me.outerRadius - chart.radiusLength; + }, + + updateElement: function(arc, index, reset) { + var me = this; + var chart = me.chart; + var dataset = me.getDataset(); + var opts = chart.options; + var animationOpts = opts.animation; + var scale = chart.scale; + var labels = chart.data.labels; + + var centerX = scale.xCenter; + var centerY = scale.yCenter; + + // var negHalfPI = -0.5 * Math.PI; + var datasetStartAngle = opts.startAngle; + var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); + var startAngle = me._starts[index]; + var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]); + + var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); + var options = arc._options || {}; + + helpers$1.extend(arc, { + // Utility + _datasetIndex: me.index, + _index: index, + _scale: scale, + + // Desired view properties + _model: { + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + borderAlign: options.borderAlign, + x: centerX, + y: centerY, + innerRadius: 0, + outerRadius: reset ? resetRadius : distance, + startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, + endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, + label: helpers$1.valueAtIndexOrDefault(labels, index, labels[index]) + } + }); + + arc.pivot(); + }, + + countVisibleElements: function() { + var dataset = this.getDataset(); + var meta = this.getMeta(); + var count = 0; + + helpers$1.each(meta.data, function(element, index) { + if (!isNaN(dataset.data[index]) && !element.hidden) { + count++; + } + }); + + return count; + }, + + /** + * @protected + */ + setHoverStyle: function(arc) { + var model = arc._model; + var options = arc._options; + var getHoverColor = helpers$1.getHoverColor; + var valueOrDefault = helpers$1.valueOrDefault; + + arc.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + }; + + model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth); + }, + + /** + * @private + */ + _resolveElementOptions: function(arc, index) { + var me = this; + var chart = me.chart; + var dataset = me.getDataset(); + var custom = arc.custom || {}; + var options = chart.options.elements.arc; + var values = {}; + var i, ilen, key; + + // Scriptable options + var context = { + chart: chart, + dataIndex: index, + dataset: dataset, + datasetIndex: me.index + }; + + var keys = [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'borderAlign', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + ]; + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + values[key] = resolve$5([ + custom[key], + dataset[key], + options[key] + ], context, index); + } + + return values; + }, + + /** + * @private + */ + _computeAngle: function(index) { + var me = this; + var count = this.getMeta().count; + var dataset = me.getDataset(); + var meta = me.getMeta(); + + if (isNaN(dataset.data[index]) || meta.data[index].hidden) { + return 0; + } + + // Scriptable options + var context = { + chart: me.chart, + dataIndex: index, + dataset: dataset, + datasetIndex: me.index + }; + + return resolve$5([ + me.chart.options.elements.arc.angle, + (2 * Math.PI) / count + ], context, index); + } +}); + +core_defaults._set('pie', helpers$1.clone(core_defaults.doughnut)); +core_defaults._set('pie', { + cutoutPercentage: 0 +}); + +// Pie charts are Doughnut chart with different defaults +var controller_pie = controller_doughnut; + +var valueOrDefault$6 = helpers$1.valueOrDefault; +var resolve$6 = helpers$1.options.resolve; + +core_defaults._set('radar', { + scale: { + type: 'radialLinear' + }, + elements: { + line: { + tension: 0 // no bezier in radar + } + } +}); + +var controller_radar = core_datasetController.extend({ + + datasetElementType: elements.Line, + + dataElementType: elements.Point, + + linkScales: helpers$1.noop, + + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var line = meta.dataset; + var points = meta.data || []; + var scale = me.chart.scale; + var dataset = me.getDataset(); + var i, ilen; + + // Compatibility: If the properties are defined with only the old name, use those values + if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { + dataset.lineTension = dataset.tension; + } + + // Utility + line._scale = scale; + line._datasetIndex = me.index; + // Data + line._children = points; + line._loop = true; + // Model + line._model = me._resolveLineOptions(line); + + line.pivot(); + + // Update Points + for (i = 0, ilen = points.length; i < ilen; ++i) { + me.updateElement(points[i], i, reset); + } + + // Update bezier control points + me.updateBezierControlPoints(); + + // Now pivot the point for animation + for (i = 0, ilen = points.length; i < ilen; ++i) { + points[i].pivot(); + } + }, + + updateElement: function(point, index, reset) { + var me = this; + var custom = point.custom || {}; + var dataset = me.getDataset(); + var scale = me.chart.scale; + var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); + var options = me._resolvePointOptions(point, index); + var lineModel = me.getMeta().dataset._model; + var x = reset ? scale.xCenter : pointPosition.x; + var y = reset ? scale.yCenter : pointPosition.y; + + // Utility + point._scale = scale; + point._options = options; + point._datasetIndex = me.index; + point._index = index; + + // Desired view properties + point._model = { + x: x, // value not used in dataset scale, but we want a consistent API between scales + y: y, + skip: custom.skip || isNaN(x) || isNaN(y), + // Appearance + radius: options.radius, + pointStyle: options.pointStyle, + rotation: options.rotation, + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + tension: valueOrDefault$6(custom.tension, lineModel ? lineModel.tension : 0), + + // Tooltip + hitRadius: options.hitRadius + }; + }, + + /** + * @private + */ + _resolvePointOptions: function(element, index) { + var me = this; + var chart = me.chart; + var dataset = chart.data.datasets[me.index]; + var custom = element.custom || {}; + var options = chart.options.elements.point; + var values = {}; + var i, ilen, key; + + // Scriptable options + var context = { + chart: chart, + dataIndex: index, + dataset: dataset, + datasetIndex: me.index + }; + + var ELEMENT_OPTIONS = { + backgroundColor: 'pointBackgroundColor', + borderColor: 'pointBorderColor', + borderWidth: 'pointBorderWidth', + hitRadius: 'pointHitRadius', + hoverBackgroundColor: 'pointHoverBackgroundColor', + hoverBorderColor: 'pointHoverBorderColor', + hoverBorderWidth: 'pointHoverBorderWidth', + hoverRadius: 'pointHoverRadius', + pointStyle: 'pointStyle', + radius: 'pointRadius', + rotation: 'pointRotation' + }; + var keys = Object.keys(ELEMENT_OPTIONS); + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + values[key] = resolve$6([ + custom[key], + dataset[ELEMENT_OPTIONS[key]], + dataset[key], + options[key] + ], context, index); + } + + return values; + }, + + /** + * @private + */ + _resolveLineOptions: function(element) { + var me = this; + var chart = me.chart; + var dataset = chart.data.datasets[me.index]; + var custom = element.custom || {}; + var options = chart.options.elements.line; + var values = {}; + var i, ilen, key; + + var keys = [ + 'backgroundColor', + 'borderWidth', + 'borderColor', + 'borderCapStyle', + 'borderDash', + 'borderDashOffset', + 'borderJoinStyle', + 'fill' + ]; + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + values[key] = resolve$6([ + custom[key], + dataset[key], + options[key] + ]); + } + + values.tension = valueOrDefault$6(dataset.lineTension, options.tension); + + return values; + }, + + updateBezierControlPoints: function() { + var me = this; + var meta = me.getMeta(); + var area = me.chart.chartArea; + var points = meta.data || []; + var i, ilen, model, controlPoints; + + function capControlPoint(pt, min, max) { + return Math.max(Math.min(pt, max), min); + } + + for (i = 0, ilen = points.length; i < ilen; ++i) { + model = points[i]._model; + controlPoints = helpers$1.splineCurve( + helpers$1.previousItem(points, i, true)._model, + model, + helpers$1.nextItem(points, i, true)._model, + model.tension + ); + + // Prevent the bezier going outside of the bounds of the graph + model.controlPointPreviousX = capControlPoint(controlPoints.previous.x, area.left, area.right); + model.controlPointPreviousY = capControlPoint(controlPoints.previous.y, area.top, area.bottom); + model.controlPointNextX = capControlPoint(controlPoints.next.x, area.left, area.right); + model.controlPointNextY = capControlPoint(controlPoints.next.y, area.top, area.bottom); + } + }, + + setHoverStyle: function(point) { + var model = point._model; + var options = point._options; + var getHoverColor = helpers$1.getHoverColor; + + point.$previousStyle = { + backgroundColor: model.backgroundColor, + borderColor: model.borderColor, + borderWidth: model.borderWidth, + radius: model.radius + }; + + model.backgroundColor = valueOrDefault$6(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault$6(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault$6(options.hoverBorderWidth, options.borderWidth); + model.radius = valueOrDefault$6(options.hoverRadius, options.radius); + } +}); + +core_defaults._set('scatter', { + hover: { + mode: 'single' + }, + + scales: { + xAxes: [{ + id: 'x-axis-1', // need an ID so datasets can reference the scale + type: 'linear', // scatter should not use a category axis + position: 'bottom' + }], + yAxes: [{ + id: 'y-axis-1', + type: 'linear', + position: 'left' + }] + }, + + showLines: false, + + tooltips: { + callbacks: { + title: function() { + return ''; // doesn't make sense for scatter since data are formatted as a point + }, + label: function(item) { + return '(' + item.xLabel + ', ' + item.yLabel + ')'; + } + } + } +}); + +// Scatter charts use line controllers +var controller_scatter = controller_line; + +// NOTE export a map in which the key represents the controller type, not +// the class, and so must be CamelCase in order to be correctly retrieved +// by the controller in core.controller.js (`controllers[meta.type]`). + +var controllers = { + bar: controller_bar, + bubble: controller_bubble, + doughnut: controller_doughnut, + horizontalBar: controller_horizontalBar, + line: controller_line, + polarArea: controller_polarArea, + pie: controller_pie, + radar: controller_radar, + scatter: controller_scatter +}; + +/** + * Helper function to get relative position for an event + * @param {Event|IEvent} event - The event to get the position for + * @param {Chart} chart - The chart + * @returns {object} the event position + */ +function getRelativePosition(e, chart) { + if (e.native) { + return { + x: e.x, + y: e.y + }; + } + + return helpers$1.getRelativePosition(e, chart); +} + +/** + * Helper function to traverse all of the visible elements in the chart + * @param {Chart} chart - the chart + * @param {function} handler - the callback to execute for each visible item + */ +function parseVisibleItems(chart, handler) { + var datasets = chart.data.datasets; + var meta, i, j, ilen, jlen; + + for (i = 0, ilen = datasets.length; i < ilen; ++i) { + if (!chart.isDatasetVisible(i)) { + continue; + } + + meta = chart.getDatasetMeta(i); + for (j = 0, jlen = meta.data.length; j < jlen; ++j) { + var element = meta.data[j]; + if (!element._view.skip) { + handler(element); + } + } + } +} + +/** + * Helper function to get the items that intersect the event position + * @param {ChartElement[]} items - elements to filter + * @param {object} position - the point to be nearest to + * @return {ChartElement[]} the nearest items + */ +function getIntersectItems(chart, position) { + var elements = []; + + parseVisibleItems(chart, function(element) { + if (element.inRange(position.x, position.y)) { + elements.push(element); + } + }); + + return elements; +} + +/** + * Helper function to get the items nearest to the event position considering all visible items in teh chart + * @param {Chart} chart - the chart to look at elements from + * @param {object} position - the point to be nearest to + * @param {boolean} intersect - if true, only consider items that intersect the position + * @param {function} distanceMetric - function to provide the distance between points + * @return {ChartElement[]} the nearest items + */ +function getNearestItems(chart, position, intersect, distanceMetric) { + var minDistance = Number.POSITIVE_INFINITY; + var nearestItems = []; + + parseVisibleItems(chart, function(element) { + if (intersect && !element.inRange(position.x, position.y)) { + return; + } + + var center = element.getCenterPoint(); + var distance = distanceMetric(position, center); + if (distance < minDistance) { + nearestItems = [element]; + minDistance = distance; + } else if (distance === minDistance) { + // Can have multiple items at the same distance in which case we sort by size + nearestItems.push(element); + } + }); + + return nearestItems; +} + +/** + * Get a distance metric function for two points based on the + * axis mode setting + * @param {string} axis - the axis mode. x|y|xy + */ +function getDistanceMetricForAxis(axis) { + var useX = axis.indexOf('x') !== -1; + var useY = axis.indexOf('y') !== -1; + + return function(pt1, pt2) { + var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; + var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; + return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); + }; +} + +function indexMode(chart, e, options) { + var position = getRelativePosition(e, chart); + // Default axis for index mode is 'x' to match old behaviour + options.axis = options.axis || 'x'; + var distanceMetric = getDistanceMetricForAxis(options.axis); + var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); + var elements = []; + + if (!items.length) { + return []; + } + + chart.data.datasets.forEach(function(dataset, datasetIndex) { + if (chart.isDatasetVisible(datasetIndex)) { + var meta = chart.getDatasetMeta(datasetIndex); + var element = meta.data[items[0]._index]; + + // don't count items that are skipped (null data) + if (element && !element._view.skip) { + elements.push(element); + } + } + }); + + return elements; +} + +/** + * @interface IInteractionOptions + */ +/** + * If true, only consider items that intersect the point + * @name IInterfaceOptions#boolean + * @type Boolean + */ + +/** + * Contains interaction related functions + * @namespace Chart.Interaction + */ +var core_interaction = { + // Helper function for different modes + modes: { + single: function(chart, e) { + var position = getRelativePosition(e, chart); + var elements = []; + + parseVisibleItems(chart, function(element) { + if (element.inRange(position.x, position.y)) { + elements.push(element); + return elements; + } + }); + + return elements.slice(0, 1); + }, + + /** + * @function Chart.Interaction.modes.label + * @deprecated since version 2.4.0 + * @todo remove at version 3 + * @private + */ + label: indexMode, + + /** + * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something + * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item + * @function Chart.Interaction.modes.index + * @since v2.4.0 + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use during interaction + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + index: indexMode, + + /** + * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something + * If the options.intersect is false, we find the nearest item and return the items in that dataset + * @function Chart.Interaction.modes.dataset + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use during interaction + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + dataset: function(chart, e, options) { + var position = getRelativePosition(e, chart); + options.axis = options.axis || 'xy'; + var distanceMetric = getDistanceMetricForAxis(options.axis); + var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); + + if (items.length > 0) { + items = chart.getDatasetMeta(items[0]._datasetIndex).data; + } + + return items; + }, + + /** + * @function Chart.Interaction.modes.x-axis + * @deprecated since version 2.4.0. Use index mode and intersect == true + * @todo remove at version 3 + * @private + */ + 'x-axis': function(chart, e) { + return indexMode(chart, e, {intersect: false}); + }, + + /** + * Point mode returns all elements that hit test based on the event position + * of the event + * @function Chart.Interaction.modes.intersect + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + point: function(chart, e) { + var position = getRelativePosition(e, chart); + return getIntersectItems(chart, position); + }, + + /** + * nearest mode returns the element closest to the point + * @function Chart.Interaction.modes.intersect + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + nearest: function(chart, e, options) { + var position = getRelativePosition(e, chart); + options.axis = options.axis || 'xy'; + var distanceMetric = getDistanceMetricForAxis(options.axis); + return getNearestItems(chart, position, options.intersect, distanceMetric); + }, + + /** + * x mode returns the elements that hit-test at the current x coordinate + * @function Chart.Interaction.modes.x + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + x: function(chart, e, options) { + var position = getRelativePosition(e, chart); + var items = []; + var intersectsItem = false; + + parseVisibleItems(chart, function(element) { + if (element.inXRange(position.x)) { + items.push(element); + } + + if (element.inRange(position.x, position.y)) { + intersectsItem = true; + } + }); + + // If we want to trigger on an intersect and we don't have any items + // that intersect the position, return nothing + if (options.intersect && !intersectsItem) { + items = []; + } + return items; + }, + + /** + * y mode returns the elements that hit-test at the current y coordinate + * @function Chart.Interaction.modes.y + * @param {Chart} chart - the chart we are returning items from + * @param {Event} e - the event we are find things at + * @param {IInteractionOptions} options - options to use + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + y: function(chart, e, options) { + var position = getRelativePosition(e, chart); + var items = []; + var intersectsItem = false; + + parseVisibleItems(chart, function(element) { + if (element.inYRange(position.y)) { + items.push(element); + } + + if (element.inRange(position.x, position.y)) { + intersectsItem = true; + } + }); + + // If we want to trigger on an intersect and we don't have any items + // that intersect the position, return nothing + if (options.intersect && !intersectsItem) { + items = []; + } + return items; + } + } +}; + +function filterByPosition(array, position) { + return helpers$1.where(array, function(v) { + return v.position === position; + }); +} + +function sortByWeight(array, reverse) { + array.forEach(function(v, i) { + v._tmpIndex_ = i; + return v; + }); + array.sort(function(a, b) { + var v0 = reverse ? b : a; + var v1 = reverse ? a : b; + return v0.weight === v1.weight ? + v0._tmpIndex_ - v1._tmpIndex_ : + v0.weight - v1.weight; + }); + array.forEach(function(v) { + delete v._tmpIndex_; + }); +} + +function findMaxPadding(boxes) { + var top = 0; + var left = 0; + var bottom = 0; + var right = 0; + helpers$1.each(boxes, function(box) { + if (box.getPadding) { + var boxPadding = box.getPadding(); + top = Math.max(top, boxPadding.top); + left = Math.max(left, boxPadding.left); + bottom = Math.max(bottom, boxPadding.bottom); + right = Math.max(right, boxPadding.right); + } + }); + return { + top: top, + left: left, + bottom: bottom, + right: right + }; +} + +function addSizeByPosition(boxes, size) { + helpers$1.each(boxes, function(box) { + size[box.position] += box.isHorizontal() ? box.height : box.width; + }); +} + +core_defaults._set('global', { + layout: { + padding: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + } +}); + +/** + * @interface ILayoutItem + * @prop {string} position - The position of the item in the chart layout. Possible values are + * 'left', 'top', 'right', 'bottom', and 'chartArea' + * @prop {number} weight - The weight used to sort the item. Higher weights are further away from the chart area + * @prop {boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down + * @prop {function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom) + * @prop {function} update - Takes two parameters: width and height. Returns size of item + * @prop {function} getPadding - Returns an object with padding on the edges + * @prop {number} width - Width of item. Must be valid after update() + * @prop {number} height - Height of item. Must be valid after update() + * @prop {number} left - Left edge of the item. Set by layout system and cannot be used in update + * @prop {number} top - Top edge of the item. Set by layout system and cannot be used in update + * @prop {number} right - Right edge of the item. Set by layout system and cannot be used in update + * @prop {number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update + */ + +// The layout service is very self explanatory. It's responsible for the layout within a chart. +// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need +// It is this service's responsibility of carrying out that layout. +var core_layouts = { + defaults: {}, + + /** + * Register a box to a chart. + * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. + * @param {Chart} chart - the chart to use + * @param {ILayoutItem} item - the item to add to be layed out + */ + addBox: function(chart, item) { + if (!chart.boxes) { + chart.boxes = []; + } + + // initialize item with default values + item.fullWidth = item.fullWidth || false; + item.position = item.position || 'top'; + item.weight = item.weight || 0; + + chart.boxes.push(item); + }, + + /** + * Remove a layoutItem from a chart + * @param {Chart} chart - the chart to remove the box from + * @param {ILayoutItem} layoutItem - the item to remove from the layout + */ + removeBox: function(chart, layoutItem) { + var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; + if (index !== -1) { + chart.boxes.splice(index, 1); + } + }, + + /** + * Sets (or updates) options on the given `item`. + * @param {Chart} chart - the chart in which the item lives (or will be added to) + * @param {ILayoutItem} item - the item to configure with the given options + * @param {object} options - the new item options. + */ + configure: function(chart, item, options) { + var props = ['fullWidth', 'position', 'weight']; + var ilen = props.length; + var i = 0; + var prop; + + for (; i < ilen; ++i) { + prop = props[i]; + if (options.hasOwnProperty(prop)) { + item[prop] = options[prop]; + } + } + }, + + /** + * Fits boxes of the given chart into the given size by having each box measure itself + * then running a fitting algorithm + * @param {Chart} chart - the chart + * @param {number} width - the width to fit into + * @param {number} height - the height to fit into + */ + update: function(chart, width, height) { + if (!chart) { + return; + } + + var layoutOptions = chart.options.layout || {}; + var padding = helpers$1.options.toPadding(layoutOptions.padding); + var leftPadding = padding.left; + var rightPadding = padding.right; + var topPadding = padding.top; + var bottomPadding = padding.bottom; + + var leftBoxes = filterByPosition(chart.boxes, 'left'); + var rightBoxes = filterByPosition(chart.boxes, 'right'); + var topBoxes = filterByPosition(chart.boxes, 'top'); + var bottomBoxes = filterByPosition(chart.boxes, 'bottom'); + var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea'); + + // Sort boxes by weight. A higher weight is further away from the chart area + sortByWeight(leftBoxes, true); + sortByWeight(rightBoxes, false); + sortByWeight(topBoxes, true); + sortByWeight(bottomBoxes, false); + + var verticalBoxes = leftBoxes.concat(rightBoxes); + var horizontalBoxes = topBoxes.concat(bottomBoxes); + var outerBoxes = verticalBoxes.concat(horizontalBoxes); + + // Essentially we now have any number of boxes on each of the 4 sides. + // Our canvas looks like the following. + // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and + // B1 is the bottom axis + // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays + // These locations are single-box locations only, when trying to register a chartArea location that is already taken, + // an error will be thrown. + // + // |----------------------------------------------------| + // | T1 (Full Width) | + // |----------------------------------------------------| + // | | | T2 | | + // | |----|-------------------------------------|----| + // | | | C1 | | C2 | | + // | | |----| |----| | + // | | | | | + // | L1 | L2 | ChartArea (C0) | R1 | + // | | | | | + // | | |----| |----| | + // | | | C3 | | C4 | | + // | |----|-------------------------------------|----| + // | | | B1 | | + // |----------------------------------------------------| + // | B2 (Full Width) | + // |----------------------------------------------------| + // + // What we do to find the best sizing, we do the following + // 1. Determine the minimum size of the chart area. + // 2. Split the remaining width equally between each vertical axis + // 3. Split the remaining height equally between each horizontal axis + // 4. Give each layout the maximum size it can be. The layout will return it's minimum size + // 5. Adjust the sizes of each axis based on it's minimum reported size. + // 6. Refit each axis + // 7. Position each axis in the final location + // 8. Tell the chart the final location of the chart area + // 9. Tell any axes that overlay the chart area the positions of the chart area + + // Step 1 + var chartWidth = width - leftPadding - rightPadding; + var chartHeight = height - topPadding - bottomPadding; + var chartAreaWidth = chartWidth / 2; // min 50% + + // Step 2 + var verticalBoxWidth = (width - chartAreaWidth) / verticalBoxes.length; + + // Step 3 + // TODO re-limit horizontal axis height (this limit has affected only padding calculation since PR 1837) + // var horizontalBoxHeight = (height - chartAreaHeight) / horizontalBoxes.length; + + // Step 4 + var maxChartAreaWidth = chartWidth; + var maxChartAreaHeight = chartHeight; + var outerBoxSizes = {top: topPadding, left: leftPadding, bottom: bottomPadding, right: rightPadding}; + var minBoxSizes = []; + var maxPadding; + + function getMinimumBoxSize(box) { + var minSize; + var isHorizontal = box.isHorizontal(); + + if (isHorizontal) { + minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2); + maxChartAreaHeight -= minSize.height; + } else { + minSize = box.update(verticalBoxWidth, maxChartAreaHeight); + maxChartAreaWidth -= minSize.width; + } + + minBoxSizes.push({ + horizontal: isHorizontal, + width: minSize.width, + box: box, + }); + } + + helpers$1.each(outerBoxes, getMinimumBoxSize); + + // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478) + maxPadding = findMaxPadding(outerBoxes); + + // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could + // be if the axes are drawn at their minimum sizes. + // Steps 5 & 6 + + // Function to fit a box + function fitBox(box) { + var minBoxSize = helpers$1.findNextWhere(minBoxSizes, function(minBox) { + return minBox.box === box; + }); + + if (minBoxSize) { + if (minBoxSize.horizontal) { + var scaleMargin = { + left: Math.max(outerBoxSizes.left, maxPadding.left), + right: Math.max(outerBoxSizes.right, maxPadding.right), + top: 0, + bottom: 0 + }; + + // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends + // on the margin. Sometimes they need to increase in size slightly + box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin); + } else { + box.update(minBoxSize.width, maxChartAreaHeight); + } + } + } + + // Update, and calculate the left and right margins for the horizontal boxes + helpers$1.each(verticalBoxes, fitBox); + addSizeByPosition(verticalBoxes, outerBoxSizes); + + // Set the Left and Right margins for the horizontal boxes + helpers$1.each(horizontalBoxes, fitBox); + addSizeByPosition(horizontalBoxes, outerBoxSizes); + + function finalFitVerticalBox(box) { + var minBoxSize = helpers$1.findNextWhere(minBoxSizes, function(minSize) { + return minSize.box === box; + }); + + var scaleMargin = { + left: 0, + right: 0, + top: outerBoxSizes.top, + bottom: outerBoxSizes.bottom + }; + + if (minBoxSize) { + box.update(minBoxSize.width, maxChartAreaHeight, scaleMargin); + } + } + + // Let the left layout know the final margin + helpers$1.each(verticalBoxes, finalFitVerticalBox); + + // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance) + outerBoxSizes = {top: topPadding, left: leftPadding, bottom: bottomPadding, right: rightPadding}; + addSizeByPosition(outerBoxes, outerBoxSizes); + + // We may be adding some padding to account for rotated x axis labels + var leftPaddingAddition = Math.max(maxPadding.left - outerBoxSizes.left, 0); + outerBoxSizes.left += leftPaddingAddition; + outerBoxSizes.right += Math.max(maxPadding.right - outerBoxSizes.right, 0); + + var topPaddingAddition = Math.max(maxPadding.top - outerBoxSizes.top, 0); + outerBoxSizes.top += topPaddingAddition; + outerBoxSizes.bottom += Math.max(maxPadding.bottom - outerBoxSizes.bottom, 0); + + // Figure out if our chart area changed. This would occur if the dataset layout label rotation + // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do + // without calling `fit` again + var newMaxChartAreaHeight = height - outerBoxSizes.top - outerBoxSizes.bottom; + var newMaxChartAreaWidth = width - outerBoxSizes.left - outerBoxSizes.right; + + if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) { + helpers$1.each(verticalBoxes, function(box) { + box.height = newMaxChartAreaHeight; + }); + + helpers$1.each(horizontalBoxes, function(box) { + if (!box.fullWidth) { + box.width = newMaxChartAreaWidth; + } + }); + + maxChartAreaHeight = newMaxChartAreaHeight; + maxChartAreaWidth = newMaxChartAreaWidth; + } + + // Step 7 - Position the boxes + var left = leftPadding + leftPaddingAddition; + var top = topPadding + topPaddingAddition; + + function placeBox(box) { + if (box.isHorizontal()) { + box.left = box.fullWidth ? leftPadding : outerBoxSizes.left; + box.right = box.fullWidth ? width - rightPadding : outerBoxSizes.left + maxChartAreaWidth; + box.top = top; + box.bottom = top + box.height; + + // Move to next point + top = box.bottom; + + } else { + + box.left = left; + box.right = left + box.width; + box.top = outerBoxSizes.top; + box.bottom = outerBoxSizes.top + maxChartAreaHeight; + + // Move to next point + left = box.right; + } + } + + helpers$1.each(leftBoxes.concat(topBoxes), placeBox); + + // Account for chart width and height + left += maxChartAreaWidth; + top += maxChartAreaHeight; + + helpers$1.each(rightBoxes, placeBox); + helpers$1.each(bottomBoxes, placeBox); + + // Step 8 + chart.chartArea = { + left: outerBoxSizes.left, + top: outerBoxSizes.top, + right: outerBoxSizes.left + maxChartAreaWidth, + bottom: outerBoxSizes.top + maxChartAreaHeight + }; + + // Step 9 + helpers$1.each(chartAreaBoxes, function(box) { + box.left = chart.chartArea.left; + box.top = chart.chartArea.top; + box.right = chart.chartArea.right; + box.bottom = chart.chartArea.bottom; + + box.update(maxChartAreaWidth, maxChartAreaHeight); + }); + } +}; + +/** + * Platform fallback implementation (minimal). + * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939 + */ + +var platform_basic = { + acquireContext: function(item) { + if (item && item.canvas) { + // Support for any object associated to a canvas (including a context2d) + item = item.canvas; + } + + return item && item.getContext('2d') || null; + } +}; + +var platform_dom = "/*\n * DOM element rendering detection\n * https://davidwalsh.name/detect-node-insertion\n */\n@keyframes chartjs-render-animation {\n\tfrom { opacity: 0.99; }\n\tto { opacity: 1; }\n}\n\n.chartjs-render-monitor {\n\tanimation: chartjs-render-animation 0.001s;\n}\n\n/*\n * DOM element resizing detection\n * https://github.com/marcj/css-element-queries\n */\n.chartjs-size-monitor,\n.chartjs-size-monitor-expand,\n.chartjs-size-monitor-shrink {\n\tposition: absolute;\n\tdirection: ltr;\n\tleft: 0;\n\ttop: 0;\n\tright: 0;\n\tbottom: 0;\n\toverflow: hidden;\n\tpointer-events: none;\n\tvisibility: hidden;\n\tz-index: -1;\n}\n\n.chartjs-size-monitor-expand > div {\n\tposition: absolute;\n\twidth: 1000000px;\n\theight: 1000000px;\n\tleft: 0;\n\ttop: 0;\n}\n\n.chartjs-size-monitor-shrink > div {\n\tposition: absolute;\n\twidth: 200%;\n\theight: 200%;\n\tleft: 0;\n\ttop: 0;\n}\n"; + +var platform_dom$1 = /*#__PURE__*/Object.freeze({ +default: platform_dom +}); + +var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + +function commonjsRequire () { + throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs'); +} + +function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; +} + +function getCjsExportFromNamespace (n) { + return n && n.default || n; +} + +var stylesheet = getCjsExportFromNamespace(platform_dom$1); + +var EXPANDO_KEY = '$chartjs'; +var CSS_PREFIX = 'chartjs-'; +var CSS_SIZE_MONITOR = CSS_PREFIX + 'size-monitor'; +var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor'; +var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation'; +var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart']; + +/** + * DOM event types -> Chart.js event types. + * Note: only events with different types are mapped. + * @see https://developer.mozilla.org/en-US/docs/Web/Events + */ +var EVENT_TYPES = { + touchstart: 'mousedown', + touchmove: 'mousemove', + touchend: 'mouseup', + pointerenter: 'mouseenter', + pointerdown: 'mousedown', + pointermove: 'mousemove', + pointerup: 'mouseup', + pointerleave: 'mouseout', + pointerout: 'mouseout' +}; + +/** + * The "used" size is the final value of a dimension property after all calculations have + * been performed. This method uses the computed style of `element` but returns undefined + * if the computed style is not expressed in pixels. That can happen in some cases where + * `element` has a size relative to its parent and this last one is not yet displayed, + * for example because of `display: none` on a parent node. + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value + * @returns {number} Size in pixels or undefined if unknown. + */ +function readUsedSize(element, property) { + var value = helpers$1.getStyle(element, property); + var matches = value && value.match(/^(\d+)(\.\d+)?px$/); + return matches ? Number(matches[1]) : undefined; +} + +/** + * Initializes the canvas style and render size without modifying the canvas display size, + * since responsiveness is handled by the controller.resize() method. The config is used + * to determine the aspect ratio to apply in case no explicit height has been specified. + */ +function initCanvas(canvas, config) { + var style = canvas.style; + + // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it + // returns null or '' if no explicit value has been set to the canvas attribute. + var renderHeight = canvas.getAttribute('height'); + var renderWidth = canvas.getAttribute('width'); + + // Chart.js modifies some canvas values that we want to restore on destroy + canvas[EXPANDO_KEY] = { + initial: { + height: renderHeight, + width: renderWidth, + style: { + display: style.display, + height: style.height, + width: style.width + } + } + }; + + // Force canvas to display as block to avoid extra space caused by inline + // elements, which would interfere with the responsive resize process. + // https://github.com/chartjs/Chart.js/issues/2538 + style.display = style.display || 'block'; + + if (renderWidth === null || renderWidth === '') { + var displayWidth = readUsedSize(canvas, 'width'); + if (displayWidth !== undefined) { + canvas.width = displayWidth; + } + } + + if (renderHeight === null || renderHeight === '') { + if (canvas.style.height === '') { + // If no explicit render height and style height, let's apply the aspect ratio, + // which one can be specified by the user but also by charts as default option + // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. + canvas.height = canvas.width / (config.options.aspectRatio || 2); + } else { + var displayHeight = readUsedSize(canvas, 'height'); + if (displayWidth !== undefined) { + canvas.height = displayHeight; + } + } + } + + return canvas; +} + +/** + * Detects support for options object argument in addEventListener. + * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support + * @private + */ +var supportsEventListenerOptions = (function() { + var supports = false; + try { + var options = Object.defineProperty({}, 'passive', { + // eslint-disable-next-line getter-return + get: function() { + supports = true; + } + }); + window.addEventListener('e', null, options); + } catch (e) { + // continue regardless of error + } + return supports; +}()); + +// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events. +// https://github.com/chartjs/Chart.js/issues/4287 +var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; + +function addListener(node, type, listener) { + node.addEventListener(type, listener, eventListenerOptions); +} + +function removeListener(node, type, listener) { + node.removeEventListener(type, listener, eventListenerOptions); +} + +function createEvent(type, chart, x, y, nativeEvent) { + return { + type: type, + chart: chart, + native: nativeEvent || null, + x: x !== undefined ? x : null, + y: y !== undefined ? y : null, + }; +} + +function fromNativeEvent(event, chart) { + var type = EVENT_TYPES[event.type] || event.type; + var pos = helpers$1.getRelativePosition(event, chart); + return createEvent(type, chart, pos.x, pos.y, event); +} + +function throttled(fn, thisArg) { + var ticking = false; + var args = []; + + return function() { + args = Array.prototype.slice.call(arguments); + thisArg = thisArg || this; + + if (!ticking) { + ticking = true; + helpers$1.requestAnimFrame.call(window, function() { + ticking = false; + fn.apply(thisArg, args); + }); + } + }; +} + +function createDiv(cls) { + var el = document.createElement('div'); + el.className = cls || ''; + return el; +} + +// Implementation based on https://github.com/marcj/css-element-queries +function createResizer(handler) { + var maxSize = 1000000; + + // NOTE(SB) Don't use innerHTML because it could be considered unsafe. + // https://github.com/chartjs/Chart.js/issues/5902 + var resizer = createDiv(CSS_SIZE_MONITOR); + var expand = createDiv(CSS_SIZE_MONITOR + '-expand'); + var shrink = createDiv(CSS_SIZE_MONITOR + '-shrink'); + + expand.appendChild(createDiv()); + shrink.appendChild(createDiv()); + + resizer.appendChild(expand); + resizer.appendChild(shrink); + resizer._reset = function() { + expand.scrollLeft = maxSize; + expand.scrollTop = maxSize; + shrink.scrollLeft = maxSize; + shrink.scrollTop = maxSize; + }; + + var onScroll = function() { + resizer._reset(); + handler(); + }; + + addListener(expand, 'scroll', onScroll.bind(expand, 'expand')); + addListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink')); + + return resizer; +} + +// https://davidwalsh.name/detect-node-insertion +function watchForRender(node, handler) { + var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); + var proxy = expando.renderProxy = function(e) { + if (e.animationName === CSS_RENDER_ANIMATION) { + handler(); + } + }; + + helpers$1.each(ANIMATION_START_EVENTS, function(type) { + addListener(node, type, proxy); + }); + + // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class + // is removed then added back immediately (same animation frame?). Accessing the + // `offsetParent` property will force a reflow and re-evaluate the CSS animation. + // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics + // https://github.com/chartjs/Chart.js/issues/4737 + expando.reflow = !!node.offsetParent; + + node.classList.add(CSS_RENDER_MONITOR); +} + +function unwatchForRender(node) { + var expando = node[EXPANDO_KEY] || {}; + var proxy = expando.renderProxy; + + if (proxy) { + helpers$1.each(ANIMATION_START_EVENTS, function(type) { + removeListener(node, type, proxy); + }); + + delete expando.renderProxy; + } + + node.classList.remove(CSS_RENDER_MONITOR); +} + +function addResizeListener(node, listener, chart) { + var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); + + // Let's keep track of this added resizer and thus avoid DOM query when removing it. + var resizer = expando.resizer = createResizer(throttled(function() { + if (expando.resizer) { + var container = chart.options.maintainAspectRatio && node.parentNode; + var w = container ? container.clientWidth : 0; + listener(createEvent('resize', chart)); + if (container && container.clientWidth < w && chart.canvas) { + // If the container size shrank during chart resize, let's assume + // scrollbar appeared. So we resize again with the scrollbar visible - + // effectively making chart smaller and the scrollbar hidden again. + // Because we are inside `throttled`, and currently `ticking`, scroll + // events are ignored during this whole 2 resize process. + // If we assumed wrong and something else happened, we are resizing + // twice in a frame (potential performance issue) + listener(createEvent('resize', chart)); + } + } + })); + + // The resizer needs to be attached to the node parent, so we first need to be + // sure that `node` is attached to the DOM before injecting the resizer element. + watchForRender(node, function() { + if (expando.resizer) { + var container = node.parentNode; + if (container && container !== resizer.parentNode) { + container.insertBefore(resizer, container.firstChild); + } + + // The container size might have changed, let's reset the resizer state. + resizer._reset(); + } + }); +} + +function removeResizeListener(node) { + var expando = node[EXPANDO_KEY] || {}; + var resizer = expando.resizer; + + delete expando.resizer; + unwatchForRender(node); + + if (resizer && resizer.parentNode) { + resizer.parentNode.removeChild(resizer); + } +} + +function injectCSS(platform, css) { + // https://stackoverflow.com/q/3922139 + var style = platform._style || document.createElement('style'); + if (!platform._style) { + platform._style = style; + css = '/* Chart.js */\n' + css; + style.setAttribute('type', 'text/css'); + document.getElementsByTagName('head')[0].appendChild(style); + } + + style.appendChild(document.createTextNode(css)); +} + +var platform_dom$2 = { + /** + * When `true`, prevents the automatic injection of the stylesheet required to + * correctly detect when the chart is added to the DOM and then resized. This + * switch has been added to allow external stylesheet (`dist/Chart(.min)?.js`) + * to be manually imported to make this library compatible with any CSP. + * See https://github.com/chartjs/Chart.js/issues/5208 + */ + disableCSSInjection: false, + + /** + * This property holds whether this platform is enabled for the current environment. + * Currently used by platform.js to select the proper implementation. + * @private + */ + _enabled: typeof window !== 'undefined' && typeof document !== 'undefined', + + /** + * @private + */ + _ensureLoaded: function() { + if (this._loaded) { + return; + } + + this._loaded = true; + + // https://github.com/chartjs/Chart.js/issues/5208 + if (!this.disableCSSInjection) { + injectCSS(this, stylesheet); + } + }, + + acquireContext: function(item, config) { + if (typeof item === 'string') { + item = document.getElementById(item); + } else if (item.length) { + // Support for array based queries (such as jQuery) + item = item[0]; + } + + if (item && item.canvas) { + // Support for any object associated to a canvas (including a context2d) + item = item.canvas; + } + + // To prevent canvas fingerprinting, some add-ons undefine the getContext + // method, for example: https://github.com/kkapsner/CanvasBlocker + // https://github.com/chartjs/Chart.js/issues/2807 + var context = item && item.getContext && item.getContext('2d'); + + // Load platform resources on first chart creation, to make possible to change + // platform options after importing the library (e.g. `disableCSSInjection`). + this._ensureLoaded(); + + // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is + // inside an iframe or when running in a protected environment. We could guess the + // types from their toString() value but let's keep things flexible and assume it's + // a sufficient condition if the item has a context2D which has item as `canvas`. + // https://github.com/chartjs/Chart.js/issues/3887 + // https://github.com/chartjs/Chart.js/issues/4102 + // https://github.com/chartjs/Chart.js/issues/4152 + if (context && context.canvas === item) { + initCanvas(item, config); + return context; + } + + return null; + }, + + releaseContext: function(context) { + var canvas = context.canvas; + if (!canvas[EXPANDO_KEY]) { + return; + } + + var initial = canvas[EXPANDO_KEY].initial; + ['height', 'width'].forEach(function(prop) { + var value = initial[prop]; + if (helpers$1.isNullOrUndef(value)) { + canvas.removeAttribute(prop); + } else { + canvas.setAttribute(prop, value); + } + }); + + helpers$1.each(initial.style || {}, function(value, key) { + canvas.style[key] = value; + }); + + // The canvas render size might have been changed (and thus the state stack discarded), + // we can't use save() and restore() to restore the initial state. So make sure that at + // least the canvas context is reset to the default state by setting the canvas width. + // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html + // eslint-disable-next-line no-self-assign + canvas.width = canvas.width; + + delete canvas[EXPANDO_KEY]; + }, + + addEventListener: function(chart, type, listener) { + var canvas = chart.canvas; + if (type === 'resize') { + // Note: the resize event is not supported on all browsers. + addResizeListener(canvas, listener, chart); + return; + } + + var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {}); + var proxies = expando.proxies || (expando.proxies = {}); + var proxy = proxies[chart.id + '_' + type] = function(event) { + listener(fromNativeEvent(event, chart)); + }; + + addListener(canvas, type, proxy); + }, + + removeEventListener: function(chart, type, listener) { + var canvas = chart.canvas; + if (type === 'resize') { + // Note: the resize event is not supported on all browsers. + removeResizeListener(canvas); + return; + } + + var expando = listener[EXPANDO_KEY] || {}; + var proxies = expando.proxies || {}; + var proxy = proxies[chart.id + '_' + type]; + if (!proxy) { + return; + } + + removeListener(canvas, type, proxy); + } +}; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use EventTarget.addEventListener instead. + * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ + * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener + * @function Chart.helpers.addEvent + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers$1.addEvent = addListener; + +/** + * Provided for backward compatibility, use EventTarget.removeEventListener instead. + * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ + * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener + * @function Chart.helpers.removeEvent + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers$1.removeEvent = removeListener; + +// @TODO Make possible to select another platform at build time. +var implementation = platform_dom$2._enabled ? platform_dom$2 : platform_basic; + +/** + * @namespace Chart.platform + * @see https://chartjs.gitbooks.io/proposals/content/Platform.html + * @since 2.4.0 + */ +var platform = helpers$1.extend({ + /** + * @since 2.7.0 + */ + initialize: function() {}, + + /** + * Called at chart construction time, returns a context2d instance implementing + * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. + * @param {*} item - The native item from which to acquire context (platform specific) + * @param {object} options - The chart options + * @returns {CanvasRenderingContext2D} context2d instance + */ + acquireContext: function() {}, + + /** + * Called at chart destruction time, releases any resources associated to the context + * previously returned by the acquireContext() method. + * @param {CanvasRenderingContext2D} context - The context2d instance + * @returns {boolean} true if the method succeeded, else false + */ + releaseContext: function() {}, + + /** + * Registers the specified listener on the given chart. + * @param {Chart} chart - Chart from which to listen for event + * @param {string} type - The ({@link IEvent}) type to listen for + * @param {function} listener - Receives a notification (an object that implements + * the {@link IEvent} interface) when an event of the specified type occurs. + */ + addEventListener: function() {}, + + /** + * Removes the specified listener previously registered with addEventListener. + * @param {Chart} chart - Chart from which to remove the listener + * @param {string} type - The ({@link IEvent}) type to remove + * @param {function} listener - The listener function to remove from the event target. + */ + removeEventListener: function() {} + +}, implementation); + +core_defaults._set('global', { + plugins: {} +}); + +/** + * The plugin service singleton + * @namespace Chart.plugins + * @since 2.1.0 + */ +var core_plugins = { + /** + * Globally registered plugins. + * @private + */ + _plugins: [], + + /** + * This identifier is used to invalidate the descriptors cache attached to each chart + * when a global plugin is registered or unregistered. In this case, the cache ID is + * incremented and descriptors are regenerated during following API calls. + * @private + */ + _cacheId: 0, + + /** + * Registers the given plugin(s) if not already registered. + * @param {IPlugin[]|IPlugin} plugins plugin instance(s). + */ + register: function(plugins) { + var p = this._plugins; + ([]).concat(plugins).forEach(function(plugin) { + if (p.indexOf(plugin) === -1) { + p.push(plugin); + } + }); + + this._cacheId++; + }, + + /** + * Unregisters the given plugin(s) only if registered. + * @param {IPlugin[]|IPlugin} plugins plugin instance(s). + */ + unregister: function(plugins) { + var p = this._plugins; + ([]).concat(plugins).forEach(function(plugin) { + var idx = p.indexOf(plugin); + if (idx !== -1) { + p.splice(idx, 1); + } + }); + + this._cacheId++; + }, + + /** + * Remove all registered plugins. + * @since 2.1.5 + */ + clear: function() { + this._plugins = []; + this._cacheId++; + }, + + /** + * Returns the number of registered plugins? + * @returns {number} + * @since 2.1.5 + */ + count: function() { + return this._plugins.length; + }, + + /** + * Returns all registered plugin instances. + * @returns {IPlugin[]} array of plugin objects. + * @since 2.1.5 + */ + getAll: function() { + return this._plugins; + }, + + /** + * Calls enabled plugins for `chart` on the specified hook and with the given args. + * This method immediately returns as soon as a plugin explicitly returns false. The + * returned value can be used, for instance, to interrupt the current action. + * @param {Chart} chart - The chart instance for which plugins should be called. + * @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate'). + * @param {Array} [args] - Extra arguments to apply to the hook call. + * @returns {boolean} false if any of the plugins return false, else returns true. + */ + notify: function(chart, hook, args) { + var descriptors = this.descriptors(chart); + var ilen = descriptors.length; + var i, descriptor, plugin, params, method; + + for (i = 0; i < ilen; ++i) { + descriptor = descriptors[i]; + plugin = descriptor.plugin; + method = plugin[hook]; + if (typeof method === 'function') { + params = [chart].concat(args || []); + params.push(descriptor.options); + if (method.apply(plugin, params) === false) { + return false; + } + } + } + + return true; + }, + + /** + * Returns descriptors of enabled plugins for the given chart. + * @returns {object[]} [{ plugin, options }] + * @private + */ + descriptors: function(chart) { + var cache = chart.$plugins || (chart.$plugins = {}); + if (cache.id === this._cacheId) { + return cache.descriptors; + } + + var plugins = []; + var descriptors = []; + var config = (chart && chart.config) || {}; + var options = (config.options && config.options.plugins) || {}; + + this._plugins.concat(config.plugins || []).forEach(function(plugin) { + var idx = plugins.indexOf(plugin); + if (idx !== -1) { + return; + } + + var id = plugin.id; + var opts = options[id]; + if (opts === false) { + return; + } + + if (opts === true) { + opts = helpers$1.clone(core_defaults.global.plugins[id]); + } + + plugins.push(plugin); + descriptors.push({ + plugin: plugin, + options: opts || {} + }); + }); + + cache.descriptors = descriptors; + cache.id = this._cacheId; + return descriptors; + }, + + /** + * Invalidates cache for the given chart: descriptors hold a reference on plugin option, + * but in some cases, this reference can be changed by the user when updating options. + * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 + * @private + */ + _invalidate: function(chart) { + delete chart.$plugins; + } +}; + +var core_scaleService = { + // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then + // use the new chart options to grab the correct scale + constructors: {}, + // Use a registration function so that we can move to an ES6 map when we no longer need to support + // old browsers + + // Scale config defaults + defaults: {}, + registerScaleType: function(type, scaleConstructor, scaleDefaults) { + this.constructors[type] = scaleConstructor; + this.defaults[type] = helpers$1.clone(scaleDefaults); + }, + getScaleConstructor: function(type) { + return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; + }, + getScaleDefaults: function(type) { + // Return the scale defaults merged with the global settings so that we always use the latest ones + return this.defaults.hasOwnProperty(type) ? helpers$1.merge({}, [core_defaults.scale, this.defaults[type]]) : {}; + }, + updateScaleDefaults: function(type, additions) { + var me = this; + if (me.defaults.hasOwnProperty(type)) { + me.defaults[type] = helpers$1.extend(me.defaults[type], additions); + } + }, + addScalesToLayout: function(chart) { + // Adds each scale to the chart.boxes array to be sized accordingly + helpers$1.each(chart.scales, function(scale) { + // Set ILayoutItem parameters for backwards compatibility + scale.fullWidth = scale.options.fullWidth; + scale.position = scale.options.position; + scale.weight = scale.options.weight; + core_layouts.addBox(chart, scale); + }); + } +}; + +var valueOrDefault$7 = helpers$1.valueOrDefault; + +core_defaults._set('global', { + tooltips: { + enabled: true, + custom: null, + mode: 'nearest', + position: 'average', + intersect: true, + backgroundColor: 'rgba(0,0,0,0.8)', + titleFontStyle: 'bold', + titleSpacing: 2, + titleMarginBottom: 6, + titleFontColor: '#fff', + titleAlign: 'left', + bodySpacing: 2, + bodyFontColor: '#fff', + bodyAlign: 'left', + footerFontStyle: 'bold', + footerSpacing: 2, + footerMarginTop: 6, + footerFontColor: '#fff', + footerAlign: 'left', + yPadding: 6, + xPadding: 6, + caretPadding: 2, + caretSize: 5, + cornerRadius: 6, + multiKeyBackground: '#fff', + displayColors: true, + borderColor: 'rgba(0,0,0,0)', + borderWidth: 0, + callbacks: { + // Args are: (tooltipItems, data) + beforeTitle: helpers$1.noop, + title: function(tooltipItems, data) { + var title = ''; + var labels = data.labels; + var labelCount = labels ? labels.length : 0; + + if (tooltipItems.length > 0) { + var item = tooltipItems[0]; + if (item.label) { + title = item.label; + } else if (item.xLabel) { + title = item.xLabel; + } else if (labelCount > 0 && item.index < labelCount) { + title = labels[item.index]; + } + } + + return title; + }, + afterTitle: helpers$1.noop, + + // Args are: (tooltipItems, data) + beforeBody: helpers$1.noop, + + // Args are: (tooltipItem, data) + beforeLabel: helpers$1.noop, + label: function(tooltipItem, data) { + var label = data.datasets[tooltipItem.datasetIndex].label || ''; + + if (label) { + label += ': '; + } + if (!helpers$1.isNullOrUndef(tooltipItem.value)) { + label += tooltipItem.value; + } else { + label += tooltipItem.yLabel; + } + return label; + }, + labelColor: function(tooltipItem, chart) { + var meta = chart.getDatasetMeta(tooltipItem.datasetIndex); + var activeElement = meta.data[tooltipItem.index]; + var view = activeElement._view; + return { + borderColor: view.borderColor, + backgroundColor: view.backgroundColor + }; + }, + labelTextColor: function() { + return this._options.bodyFontColor; + }, + afterLabel: helpers$1.noop, + + // Args are: (tooltipItems, data) + afterBody: helpers$1.noop, + + // Args are: (tooltipItems, data) + beforeFooter: helpers$1.noop, + footer: helpers$1.noop, + afterFooter: helpers$1.noop + } + } +}); + +var positioners = { + /** + * Average mode places the tooltip at the average position of the elements shown + * @function Chart.Tooltip.positioners.average + * @param elements {ChartElement[]} the elements being displayed in the tooltip + * @returns {object} tooltip position + */ + average: function(elements) { + if (!elements.length) { + return false; + } + + var i, len; + var x = 0; + var y = 0; + var count = 0; + + for (i = 0, len = elements.length; i < len; ++i) { + var el = elements[i]; + if (el && el.hasValue()) { + var pos = el.tooltipPosition(); + x += pos.x; + y += pos.y; + ++count; + } + } + + return { + x: x / count, + y: y / count + }; + }, + + /** + * Gets the tooltip position nearest of the item nearest to the event position + * @function Chart.Tooltip.positioners.nearest + * @param elements {Chart.Element[]} the tooltip elements + * @param eventPosition {object} the position of the event in canvas coordinates + * @returns {object} the tooltip position + */ + nearest: function(elements, eventPosition) { + var x = eventPosition.x; + var y = eventPosition.y; + var minDistance = Number.POSITIVE_INFINITY; + var i, len, nearestElement; + + for (i = 0, len = elements.length; i < len; ++i) { + var el = elements[i]; + if (el && el.hasValue()) { + var center = el.getCenterPoint(); + var d = helpers$1.distanceBetweenPoints(eventPosition, center); + + if (d < minDistance) { + minDistance = d; + nearestElement = el; + } + } + } + + if (nearestElement) { + var tp = nearestElement.tooltipPosition(); + x = tp.x; + y = tp.y; + } + + return { + x: x, + y: y + }; + } +}; + +// Helper to push or concat based on if the 2nd parameter is an array or not +function pushOrConcat(base, toPush) { + if (toPush) { + if (helpers$1.isArray(toPush)) { + // base = base.concat(toPush); + Array.prototype.push.apply(base, toPush); + } else { + base.push(toPush); + } + } + + return base; +} + +/** + * Returns array of strings split by newline + * @param {string} value - The value to split by newline. + * @returns {string[]} value if newline present - Returned from String split() method + * @function + */ +function splitNewlines(str) { + if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) { + return str.split('\n'); + } + return str; +} + + +/** + * Private helper to create a tooltip item model + * @param element - the chart element (point, arc, bar) to create the tooltip item for + * @return new tooltip item + */ +function createTooltipItem(element) { + var xScale = element._xScale; + var yScale = element._yScale || element._scale; // handle radar || polarArea charts + var index = element._index; + var datasetIndex = element._datasetIndex; + var controller = element._chart.getDatasetMeta(datasetIndex).controller; + var indexScale = controller._getIndexScale(); + var valueScale = controller._getValueScale(); + + return { + xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', + yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', + label: indexScale ? '' + indexScale.getLabelForIndex(index, datasetIndex) : '', + value: valueScale ? '' + valueScale.getLabelForIndex(index, datasetIndex) : '', + index: index, + datasetIndex: datasetIndex, + x: element._model.x, + y: element._model.y + }; +} + +/** + * Helper to get the reset model for the tooltip + * @param tooltipOpts {object} the tooltip options + */ +function getBaseModel(tooltipOpts) { + var globalDefaults = core_defaults.global; + + return { + // Positioning + xPadding: tooltipOpts.xPadding, + yPadding: tooltipOpts.yPadding, + xAlign: tooltipOpts.xAlign, + yAlign: tooltipOpts.yAlign, + + // Body + bodyFontColor: tooltipOpts.bodyFontColor, + _bodyFontFamily: valueOrDefault$7(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), + _bodyFontStyle: valueOrDefault$7(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), + _bodyAlign: tooltipOpts.bodyAlign, + bodyFontSize: valueOrDefault$7(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), + bodySpacing: tooltipOpts.bodySpacing, + + // Title + titleFontColor: tooltipOpts.titleFontColor, + _titleFontFamily: valueOrDefault$7(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), + _titleFontStyle: valueOrDefault$7(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), + titleFontSize: valueOrDefault$7(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), + _titleAlign: tooltipOpts.titleAlign, + titleSpacing: tooltipOpts.titleSpacing, + titleMarginBottom: tooltipOpts.titleMarginBottom, + + // Footer + footerFontColor: tooltipOpts.footerFontColor, + _footerFontFamily: valueOrDefault$7(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), + _footerFontStyle: valueOrDefault$7(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), + footerFontSize: valueOrDefault$7(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), + _footerAlign: tooltipOpts.footerAlign, + footerSpacing: tooltipOpts.footerSpacing, + footerMarginTop: tooltipOpts.footerMarginTop, + + // Appearance + caretSize: tooltipOpts.caretSize, + cornerRadius: tooltipOpts.cornerRadius, + backgroundColor: tooltipOpts.backgroundColor, + opacity: 0, + legendColorBackground: tooltipOpts.multiKeyBackground, + displayColors: tooltipOpts.displayColors, + borderColor: tooltipOpts.borderColor, + borderWidth: tooltipOpts.borderWidth + }; +} + +/** + * Get the size of the tooltip + */ +function getTooltipSize(tooltip, model) { + var ctx = tooltip._chart.ctx; + + var height = model.yPadding * 2; // Tooltip Padding + var width = 0; + + // Count of all lines in the body + var body = model.body; + var combinedBodyLength = body.reduce(function(count, bodyItem) { + return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; + }, 0); + combinedBodyLength += model.beforeBody.length + model.afterBody.length; + + var titleLineCount = model.title.length; + var footerLineCount = model.footer.length; + var titleFontSize = model.titleFontSize; + var bodyFontSize = model.bodyFontSize; + var footerFontSize = model.footerFontSize; + + height += titleLineCount * titleFontSize; // Title Lines + height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing + height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin + height += combinedBodyLength * bodyFontSize; // Body Lines + height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing + height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin + height += footerLineCount * (footerFontSize); // Footer Lines + height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing + + // Title width + var widthPadding = 0; + var maxLineWidth = function(line) { + width = Math.max(width, ctx.measureText(line).width + widthPadding); + }; + + ctx.font = helpers$1.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily); + helpers$1.each(model.title, maxLineWidth); + + // Body width + ctx.font = helpers$1.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily); + helpers$1.each(model.beforeBody.concat(model.afterBody), maxLineWidth); + + // Body lines may include some extra width due to the color box + widthPadding = model.displayColors ? (bodyFontSize + 2) : 0; + helpers$1.each(body, function(bodyItem) { + helpers$1.each(bodyItem.before, maxLineWidth); + helpers$1.each(bodyItem.lines, maxLineWidth); + helpers$1.each(bodyItem.after, maxLineWidth); + }); + + // Reset back to 0 + widthPadding = 0; + + // Footer width + ctx.font = helpers$1.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily); + helpers$1.each(model.footer, maxLineWidth); + + // Add padding + width += 2 * model.xPadding; + + return { + width: width, + height: height + }; +} + +/** + * Helper to get the alignment of a tooltip given the size + */ +function determineAlignment(tooltip, size) { + var model = tooltip._model; + var chart = tooltip._chart; + var chartArea = tooltip._chart.chartArea; + var xAlign = 'center'; + var yAlign = 'center'; + + if (model.y < size.height) { + yAlign = 'top'; + } else if (model.y > (chart.height - size.height)) { + yAlign = 'bottom'; + } + + var lf, rf; // functions to determine left, right alignment + var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart + var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges + var midX = (chartArea.left + chartArea.right) / 2; + var midY = (chartArea.top + chartArea.bottom) / 2; + + if (yAlign === 'center') { + lf = function(x) { + return x <= midX; + }; + rf = function(x) { + return x > midX; + }; + } else { + lf = function(x) { + return x <= (size.width / 2); + }; + rf = function(x) { + return x >= (chart.width - (size.width / 2)); + }; + } + + olf = function(x) { + return x + size.width + model.caretSize + model.caretPadding > chart.width; + }; + orf = function(x) { + return x - size.width - model.caretSize - model.caretPadding < 0; + }; + yf = function(y) { + return y <= midY ? 'top' : 'bottom'; + }; + + if (lf(model.x)) { + xAlign = 'left'; + + // Is tooltip too wide and goes over the right side of the chart.? + if (olf(model.x)) { + xAlign = 'center'; + yAlign = yf(model.y); + } + } else if (rf(model.x)) { + xAlign = 'right'; + + // Is tooltip too wide and goes outside left edge of canvas? + if (orf(model.x)) { + xAlign = 'center'; + yAlign = yf(model.y); + } + } + + var opts = tooltip._options; + return { + xAlign: opts.xAlign ? opts.xAlign : xAlign, + yAlign: opts.yAlign ? opts.yAlign : yAlign + }; +} + +/** + * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment + */ +function getBackgroundPoint(vm, size, alignment, chart) { + // Background Position + var x = vm.x; + var y = vm.y; + + var caretSize = vm.caretSize; + var caretPadding = vm.caretPadding; + var cornerRadius = vm.cornerRadius; + var xAlign = alignment.xAlign; + var yAlign = alignment.yAlign; + var paddingAndSize = caretSize + caretPadding; + var radiusAndPadding = cornerRadius + caretPadding; + + if (xAlign === 'right') { + x -= size.width; + } else if (xAlign === 'center') { + x -= (size.width / 2); + if (x + size.width > chart.width) { + x = chart.width - size.width; + } + if (x < 0) { + x = 0; + } + } + + if (yAlign === 'top') { + y += paddingAndSize; + } else if (yAlign === 'bottom') { + y -= size.height + paddingAndSize; + } else { + y -= (size.height / 2); + } + + if (yAlign === 'center') { + if (xAlign === 'left') { + x += paddingAndSize; + } else if (xAlign === 'right') { + x -= paddingAndSize; + } + } else if (xAlign === 'left') { + x -= radiusAndPadding; + } else if (xAlign === 'right') { + x += radiusAndPadding; + } + + return { + x: x, + y: y + }; +} + +function getAlignedX(vm, align) { + return align === 'center' + ? vm.x + vm.width / 2 + : align === 'right' + ? vm.x + vm.width - vm.xPadding + : vm.x + vm.xPadding; +} + +/** + * Helper to build before and after body lines + */ +function getBeforeAfterBodyLines(callback) { + return pushOrConcat([], splitNewlines(callback)); +} + +var exports$3 = core_element.extend({ + initialize: function() { + this._model = getBaseModel(this._options); + this._lastActive = []; + }, + + // Get the title + // Args are: (tooltipItem, data) + getTitle: function() { + var me = this; + var opts = me._options; + var callbacks = opts.callbacks; + + var beforeTitle = callbacks.beforeTitle.apply(me, arguments); + var title = callbacks.title.apply(me, arguments); + var afterTitle = callbacks.afterTitle.apply(me, arguments); + + var lines = []; + lines = pushOrConcat(lines, splitNewlines(beforeTitle)); + lines = pushOrConcat(lines, splitNewlines(title)); + lines = pushOrConcat(lines, splitNewlines(afterTitle)); + + return lines; + }, + + // Args are: (tooltipItem, data) + getBeforeBody: function() { + return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments)); + }, + + // Args are: (tooltipItem, data) + getBody: function(tooltipItems, data) { + var me = this; + var callbacks = me._options.callbacks; + var bodyItems = []; + + helpers$1.each(tooltipItems, function(tooltipItem) { + var bodyItem = { + before: [], + lines: [], + after: [] + }; + pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data))); + pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); + pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data))); + + bodyItems.push(bodyItem); + }); + + return bodyItems; + }, + + // Args are: (tooltipItem, data) + getAfterBody: function() { + return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments)); + }, + + // Get the footer and beforeFooter and afterFooter lines + // Args are: (tooltipItem, data) + getFooter: function() { + var me = this; + var callbacks = me._options.callbacks; + + var beforeFooter = callbacks.beforeFooter.apply(me, arguments); + var footer = callbacks.footer.apply(me, arguments); + var afterFooter = callbacks.afterFooter.apply(me, arguments); + + var lines = []; + lines = pushOrConcat(lines, splitNewlines(beforeFooter)); + lines = pushOrConcat(lines, splitNewlines(footer)); + lines = pushOrConcat(lines, splitNewlines(afterFooter)); + + return lines; + }, + + update: function(changed) { + var me = this; + var opts = me._options; + + // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition + // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time + // which breaks any animations. + var existingModel = me._model; + var model = me._model = getBaseModel(opts); + var active = me._active; + + var data = me._data; + + // In the case where active.length === 0 we need to keep these at existing values for good animations + var alignment = { + xAlign: existingModel.xAlign, + yAlign: existingModel.yAlign + }; + var backgroundPoint = { + x: existingModel.x, + y: existingModel.y + }; + var tooltipSize = { + width: existingModel.width, + height: existingModel.height + }; + var tooltipPosition = { + x: existingModel.caretX, + y: existingModel.caretY + }; + + var i, len; + + if (active.length) { + model.opacity = 1; + + var labelColors = []; + var labelTextColors = []; + tooltipPosition = positioners[opts.position].call(me, active, me._eventPosition); + + var tooltipItems = []; + for (i = 0, len = active.length; i < len; ++i) { + tooltipItems.push(createTooltipItem(active[i])); + } + + // If the user provided a filter function, use it to modify the tooltip items + if (opts.filter) { + tooltipItems = tooltipItems.filter(function(a) { + return opts.filter(a, data); + }); + } + + // If the user provided a sorting function, use it to modify the tooltip items + if (opts.itemSort) { + tooltipItems = tooltipItems.sort(function(a, b) { + return opts.itemSort(a, b, data); + }); + } + + // Determine colors for boxes + helpers$1.each(tooltipItems, function(tooltipItem) { + labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart)); + labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart)); + }); + + + // Build the Text Lines + model.title = me.getTitle(tooltipItems, data); + model.beforeBody = me.getBeforeBody(tooltipItems, data); + model.body = me.getBody(tooltipItems, data); + model.afterBody = me.getAfterBody(tooltipItems, data); + model.footer = me.getFooter(tooltipItems, data); + + // Initial positioning and colors + model.x = tooltipPosition.x; + model.y = tooltipPosition.y; + model.caretPadding = opts.caretPadding; + model.labelColors = labelColors; + model.labelTextColors = labelTextColors; + + // data points + model.dataPoints = tooltipItems; + + // We need to determine alignment of the tooltip + tooltipSize = getTooltipSize(this, model); + alignment = determineAlignment(this, tooltipSize); + // Final Size and Position + backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart); + } else { + model.opacity = 0; + } + + model.xAlign = alignment.xAlign; + model.yAlign = alignment.yAlign; + model.x = backgroundPoint.x; + model.y = backgroundPoint.y; + model.width = tooltipSize.width; + model.height = tooltipSize.height; + + // Point where the caret on the tooltip points to + model.caretX = tooltipPosition.x; + model.caretY = tooltipPosition.y; + + me._model = model; + + if (changed && opts.custom) { + opts.custom.call(me, model); + } + + return me; + }, + + drawCaret: function(tooltipPoint, size) { + var ctx = this._chart.ctx; + var vm = this._view; + var caretPosition = this.getCaretPosition(tooltipPoint, size, vm); + + ctx.lineTo(caretPosition.x1, caretPosition.y1); + ctx.lineTo(caretPosition.x2, caretPosition.y2); + ctx.lineTo(caretPosition.x3, caretPosition.y3); + }, + getCaretPosition: function(tooltipPoint, size, vm) { + var x1, x2, x3, y1, y2, y3; + var caretSize = vm.caretSize; + var cornerRadius = vm.cornerRadius; + var xAlign = vm.xAlign; + var yAlign = vm.yAlign; + var ptX = tooltipPoint.x; + var ptY = tooltipPoint.y; + var width = size.width; + var height = size.height; + + if (yAlign === 'center') { + y2 = ptY + (height / 2); + + if (xAlign === 'left') { + x1 = ptX; + x2 = x1 - caretSize; + x3 = x1; + + y1 = y2 + caretSize; + y3 = y2 - caretSize; + } else { + x1 = ptX + width; + x2 = x1 + caretSize; + x3 = x1; + + y1 = y2 - caretSize; + y3 = y2 + caretSize; + } + } else { + if (xAlign === 'left') { + x2 = ptX + cornerRadius + (caretSize); + x1 = x2 - caretSize; + x3 = x2 + caretSize; + } else if (xAlign === 'right') { + x2 = ptX + width - cornerRadius - caretSize; + x1 = x2 - caretSize; + x3 = x2 + caretSize; + } else { + x2 = vm.caretX; + x1 = x2 - caretSize; + x3 = x2 + caretSize; + } + if (yAlign === 'top') { + y1 = ptY; + y2 = y1 - caretSize; + y3 = y1; + } else { + y1 = ptY + height; + y2 = y1 + caretSize; + y3 = y1; + // invert drawing order + var tmp = x3; + x3 = x1; + x1 = tmp; + } + } + return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3}; + }, + + drawTitle: function(pt, vm, ctx) { + var title = vm.title; + + if (title.length) { + pt.x = getAlignedX(vm, vm._titleAlign); + + ctx.textAlign = vm._titleAlign; + ctx.textBaseline = 'top'; + + var titleFontSize = vm.titleFontSize; + var titleSpacing = vm.titleSpacing; + + ctx.fillStyle = vm.titleFontColor; + ctx.font = helpers$1.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); + + var i, len; + for (i = 0, len = title.length; i < len; ++i) { + ctx.fillText(title[i], pt.x, pt.y); + pt.y += titleFontSize + titleSpacing; // Line Height and spacing + + if (i + 1 === title.length) { + pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing + } + } + } + }, + + drawBody: function(pt, vm, ctx) { + var bodyFontSize = vm.bodyFontSize; + var bodySpacing = vm.bodySpacing; + var bodyAlign = vm._bodyAlign; + var body = vm.body; + var drawColorBoxes = vm.displayColors; + var labelColors = vm.labelColors; + var xLinePadding = 0; + var colorX = drawColorBoxes ? getAlignedX(vm, 'left') : 0; + var textColor; + + ctx.textAlign = bodyAlign; + ctx.textBaseline = 'top'; + ctx.font = helpers$1.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); + + pt.x = getAlignedX(vm, bodyAlign); + + // Before Body + var fillLineOfText = function(line) { + ctx.fillText(line, pt.x + xLinePadding, pt.y); + pt.y += bodyFontSize + bodySpacing; + }; + + // Before body lines + ctx.fillStyle = vm.bodyFontColor; + helpers$1.each(vm.beforeBody, fillLineOfText); + + xLinePadding = drawColorBoxes && bodyAlign !== 'right' + ? bodyAlign === 'center' ? (bodyFontSize / 2 + 1) : (bodyFontSize + 2) + : 0; + + // Draw body lines now + helpers$1.each(body, function(bodyItem, i) { + textColor = vm.labelTextColors[i]; + ctx.fillStyle = textColor; + helpers$1.each(bodyItem.before, fillLineOfText); + + helpers$1.each(bodyItem.lines, function(line) { + // Draw Legend-like boxes if needed + if (drawColorBoxes) { + // Fill a white rect so that colours merge nicely if the opacity is < 1 + ctx.fillStyle = vm.legendColorBackground; + ctx.fillRect(colorX, pt.y, bodyFontSize, bodyFontSize); + + // Border + ctx.lineWidth = 1; + ctx.strokeStyle = labelColors[i].borderColor; + ctx.strokeRect(colorX, pt.y, bodyFontSize, bodyFontSize); + + // Inner square + ctx.fillStyle = labelColors[i].backgroundColor; + ctx.fillRect(colorX + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); + ctx.fillStyle = textColor; + } + + fillLineOfText(line); + }); + + helpers$1.each(bodyItem.after, fillLineOfText); + }); + + // Reset back to 0 for after body + xLinePadding = 0; + + // After body lines + helpers$1.each(vm.afterBody, fillLineOfText); + pt.y -= bodySpacing; // Remove last body spacing + }, + + drawFooter: function(pt, vm, ctx) { + var footer = vm.footer; + + if (footer.length) { + pt.x = getAlignedX(vm, vm._footerAlign); + pt.y += vm.footerMarginTop; + + ctx.textAlign = vm._footerAlign; + ctx.textBaseline = 'top'; + + ctx.fillStyle = vm.footerFontColor; + ctx.font = helpers$1.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily); + + helpers$1.each(footer, function(line) { + ctx.fillText(line, pt.x, pt.y); + pt.y += vm.footerFontSize + vm.footerSpacing; + }); + } + }, + + drawBackground: function(pt, vm, ctx, tooltipSize) { + ctx.fillStyle = vm.backgroundColor; + ctx.strokeStyle = vm.borderColor; + ctx.lineWidth = vm.borderWidth; + var xAlign = vm.xAlign; + var yAlign = vm.yAlign; + var x = pt.x; + var y = pt.y; + var width = tooltipSize.width; + var height = tooltipSize.height; + var radius = vm.cornerRadius; + + ctx.beginPath(); + ctx.moveTo(x + radius, y); + if (yAlign === 'top') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + if (yAlign === 'center' && xAlign === 'right') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + if (yAlign === 'bottom') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + if (yAlign === 'center' && xAlign === 'left') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); + + ctx.fill(); + + if (vm.borderWidth > 0) { + ctx.stroke(); + } + }, + + draw: function() { + var ctx = this._chart.ctx; + var vm = this._view; + + if (vm.opacity === 0) { + return; + } + + var tooltipSize = { + width: vm.width, + height: vm.height + }; + var pt = { + x: vm.x, + y: vm.y + }; + + // IE11/Edge does not like very small opacities, so snap to 0 + var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; + + // Truthy/falsey value for empty tooltip + var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length; + + if (this._options.enabled && hasTooltipContent) { + ctx.save(); + ctx.globalAlpha = opacity; + + // Draw Background + this.drawBackground(pt, vm, ctx, tooltipSize); + + // Draw Title, Body, and Footer + pt.y += vm.yPadding; + + // Titles + this.drawTitle(pt, vm, ctx); + + // Body + this.drawBody(pt, vm, ctx); + + // Footer + this.drawFooter(pt, vm, ctx); + + ctx.restore(); + } + }, + + /** + * Handle an event + * @private + * @param {IEvent} event - The event to handle + * @returns {boolean} true if the tooltip changed + */ + handleEvent: function(e) { + var me = this; + var options = me._options; + var changed = false; + + me._lastActive = me._lastActive || []; + + // Find Active Elements for tooltips + if (e.type === 'mouseout') { + me._active = []; + } else { + me._active = me._chart.getElementsAtEventForMode(e, options.mode, options); + } + + // Remember Last Actives + changed = !helpers$1.arrayEquals(me._active, me._lastActive); + + // Only handle target event on tooltip change + if (changed) { + me._lastActive = me._active; + + if (options.enabled || options.custom) { + me._eventPosition = { + x: e.x, + y: e.y + }; + + me.update(true); + me.pivot(); + } + } + + return changed; + } +}); + +/** + * @namespace Chart.Tooltip.positioners + */ +var positioners_1 = positioners; + +var core_tooltip = exports$3; +core_tooltip.positioners = positioners_1; + +var valueOrDefault$8 = helpers$1.valueOrDefault; + +core_defaults._set('global', { + elements: {}, + events: [ + 'mousemove', + 'mouseout', + 'click', + 'touchstart', + 'touchmove' + ], + hover: { + onHover: null, + mode: 'nearest', + intersect: true, + animationDuration: 400 + }, + onClick: null, + maintainAspectRatio: true, + responsive: true, + responsiveAnimationDuration: 0 +}); + +/** + * Recursively merge the given config objects representing the `scales` option + * by incorporating scale defaults in `xAxes` and `yAxes` array items, then + * returns a deep copy of the result, thus doesn't alter inputs. + */ +function mergeScaleConfig(/* config objects ... */) { + return helpers$1.merge({}, [].slice.call(arguments), { + merger: function(key, target, source, options) { + if (key === 'xAxes' || key === 'yAxes') { + var slen = source[key].length; + var i, type, scale; + + if (!target[key]) { + target[key] = []; + } + + for (i = 0; i < slen; ++i) { + scale = source[key][i]; + type = valueOrDefault$8(scale.type, key === 'xAxes' ? 'category' : 'linear'); + + if (i >= target[key].length) { + target[key].push({}); + } + + if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) { + // new/untyped scale or type changed: let's apply the new defaults + // then merge source scale to correctly overwrite the defaults. + helpers$1.merge(target[key][i], [core_scaleService.getScaleDefaults(type), scale]); + } else { + // scales type are the same + helpers$1.merge(target[key][i], scale); + } + } + } else { + helpers$1._merger(key, target, source, options); + } + } + }); +} + +/** + * Recursively merge the given config objects as the root options by handling + * default scale options for the `scales` and `scale` properties, then returns + * a deep copy of the result, thus doesn't alter inputs. + */ +function mergeConfig(/* config objects ... */) { + return helpers$1.merge({}, [].slice.call(arguments), { + merger: function(key, target, source, options) { + var tval = target[key] || {}; + var sval = source[key]; + + if (key === 'scales') { + // scale config merging is complex. Add our own function here for that + target[key] = mergeScaleConfig(tval, sval); + } else if (key === 'scale') { + // used in polar area & radar charts since there is only one scale + target[key] = helpers$1.merge(tval, [core_scaleService.getScaleDefaults(sval.type), sval]); + } else { + helpers$1._merger(key, target, source, options); + } + } + }); +} + +function initConfig(config) { + config = config || {}; + + // Do NOT use mergeConfig for the data object because this method merges arrays + // and so would change references to labels and datasets, preventing data updates. + var data = config.data = config.data || {}; + data.datasets = data.datasets || []; + data.labels = data.labels || []; + + config.options = mergeConfig( + core_defaults.global, + core_defaults[config.type], + config.options || {}); + + return config; +} + +function updateConfig(chart) { + var newOptions = chart.options; + + helpers$1.each(chart.scales, function(scale) { + core_layouts.removeBox(chart, scale); + }); + + newOptions = mergeConfig( + core_defaults.global, + core_defaults[chart.config.type], + newOptions); + + chart.options = chart.config.options = newOptions; + chart.ensureScalesHaveIDs(); + chart.buildOrUpdateScales(); + + // Tooltip + chart.tooltip._options = newOptions.tooltips; + chart.tooltip.initialize(); +} + +function positionIsHorizontal(position) { + return position === 'top' || position === 'bottom'; +} + +var Chart = function(item, config) { + this.construct(item, config); + return this; +}; + +helpers$1.extend(Chart.prototype, /** @lends Chart */ { + /** + * @private + */ + construct: function(item, config) { + var me = this; + + config = initConfig(config); + + var context = platform.acquireContext(item, config); + var canvas = context && context.canvas; + var height = canvas && canvas.height; + var width = canvas && canvas.width; + + me.id = helpers$1.uid(); + me.ctx = context; + me.canvas = canvas; + me.config = config; + me.width = width; + me.height = height; + me.aspectRatio = height ? width / height : null; + me.options = config.options; + me._bufferedRender = false; + + /** + * Provided for backward compatibility, Chart and Chart.Controller have been merged, + * the "instance" still need to be defined since it might be called from plugins. + * @prop Chart#chart + * @deprecated since version 2.6.0 + * @todo remove at version 3 + * @private + */ + me.chart = me; + me.controller = me; // chart.chart.controller #inception + + // Add the chart instance to the global namespace + Chart.instances[me.id] = me; + + // Define alias to the config data: `chart.data === chart.config.data` + Object.defineProperty(me, 'data', { + get: function() { + return me.config.data; + }, + set: function(value) { + me.config.data = value; + } + }); + + if (!context || !canvas) { + // The given item is not a compatible context2d element, let's return before finalizing + // the chart initialization but after setting basic chart / controller properties that + // can help to figure out that the chart is not valid (e.g chart.canvas !== null); + // https://github.com/chartjs/Chart.js/issues/2807 + console.error("Failed to create chart: can't acquire context from the given item"); + return; + } + + me.initialize(); + me.update(); + }, + + /** + * @private + */ + initialize: function() { + var me = this; + + // Before init plugin notification + core_plugins.notify(me, 'beforeInit'); + + helpers$1.retinaScale(me, me.options.devicePixelRatio); + + me.bindEvents(); + + if (me.options.responsive) { + // Initial resize before chart draws (must be silent to preserve initial animations). + me.resize(true); + } + + // Make sure scales have IDs and are built before we build any controllers. + me.ensureScalesHaveIDs(); + me.buildOrUpdateScales(); + me.initToolTip(); + + // After init plugin notification + core_plugins.notify(me, 'afterInit'); + + return me; + }, + + clear: function() { + helpers$1.canvas.clear(this); + return this; + }, + + stop: function() { + // Stops any current animation loop occurring + core_animations.cancelAnimation(this); + return this; + }, + + resize: function(silent) { + var me = this; + var options = me.options; + var canvas = me.canvas; + var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; + + // the canvas render width and height will be casted to integers so make sure that + // the canvas display style uses the same integer values to avoid blurring effect. + + // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed + var newWidth = Math.max(0, Math.floor(helpers$1.getMaximumWidth(canvas))); + var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers$1.getMaximumHeight(canvas))); + + if (me.width === newWidth && me.height === newHeight) { + return; + } + + canvas.width = me.width = newWidth; + canvas.height = me.height = newHeight; + canvas.style.width = newWidth + 'px'; + canvas.style.height = newHeight + 'px'; + + helpers$1.retinaScale(me, options.devicePixelRatio); + + if (!silent) { + // Notify any plugins about the resize + var newSize = {width: newWidth, height: newHeight}; + core_plugins.notify(me, 'resize', [newSize]); + + // Notify of resize + if (options.onResize) { + options.onResize(me, newSize); + } + + me.stop(); + me.update({ + duration: options.responsiveAnimationDuration + }); + } + }, + + ensureScalesHaveIDs: function() { + var options = this.options; + var scalesOptions = options.scales || {}; + var scaleOptions = options.scale; + + helpers$1.each(scalesOptions.xAxes, function(xAxisOptions, index) { + xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); + }); + + helpers$1.each(scalesOptions.yAxes, function(yAxisOptions, index) { + yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); + }); + + if (scaleOptions) { + scaleOptions.id = scaleOptions.id || 'scale'; + } + }, + + /** + * Builds a map of scale ID to scale object for future lookup. + */ + buildOrUpdateScales: function() { + var me = this; + var options = me.options; + var scales = me.scales || {}; + var items = []; + var updated = Object.keys(scales).reduce(function(obj, id) { + obj[id] = false; + return obj; + }, {}); + + if (options.scales) { + items = items.concat( + (options.scales.xAxes || []).map(function(xAxisOptions) { + return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; + }), + (options.scales.yAxes || []).map(function(yAxisOptions) { + return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; + }) + ); + } + + if (options.scale) { + items.push({ + options: options.scale, + dtype: 'radialLinear', + isDefault: true, + dposition: 'chartArea' + }); + } + + helpers$1.each(items, function(item) { + var scaleOptions = item.options; + var id = scaleOptions.id; + var scaleType = valueOrDefault$8(scaleOptions.type, item.dtype); + + if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { + scaleOptions.position = item.dposition; + } + + updated[id] = true; + var scale = null; + if (id in scales && scales[id].type === scaleType) { + scale = scales[id]; + scale.options = scaleOptions; + scale.ctx = me.ctx; + scale.chart = me; + } else { + var scaleClass = core_scaleService.getScaleConstructor(scaleType); + if (!scaleClass) { + return; + } + scale = new scaleClass({ + id: id, + type: scaleType, + options: scaleOptions, + ctx: me.ctx, + chart: me + }); + scales[scale.id] = scale; + } + + scale.mergeTicksOptions(); + + // TODO(SB): I think we should be able to remove this custom case (options.scale) + // and consider it as a regular scale part of the "scales"" map only! This would + // make the logic easier and remove some useless? custom code. + if (item.isDefault) { + me.scale = scale; + } + }); + // clear up discarded scales + helpers$1.each(updated, function(hasUpdated, id) { + if (!hasUpdated) { + delete scales[id]; + } + }); + + me.scales = scales; + + core_scaleService.addScalesToLayout(this); + }, + + buildOrUpdateControllers: function() { + var me = this; + var newControllers = []; + + helpers$1.each(me.data.datasets, function(dataset, datasetIndex) { + var meta = me.getDatasetMeta(datasetIndex); + var type = dataset.type || me.config.type; + + if (meta.type && meta.type !== type) { + me.destroyDatasetMeta(datasetIndex); + meta = me.getDatasetMeta(datasetIndex); + } + meta.type = type; + + if (meta.controller) { + meta.controller.updateIndex(datasetIndex); + meta.controller.linkScales(); + } else { + var ControllerClass = controllers[meta.type]; + if (ControllerClass === undefined) { + throw new Error('"' + meta.type + '" is not a chart type.'); + } + + meta.controller = new ControllerClass(me, datasetIndex); + newControllers.push(meta.controller); + } + }, me); + + return newControllers; + }, + + /** + * Reset the elements of all datasets + * @private + */ + resetElements: function() { + var me = this; + helpers$1.each(me.data.datasets, function(dataset, datasetIndex) { + me.getDatasetMeta(datasetIndex).controller.reset(); + }, me); + }, + + /** + * Resets the chart back to it's state before the initial animation + */ + reset: function() { + this.resetElements(); + this.tooltip.initialize(); + }, + + update: function(config) { + var me = this; + + if (!config || typeof config !== 'object') { + // backwards compatibility + config = { + duration: config, + lazy: arguments[1] + }; + } + + updateConfig(me); + + // plugins options references might have change, let's invalidate the cache + // https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 + core_plugins._invalidate(me); + + if (core_plugins.notify(me, 'beforeUpdate') === false) { + return; + } + + // In case the entire data object changed + me.tooltip._data = me.data; + + // Make sure dataset controllers are updated and new controllers are reset + var newControllers = me.buildOrUpdateControllers(); + + // Make sure all dataset controllers have correct meta data counts + helpers$1.each(me.data.datasets, function(dataset, datasetIndex) { + me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); + }, me); + + me.updateLayout(); + + // Can only reset the new controllers after the scales have been updated + if (me.options.animation && me.options.animation.duration) { + helpers$1.each(newControllers, function(controller) { + controller.reset(); + }); + } + + me.updateDatasets(); + + // Need to reset tooltip in case it is displayed with elements that are removed + // after update. + me.tooltip.initialize(); + + // Last active contains items that were previously in the tooltip. + // When we reset the tooltip, we need to clear it + me.lastActive = []; + + // Do this before render so that any plugins that need final scale updates can use it + core_plugins.notify(me, 'afterUpdate'); + + if (me._bufferedRender) { + me._bufferedRequest = { + duration: config.duration, + easing: config.easing, + lazy: config.lazy + }; + } else { + me.render(config); + } + }, + + /** + * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` + * hook, in which case, plugins will not be called on `afterLayout`. + * @private + */ + updateLayout: function() { + var me = this; + + if (core_plugins.notify(me, 'beforeLayout') === false) { + return; + } + + core_layouts.update(this, this.width, this.height); + + /** + * Provided for backward compatibility, use `afterLayout` instead. + * @method IPlugin#afterScaleUpdate + * @deprecated since version 2.5.0 + * @todo remove at version 3 + * @private + */ + core_plugins.notify(me, 'afterScaleUpdate'); + core_plugins.notify(me, 'afterLayout'); + }, + + /** + * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` + * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. + * @private + */ + updateDatasets: function() { + var me = this; + + if (core_plugins.notify(me, 'beforeDatasetsUpdate') === false) { + return; + } + + for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { + me.updateDataset(i); + } + + core_plugins.notify(me, 'afterDatasetsUpdate'); + }, + + /** + * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` + * hook, in which case, plugins will not be called on `afterDatasetUpdate`. + * @private + */ + updateDataset: function(index) { + var me = this; + var meta = me.getDatasetMeta(index); + var args = { + meta: meta, + index: index + }; + + if (core_plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { + return; + } + + meta.controller.update(); + + core_plugins.notify(me, 'afterDatasetUpdate', [args]); + }, + + render: function(config) { + var me = this; + + if (!config || typeof config !== 'object') { + // backwards compatibility + config = { + duration: config, + lazy: arguments[1] + }; + } + + var animationOptions = me.options.animation; + var duration = valueOrDefault$8(config.duration, animationOptions && animationOptions.duration); + var lazy = config.lazy; + + if (core_plugins.notify(me, 'beforeRender') === false) { + return; + } + + var onComplete = function(animation) { + core_plugins.notify(me, 'afterRender'); + helpers$1.callback(animationOptions && animationOptions.onComplete, [animation], me); + }; + + if (animationOptions && duration) { + var animation = new core_animation({ + numSteps: duration / 16.66, // 60 fps + easing: config.easing || animationOptions.easing, + + render: function(chart, animationObject) { + var easingFunction = helpers$1.easing.effects[animationObject.easing]; + var currentStep = animationObject.currentStep; + var stepDecimal = currentStep / animationObject.numSteps; + + chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); + }, + + onAnimationProgress: animationOptions.onProgress, + onAnimationComplete: onComplete + }); + + core_animations.addAnimation(me, animation, duration, lazy); + } else { + me.draw(); + + // See https://github.com/chartjs/Chart.js/issues/3781 + onComplete(new core_animation({numSteps: 0, chart: me})); + } + + return me; + }, + + draw: function(easingValue) { + var me = this; + + me.clear(); + + if (helpers$1.isNullOrUndef(easingValue)) { + easingValue = 1; + } + + me.transition(easingValue); + + if (me.width <= 0 || me.height <= 0) { + return; + } + + if (core_plugins.notify(me, 'beforeDraw', [easingValue]) === false) { + return; + } + + // Draw all the scales + helpers$1.each(me.boxes, function(box) { + box.draw(me.chartArea); + }, me); + + me.drawDatasets(easingValue); + me._drawTooltip(easingValue); + + core_plugins.notify(me, 'afterDraw', [easingValue]); + }, + + /** + * @private + */ + transition: function(easingValue) { + var me = this; + + for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) { + if (me.isDatasetVisible(i)) { + me.getDatasetMeta(i).controller.transition(easingValue); + } + } + + me.tooltip.transition(easingValue); + }, + + /** + * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` + * hook, in which case, plugins will not be called on `afterDatasetsDraw`. + * @private + */ + drawDatasets: function(easingValue) { + var me = this; + + if (core_plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { + return; + } + + // Draw datasets reversed to support proper line stacking + for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) { + if (me.isDatasetVisible(i)) { + me.drawDataset(i, easingValue); + } + } + + core_plugins.notify(me, 'afterDatasetsDraw', [easingValue]); + }, + + /** + * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` + * hook, in which case, plugins will not be called on `afterDatasetDraw`. + * @private + */ + drawDataset: function(index, easingValue) { + var me = this; + var meta = me.getDatasetMeta(index); + var args = { + meta: meta, + index: index, + easingValue: easingValue + }; + + if (core_plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { + return; + } + + meta.controller.draw(easingValue); + + core_plugins.notify(me, 'afterDatasetDraw', [args]); + }, + + /** + * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw` + * hook, in which case, plugins will not be called on `afterTooltipDraw`. + * @private + */ + _drawTooltip: function(easingValue) { + var me = this; + var tooltip = me.tooltip; + var args = { + tooltip: tooltip, + easingValue: easingValue + }; + + if (core_plugins.notify(me, 'beforeTooltipDraw', [args]) === false) { + return; + } + + tooltip.draw(); + + core_plugins.notify(me, 'afterTooltipDraw', [args]); + }, + + /** + * Get the single element that was clicked on + * @return An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw + */ + getElementAtEvent: function(e) { + return core_interaction.modes.single(this, e); + }, + + getElementsAtEvent: function(e) { + return core_interaction.modes.label(this, e, {intersect: true}); + }, + + getElementsAtXAxis: function(e) { + return core_interaction.modes['x-axis'](this, e, {intersect: true}); + }, + + getElementsAtEventForMode: function(e, mode, options) { + var method = core_interaction.modes[mode]; + if (typeof method === 'function') { + return method(this, e, options); + } + + return []; + }, + + getDatasetAtEvent: function(e) { + return core_interaction.modes.dataset(this, e, {intersect: true}); + }, + + getDatasetMeta: function(datasetIndex) { + var me = this; + var dataset = me.data.datasets[datasetIndex]; + if (!dataset._meta) { + dataset._meta = {}; + } + + var meta = dataset._meta[me.id]; + if (!meta) { + meta = dataset._meta[me.id] = { + type: null, + data: [], + dataset: null, + controller: null, + hidden: null, // See isDatasetVisible() comment + xAxisID: null, + yAxisID: null + }; + } + + return meta; + }, + + getVisibleDatasetCount: function() { + var count = 0; + for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { + if (this.isDatasetVisible(i)) { + count++; + } + } + return count; + }, + + isDatasetVisible: function(datasetIndex) { + var meta = this.getDatasetMeta(datasetIndex); + + // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, + // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. + return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden; + }, + + generateLegend: function() { + return this.options.legendCallback(this); + }, + + /** + * @private + */ + destroyDatasetMeta: function(datasetIndex) { + var id = this.id; + var dataset = this.data.datasets[datasetIndex]; + var meta = dataset._meta && dataset._meta[id]; + + if (meta) { + meta.controller.destroy(); + delete dataset._meta[id]; + } + }, + + destroy: function() { + var me = this; + var canvas = me.canvas; + var i, ilen; + + me.stop(); + + // dataset controllers need to cleanup associated data + for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { + me.destroyDatasetMeta(i); + } + + if (canvas) { + me.unbindEvents(); + helpers$1.canvas.clear(me); + platform.releaseContext(me.ctx); + me.canvas = null; + me.ctx = null; + } + + core_plugins.notify(me, 'destroy'); + + delete Chart.instances[me.id]; + }, + + toBase64Image: function() { + return this.canvas.toDataURL.apply(this.canvas, arguments); + }, + + initToolTip: function() { + var me = this; + me.tooltip = new core_tooltip({ + _chart: me, + _chartInstance: me, // deprecated, backward compatibility + _data: me.data, + _options: me.options.tooltips + }, me); + }, + + /** + * @private + */ + bindEvents: function() { + var me = this; + var listeners = me._listeners = {}; + var listener = function() { + me.eventHandler.apply(me, arguments); + }; + + helpers$1.each(me.options.events, function(type) { + platform.addEventListener(me, type, listener); + listeners[type] = listener; + }); + + // Elements used to detect size change should not be injected for non responsive charts. + // See https://github.com/chartjs/Chart.js/issues/2210 + if (me.options.responsive) { + listener = function() { + me.resize(); + }; + + platform.addEventListener(me, 'resize', listener); + listeners.resize = listener; + } + }, + + /** + * @private + */ + unbindEvents: function() { + var me = this; + var listeners = me._listeners; + if (!listeners) { + return; + } + + delete me._listeners; + helpers$1.each(listeners, function(listener, type) { + platform.removeEventListener(me, type, listener); + }); + }, + + updateHoverStyle: function(elements, mode, enabled) { + var method = enabled ? 'setHoverStyle' : 'removeHoverStyle'; + var element, i, ilen; + + for (i = 0, ilen = elements.length; i < ilen; ++i) { + element = elements[i]; + if (element) { + this.getDatasetMeta(element._datasetIndex).controller[method](element); + } + } + }, + + /** + * @private + */ + eventHandler: function(e) { + var me = this; + var tooltip = me.tooltip; + + if (core_plugins.notify(me, 'beforeEvent', [e]) === false) { + return; + } + + // Buffer any update calls so that renders do not occur + me._bufferedRender = true; + me._bufferedRequest = null; + + var changed = me.handleEvent(e); + // for smooth tooltip animations issue #4989 + // the tooltip should be the source of change + // Animation check workaround: + // tooltip._start will be null when tooltip isn't animating + if (tooltip) { + changed = tooltip._start + ? tooltip.handleEvent(e) + : changed | tooltip.handleEvent(e); + } + + core_plugins.notify(me, 'afterEvent', [e]); + + var bufferedRequest = me._bufferedRequest; + if (bufferedRequest) { + // If we have an update that was triggered, we need to do a normal render + me.render(bufferedRequest); + } else if (changed && !me.animating) { + // If entering, leaving, or changing elements, animate the change via pivot + me.stop(); + + // We only need to render at this point. Updating will cause scales to be + // recomputed generating flicker & using more memory than necessary. + me.render({ + duration: me.options.hover.animationDuration, + lazy: true + }); + } + + me._bufferedRender = false; + me._bufferedRequest = null; + + return me; + }, + + /** + * Handle an event + * @private + * @param {IEvent} event the event to handle + * @return {boolean} true if the chart needs to re-render + */ + handleEvent: function(e) { + var me = this; + var options = me.options || {}; + var hoverOptions = options.hover; + var changed = false; + + me.lastActive = me.lastActive || []; + + // Find Active Elements for hover and tooltips + if (e.type === 'mouseout') { + me.active = []; + } else { + me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); + } + + // Invoke onHover hook + // Need to call with native event here to not break backwards compatibility + helpers$1.callback(options.onHover || options.hover.onHover, [e.native, me.active], me); + + if (e.type === 'mouseup' || e.type === 'click') { + if (options.onClick) { + // Use e.native here for backwards compatibility + options.onClick.call(me, e.native, me.active); + } + } + + // Remove styling for last active (even if it may still be active) + if (me.lastActive.length) { + me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); + } + + // Built in hover styling + if (me.active.length && hoverOptions.mode) { + me.updateHoverStyle(me.active, hoverOptions.mode, true); + } + + changed = !helpers$1.arrayEquals(me.active, me.lastActive); + + // Remember Last Actives + me.lastActive = me.active; + + return changed; + } +}); + +/** + * NOTE(SB) We actually don't use this container anymore but we need to keep it + * for backward compatibility. Though, it can still be useful for plugins that + * would need to work on multiple charts?! + */ +Chart.instances = {}; + +var core_controller = Chart; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart instead. + * @class Chart.Controller + * @deprecated since version 2.6 + * @todo remove at version 3 + * @private + */ +Chart.Controller = Chart; + +/** + * Provided for backward compatibility, not available anymore. + * @namespace Chart + * @deprecated since version 2.8 + * @todo remove at version 3 + * @private + */ +Chart.types = {}; + +/** + * Provided for backward compatibility, not available anymore. + * @namespace Chart.helpers.configMerge + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ +helpers$1.configMerge = mergeConfig; + +/** + * Provided for backward compatibility, not available anymore. + * @namespace Chart.helpers.scaleMerge + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ +helpers$1.scaleMerge = mergeScaleConfig; + +var core_helpers = function() { + + // -- Basic js utility methods + + helpers$1.where = function(collection, filterCallback) { + if (helpers$1.isArray(collection) && Array.prototype.filter) { + return collection.filter(filterCallback); + } + var filtered = []; + + helpers$1.each(collection, function(item) { + if (filterCallback(item)) { + filtered.push(item); + } + }); + + return filtered; + }; + helpers$1.findIndex = Array.prototype.findIndex ? + function(array, callback, scope) { + return array.findIndex(callback, scope); + } : + function(array, callback, scope) { + scope = scope === undefined ? array : scope; + for (var i = 0, ilen = array.length; i < ilen; ++i) { + if (callback.call(scope, array[i], i, array)) { + return i; + } + } + return -1; + }; + helpers$1.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { + // Default to start of the array + if (helpers$1.isNullOrUndef(startIndex)) { + startIndex = -1; + } + for (var i = startIndex + 1; i < arrayToSearch.length; i++) { + var currentItem = arrayToSearch[i]; + if (filterCallback(currentItem)) { + return currentItem; + } + } + }; + helpers$1.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { + // Default to end of the array + if (helpers$1.isNullOrUndef(startIndex)) { + startIndex = arrayToSearch.length; + } + for (var i = startIndex - 1; i >= 0; i--) { + var currentItem = arrayToSearch[i]; + if (filterCallback(currentItem)) { + return currentItem; + } + } + }; + + // -- Math methods + helpers$1.isNumber = function(n) { + return !isNaN(parseFloat(n)) && isFinite(n); + }; + helpers$1.almostEquals = function(x, y, epsilon) { + return Math.abs(x - y) < epsilon; + }; + helpers$1.almostWhole = function(x, epsilon) { + var rounded = Math.round(x); + return (((rounded - epsilon) < x) && ((rounded + epsilon) > x)); + }; + helpers$1.max = function(array) { + return array.reduce(function(max, value) { + if (!isNaN(value)) { + return Math.max(max, value); + } + return max; + }, Number.NEGATIVE_INFINITY); + }; + helpers$1.min = function(array) { + return array.reduce(function(min, value) { + if (!isNaN(value)) { + return Math.min(min, value); + } + return min; + }, Number.POSITIVE_INFINITY); + }; + helpers$1.sign = Math.sign ? + function(x) { + return Math.sign(x); + } : + function(x) { + x = +x; // convert to a number + if (x === 0 || isNaN(x)) { + return x; + } + return x > 0 ? 1 : -1; + }; + helpers$1.log10 = Math.log10 ? + function(x) { + return Math.log10(x); + } : + function(x) { + var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10. + // Check for whole powers of 10, + // which due to floating point rounding error should be corrected. + var powerOf10 = Math.round(exponent); + var isPowerOf10 = x === Math.pow(10, powerOf10); + + return isPowerOf10 ? powerOf10 : exponent; + }; + helpers$1.toRadians = function(degrees) { + return degrees * (Math.PI / 180); + }; + helpers$1.toDegrees = function(radians) { + return radians * (180 / Math.PI); + }; + + /** + * Returns the number of decimal places + * i.e. the number of digits after the decimal point, of the value of this Number. + * @param {number} x - A number. + * @returns {number} The number of decimal places. + * @private + */ + helpers$1._decimalPlaces = function(x) { + if (!helpers$1.isFinite(x)) { + return; + } + var e = 1; + var p = 0; + while (Math.round(x * e) / e !== x) { + e *= 10; + p++; + } + return p; + }; + + // Gets the angle from vertical upright to the point about a centre. + helpers$1.getAngleFromPoint = function(centrePoint, anglePoint) { + var distanceFromXCenter = anglePoint.x - centrePoint.x; + var distanceFromYCenter = anglePoint.y - centrePoint.y; + var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); + + var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); + + if (angle < (-0.5 * Math.PI)) { + angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] + } + + return { + angle: angle, + distance: radialDistanceFromCenter + }; + }; + helpers$1.distanceBetweenPoints = function(pt1, pt2) { + return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); + }; + + /** + * Provided for backward compatibility, not available anymore + * @function Chart.helpers.aliasPixel + * @deprecated since version 2.8.0 + * @todo remove at version 3 + */ + helpers$1.aliasPixel = function(pixelWidth) { + return (pixelWidth % 2 === 0) ? 0 : 0.5; + }; + + /** + * Returns the aligned pixel value to avoid anti-aliasing blur + * @param {Chart} chart - The chart instance. + * @param {number} pixel - A pixel value. + * @param {number} width - The width of the element. + * @returns {number} The aligned pixel value. + * @private + */ + helpers$1._alignPixel = function(chart, pixel, width) { + var devicePixelRatio = chart.currentDevicePixelRatio; + var halfWidth = width / 2; + return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth; + }; + + helpers$1.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { + // Props to Rob Spencer at scaled innovation for his post on splining between points + // http://scaledinnovation.com/analytics/splines/aboutSplines.html + + // This function must also respect "skipped" points + + var previous = firstPoint.skip ? middlePoint : firstPoint; + var current = middlePoint; + var next = afterPoint.skip ? middlePoint : afterPoint; + + var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); + var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); + + var s01 = d01 / (d01 + d12); + var s12 = d12 / (d01 + d12); + + // If all points are the same, s01 & s02 will be inf + s01 = isNaN(s01) ? 0 : s01; + s12 = isNaN(s12) ? 0 : s12; + + var fa = t * s01; // scaling factor for triangle Ta + var fb = t * s12; + + return { + previous: { + x: current.x - fa * (next.x - previous.x), + y: current.y - fa * (next.y - previous.y) + }, + next: { + x: current.x + fb * (next.x - previous.x), + y: current.y + fb * (next.y - previous.y) + } + }; + }; + helpers$1.EPSILON = Number.EPSILON || 1e-14; + helpers$1.splineCurveMonotone = function(points) { + // This function calculates Bézier control points in a similar way than |splineCurve|, + // but preserves monotonicity of the provided data and ensures no local extremums are added + // between the dataset discrete points due to the interpolation. + // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation + + var pointsWithTangents = (points || []).map(function(point) { + return { + model: point._model, + deltaK: 0, + mK: 0 + }; + }); + + // Calculate slopes (deltaK) and initialize tangents (mK) + var pointsLen = pointsWithTangents.length; + var i, pointBefore, pointCurrent, pointAfter; + for (i = 0; i < pointsLen; ++i) { + pointCurrent = pointsWithTangents[i]; + if (pointCurrent.model.skip) { + continue; + } + + pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; + pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; + if (pointAfter && !pointAfter.model.skip) { + var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x); + + // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 + pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0; + } + + if (!pointBefore || pointBefore.model.skip) { + pointCurrent.mK = pointCurrent.deltaK; + } else if (!pointAfter || pointAfter.model.skip) { + pointCurrent.mK = pointBefore.deltaK; + } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) { + pointCurrent.mK = 0; + } else { + pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; + } + } + + // Adjust tangents to ensure monotonic properties + var alphaK, betaK, tauK, squaredMagnitude; + for (i = 0; i < pointsLen - 1; ++i) { + pointCurrent = pointsWithTangents[i]; + pointAfter = pointsWithTangents[i + 1]; + if (pointCurrent.model.skip || pointAfter.model.skip) { + continue; + } + + if (helpers$1.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) { + pointCurrent.mK = pointAfter.mK = 0; + continue; + } + + alphaK = pointCurrent.mK / pointCurrent.deltaK; + betaK = pointAfter.mK / pointCurrent.deltaK; + squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); + if (squaredMagnitude <= 9) { + continue; + } + + tauK = 3 / Math.sqrt(squaredMagnitude); + pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; + pointAfter.mK = betaK * tauK * pointCurrent.deltaK; + } + + // Compute control points + var deltaX; + for (i = 0; i < pointsLen; ++i) { + pointCurrent = pointsWithTangents[i]; + if (pointCurrent.model.skip) { + continue; + } + + pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; + pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; + if (pointBefore && !pointBefore.model.skip) { + deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; + pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; + pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; + } + if (pointAfter && !pointAfter.model.skip) { + deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; + pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; + pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; + } + } + }; + helpers$1.nextItem = function(collection, index, loop) { + if (loop) { + return index >= collection.length - 1 ? collection[0] : collection[index + 1]; + } + return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; + }; + helpers$1.previousItem = function(collection, index, loop) { + if (loop) { + return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; + } + return index <= 0 ? collection[0] : collection[index - 1]; + }; + // Implementation of the nice number algorithm used in determining where axis labels will go + helpers$1.niceNum = function(range, round) { + var exponent = Math.floor(helpers$1.log10(range)); + var fraction = range / Math.pow(10, exponent); + var niceFraction; + + if (round) { + if (fraction < 1.5) { + niceFraction = 1; + } else if (fraction < 3) { + niceFraction = 2; + } else if (fraction < 7) { + niceFraction = 5; + } else { + niceFraction = 10; + } + } else if (fraction <= 1.0) { + niceFraction = 1; + } else if (fraction <= 2) { + niceFraction = 2; + } else if (fraction <= 5) { + niceFraction = 5; + } else { + niceFraction = 10; + } + + return niceFraction * Math.pow(10, exponent); + }; + // Request animation polyfill - https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ + helpers$1.requestAnimFrame = (function() { + if (typeof window === 'undefined') { + return function(callback) { + callback(); + }; + } + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback) { + return window.setTimeout(callback, 1000 / 60); + }; + }()); + // -- DOM methods + helpers$1.getRelativePosition = function(evt, chart) { + var mouseX, mouseY; + var e = evt.originalEvent || evt; + var canvas = evt.target || evt.srcElement; + var boundingRect = canvas.getBoundingClientRect(); + + var touches = e.touches; + if (touches && touches.length > 0) { + mouseX = touches[0].clientX; + mouseY = touches[0].clientY; + + } else { + mouseX = e.clientX; + mouseY = e.clientY; + } + + // Scale mouse coordinates into canvas coordinates + // by following the pattern laid out by 'jerryj' in the comments of + // https://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ + var paddingLeft = parseFloat(helpers$1.getStyle(canvas, 'padding-left')); + var paddingTop = parseFloat(helpers$1.getStyle(canvas, 'padding-top')); + var paddingRight = parseFloat(helpers$1.getStyle(canvas, 'padding-right')); + var paddingBottom = parseFloat(helpers$1.getStyle(canvas, 'padding-bottom')); + var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; + var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; + + // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However + // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here + mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); + mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); + + return { + x: mouseX, + y: mouseY + }; + + }; + + // Private helper function to convert max-width/max-height values that may be percentages into a number + function parseMaxStyle(styleValue, node, parentProperty) { + var valueInPixels; + if (typeof styleValue === 'string') { + valueInPixels = parseInt(styleValue, 10); + + if (styleValue.indexOf('%') !== -1) { + // percentage * size in dimension + valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; + } + } else { + valueInPixels = styleValue; + } + + return valueInPixels; + } + + /** + * Returns if the given value contains an effective constraint. + * @private + */ + function isConstrainedValue(value) { + return value !== undefined && value !== null && value !== 'none'; + } + + /** + * Returns the max width or height of the given DOM node in a cross-browser compatible fashion + * @param {HTMLElement} domNode - the node to check the constraint on + * @param {string} maxStyle - the style that defines the maximum for the direction we are using ('max-width' / 'max-height') + * @param {string} percentageProperty - property of parent to use when calculating width as a percentage + * @see {@link https://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser} + */ + function getConstraintDimension(domNode, maxStyle, percentageProperty) { + var view = document.defaultView; + var parentNode = helpers$1._getParentNode(domNode); + var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; + var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; + var hasCNode = isConstrainedValue(constrainedNode); + var hasCContainer = isConstrainedValue(constrainedContainer); + var infinity = Number.POSITIVE_INFINITY; + + if (hasCNode || hasCContainer) { + return Math.min( + hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, + hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); + } + + return 'none'; + } + // returns Number or undefined if no constraint + helpers$1.getConstraintWidth = function(domNode) { + return getConstraintDimension(domNode, 'max-width', 'clientWidth'); + }; + // returns Number or undefined if no constraint + helpers$1.getConstraintHeight = function(domNode) { + return getConstraintDimension(domNode, 'max-height', 'clientHeight'); + }; + /** + * @private + */ + helpers$1._calculatePadding = function(container, padding, parentDimension) { + padding = helpers$1.getStyle(container, padding); + + return padding.indexOf('%') > -1 ? parentDimension * parseInt(padding, 10) / 100 : parseInt(padding, 10); + }; + /** + * @private + */ + helpers$1._getParentNode = function(domNode) { + var parent = domNode.parentNode; + if (parent && parent.toString() === '[object ShadowRoot]') { + parent = parent.host; + } + return parent; + }; + helpers$1.getMaximumWidth = function(domNode) { + var container = helpers$1._getParentNode(domNode); + if (!container) { + return domNode.clientWidth; + } + + var clientWidth = container.clientWidth; + var paddingLeft = helpers$1._calculatePadding(container, 'padding-left', clientWidth); + var paddingRight = helpers$1._calculatePadding(container, 'padding-right', clientWidth); + + var w = clientWidth - paddingLeft - paddingRight; + var cw = helpers$1.getConstraintWidth(domNode); + return isNaN(cw) ? w : Math.min(w, cw); + }; + helpers$1.getMaximumHeight = function(domNode) { + var container = helpers$1._getParentNode(domNode); + if (!container) { + return domNode.clientHeight; + } + + var clientHeight = container.clientHeight; + var paddingTop = helpers$1._calculatePadding(container, 'padding-top', clientHeight); + var paddingBottom = helpers$1._calculatePadding(container, 'padding-bottom', clientHeight); + + var h = clientHeight - paddingTop - paddingBottom; + var ch = helpers$1.getConstraintHeight(domNode); + return isNaN(ch) ? h : Math.min(h, ch); + }; + helpers$1.getStyle = function(el, property) { + return el.currentStyle ? + el.currentStyle[property] : + document.defaultView.getComputedStyle(el, null).getPropertyValue(property); + }; + helpers$1.retinaScale = function(chart, forceRatio) { + var pixelRatio = chart.currentDevicePixelRatio = forceRatio || (typeof window !== 'undefined' && window.devicePixelRatio) || 1; + if (pixelRatio === 1) { + return; + } + + var canvas = chart.canvas; + var height = chart.height; + var width = chart.width; + + canvas.height = height * pixelRatio; + canvas.width = width * pixelRatio; + chart.ctx.scale(pixelRatio, pixelRatio); + + // If no style has been set on the canvas, the render size is used as display size, + // making the chart visually bigger, so let's enforce it to the "correct" values. + // See https://github.com/chartjs/Chart.js/issues/3575 + if (!canvas.style.height && !canvas.style.width) { + canvas.style.height = height + 'px'; + canvas.style.width = width + 'px'; + } + }; + // -- Canvas methods + helpers$1.fontString = function(pixelSize, fontStyle, fontFamily) { + return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; + }; + helpers$1.longestText = function(ctx, font, arrayOfThings, cache) { + cache = cache || {}; + var data = cache.data = cache.data || {}; + var gc = cache.garbageCollect = cache.garbageCollect || []; + + if (cache.font !== font) { + data = cache.data = {}; + gc = cache.garbageCollect = []; + cache.font = font; + } + + ctx.font = font; + var longest = 0; + helpers$1.each(arrayOfThings, function(thing) { + // Undefined strings and arrays should not be measured + if (thing !== undefined && thing !== null && helpers$1.isArray(thing) !== true) { + longest = helpers$1.measureText(ctx, data, gc, longest, thing); + } else if (helpers$1.isArray(thing)) { + // if it is an array lets measure each element + // to do maybe simplify this function a bit so we can do this more recursively? + helpers$1.each(thing, function(nestedThing) { + // Undefined strings and arrays should not be measured + if (nestedThing !== undefined && nestedThing !== null && !helpers$1.isArray(nestedThing)) { + longest = helpers$1.measureText(ctx, data, gc, longest, nestedThing); + } + }); + } + }); + + var gcLen = gc.length / 2; + if (gcLen > arrayOfThings.length) { + for (var i = 0; i < gcLen; i++) { + delete data[gc[i]]; + } + gc.splice(0, gcLen); + } + return longest; + }; + helpers$1.measureText = function(ctx, data, gc, longest, string) { + var textWidth = data[string]; + if (!textWidth) { + textWidth = data[string] = ctx.measureText(string).width; + gc.push(string); + } + if (textWidth > longest) { + longest = textWidth; + } + return longest; + }; + helpers$1.numberOfLabelLines = function(arrayOfThings) { + var numberOfLines = 1; + helpers$1.each(arrayOfThings, function(thing) { + if (helpers$1.isArray(thing)) { + if (thing.length > numberOfLines) { + numberOfLines = thing.length; + } + } + }); + return numberOfLines; + }; + + helpers$1.color = !chartjsColor ? + function(value) { + console.error('Color.js not found!'); + return value; + } : + function(value) { + /* global CanvasGradient */ + if (value instanceof CanvasGradient) { + value = core_defaults.global.defaultColor; + } + + return chartjsColor(value); + }; + + helpers$1.getHoverColor = function(colorValue) { + /* global CanvasPattern */ + return (colorValue instanceof CanvasPattern || colorValue instanceof CanvasGradient) ? + colorValue : + helpers$1.color(colorValue).saturate(0.5).darken(0.1).rgbString(); + }; +}; + +function abstract() { + throw new Error( + 'This method is not implemented: either no adapter can ' + + 'be found or an incomplete integration was provided.' + ); +} + +/** + * Date adapter (current used by the time scale) + * @namespace Chart._adapters._date + * @memberof Chart._adapters + * @private + */ + +/** + * Currently supported unit string values. + * @typedef {('millisecond'|'second'|'minute'|'hour'|'day'|'week'|'month'|'quarter'|'year')} + * @memberof Chart._adapters._date + * @name Unit + */ + +/** + * @class + */ +function DateAdapter(options) { + this.options = options || {}; +} + +helpers$1.extend(DateAdapter.prototype, /** @lends DateAdapter */ { + /** + * Returns a map of time formats for the supported formatting units defined + * in Unit as well as 'datetime' representing a detailed date/time string. + * @returns {{string: string}} + */ + formats: abstract, + + /** + * Parses the given `value` and return the associated timestamp. + * @param {any} value - the value to parse (usually comes from the data) + * @param {string} [format] - the expected data format + * @returns {(number|null)} + * @function + */ + parse: abstract, + + /** + * Returns the formatted date in the specified `format` for a given `timestamp`. + * @param {number} timestamp - the timestamp to format + * @param {string} format - the date/time token + * @return {string} + * @function + */ + format: abstract, + + /** + * Adds the specified `amount` of `unit` to the given `timestamp`. + * @param {number} timestamp - the input timestamp + * @param {number} amount - the amount to add + * @param {Unit} unit - the unit as string + * @return {number} + * @function + */ + add: abstract, + + /** + * Returns the number of `unit` between the given timestamps. + * @param {number} max - the input timestamp (reference) + * @param {number} min - the timestamp to substract + * @param {Unit} unit - the unit as string + * @return {number} + * @function + */ + diff: abstract, + + /** + * Returns start of `unit` for the given `timestamp`. + * @param {number} timestamp - the input timestamp + * @param {Unit} unit - the unit as string + * @param {number} [weekday] - the ISO day of the week with 1 being Monday + * and 7 being Sunday (only needed if param *unit* is `isoWeek`). + * @function + */ + startOf: abstract, + + /** + * Returns end of `unit` for the given `timestamp`. + * @param {number} timestamp - the input timestamp + * @param {Unit} unit - the unit as string + * @function + */ + endOf: abstract, + + // DEPRECATIONS + + /** + * Provided for backward compatibility for scale.getValueForPixel(), + * this method should be overridden only by the moment adapter. + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ + _create: function(value) { + return value; + } +}); + +DateAdapter.override = function(members) { + helpers$1.extend(DateAdapter.prototype, members); +}; + +var _date = DateAdapter; + +var core_adapters = { + _date: _date +}; + +/** + * Namespace to hold static tick generation functions + * @namespace Chart.Ticks + */ +var core_ticks = { + /** + * Namespace to hold formatters for different types of ticks + * @namespace Chart.Ticks.formatters + */ + formatters: { + /** + * Formatter for value labels + * @method Chart.Ticks.formatters.values + * @param value the value to display + * @return {string|string[]} the label to display + */ + values: function(value) { + return helpers$1.isArray(value) ? value : '' + value; + }, + + /** + * Formatter for linear numeric ticks + * @method Chart.Ticks.formatters.linear + * @param tickValue {number} the value to be formatted + * @param index {number} the position of the tickValue parameter in the ticks array + * @param ticks {number[]} the list of ticks being converted + * @return {string} string representation of the tickValue parameter + */ + linear: function(tickValue, index, ticks) { + // If we have lots of ticks, don't use the ones + var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; + + // If we have a number like 2.5 as the delta, figure out how many decimal places we need + if (Math.abs(delta) > 1) { + if (tickValue !== Math.floor(tickValue)) { + // not an integer + delta = tickValue - Math.floor(tickValue); + } + } + + var logDelta = helpers$1.log10(Math.abs(delta)); + var tickString = ''; + + if (tickValue !== 0) { + var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1])); + if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation + var logTick = helpers$1.log10(Math.abs(tickValue)); + tickString = tickValue.toExponential(Math.floor(logTick) - Math.floor(logDelta)); + } else { + var numDecimal = -1 * Math.floor(logDelta); + numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places + tickString = tickValue.toFixed(numDecimal); + } + } else { + tickString = '0'; // never show decimal places for 0 + } + + return tickString; + }, + + logarithmic: function(tickValue, index, ticks) { + var remain = tickValue / (Math.pow(10, Math.floor(helpers$1.log10(tickValue)))); + + if (tickValue === 0) { + return '0'; + } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) { + return tickValue.toExponential(); + } + return ''; + } + } +}; + +var valueOrDefault$9 = helpers$1.valueOrDefault; +var valueAtIndexOrDefault = helpers$1.valueAtIndexOrDefault; + +core_defaults._set('scale', { + display: true, + position: 'left', + offset: false, + + // grid line settings + gridLines: { + display: true, + color: 'rgba(0, 0, 0, 0.1)', + lineWidth: 1, + drawBorder: true, + drawOnChartArea: true, + drawTicks: true, + tickMarkLength: 10, + zeroLineWidth: 1, + zeroLineColor: 'rgba(0,0,0,0.25)', + zeroLineBorderDash: [], + zeroLineBorderDashOffset: 0.0, + offsetGridLines: false, + borderDash: [], + borderDashOffset: 0.0 + }, + + // scale label + scaleLabel: { + // display property + display: false, + + // actual label + labelString: '', + + // top/bottom padding + padding: { + top: 4, + bottom: 4 + } + }, + + // label settings + ticks: { + beginAtZero: false, + minRotation: 0, + maxRotation: 50, + mirror: false, + padding: 0, + reverse: false, + display: true, + autoSkip: true, + autoSkipPadding: 0, + labelOffset: 0, + // We pass through arrays to be rendered as multiline labels, we convert Others to strings here. + callback: core_ticks.formatters.values, + minor: {}, + major: {} + } +}); + +function labelsFromTicks(ticks) { + var labels = []; + var i, ilen; + + for (i = 0, ilen = ticks.length; i < ilen; ++i) { + labels.push(ticks[i].label); + } + + return labels; +} + +function getPixelForGridLine(scale, index, offsetGridLines) { + var lineValue = scale.getPixelForTick(index); + + if (offsetGridLines) { + if (scale.getTicks().length === 1) { + lineValue -= scale.isHorizontal() ? + Math.max(lineValue - scale.left, scale.right - lineValue) : + Math.max(lineValue - scale.top, scale.bottom - lineValue); + } else if (index === 0) { + lineValue -= (scale.getPixelForTick(1) - lineValue) / 2; + } else { + lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2; + } + } + return lineValue; +} + +function computeTextSize(context, tick, font) { + return helpers$1.isArray(tick) ? + helpers$1.longestText(context, font, tick) : + context.measureText(tick).width; +} + +var core_scale = core_element.extend({ + /** + * Get the padding needed for the scale + * @method getPadding + * @private + * @returns {Padding} the necessary padding + */ + getPadding: function() { + var me = this; + return { + left: me.paddingLeft || 0, + top: me.paddingTop || 0, + right: me.paddingRight || 0, + bottom: me.paddingBottom || 0 + }; + }, + + /** + * Returns the scale tick objects ({label, major}) + * @since 2.7 + */ + getTicks: function() { + return this._ticks; + }, + + // These methods are ordered by lifecyle. Utilities then follow. + // Any function defined here is inherited by all scale types. + // Any function can be extended by the scale type + + mergeTicksOptions: function() { + var ticks = this.options.ticks; + if (ticks.minor === false) { + ticks.minor = { + display: false + }; + } + if (ticks.major === false) { + ticks.major = { + display: false + }; + } + for (var key in ticks) { + if (key !== 'major' && key !== 'minor') { + if (typeof ticks.minor[key] === 'undefined') { + ticks.minor[key] = ticks[key]; + } + if (typeof ticks.major[key] === 'undefined') { + ticks.major[key] = ticks[key]; + } + } + } + }, + beforeUpdate: function() { + helpers$1.callback(this.options.beforeUpdate, [this]); + }, + + update: function(maxWidth, maxHeight, margins) { + var me = this; + var i, ilen, labels, label, ticks, tick; + + // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) + me.beforeUpdate(); + + // Absorb the master measurements + me.maxWidth = maxWidth; + me.maxHeight = maxHeight; + me.margins = helpers$1.extend({ + left: 0, + right: 0, + top: 0, + bottom: 0 + }, margins); + + me._maxLabelLines = 0; + me.longestLabelWidth = 0; + me.longestTextCache = me.longestTextCache || {}; + + // Dimensions + me.beforeSetDimensions(); + me.setDimensions(); + me.afterSetDimensions(); + + // Data min/max + me.beforeDataLimits(); + me.determineDataLimits(); + me.afterDataLimits(); + + // Ticks - `this.ticks` is now DEPRECATED! + // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member + // and must not be accessed directly from outside this class. `this.ticks` being + // around for long time and not marked as private, we can't change its structure + // without unexpected breaking changes. If you need to access the scale ticks, + // use scale.getTicks() instead. + + me.beforeBuildTicks(); + + // New implementations should return an array of objects but for BACKWARD COMPAT, + // we still support no return (`this.ticks` internally set by calling this method). + ticks = me.buildTicks() || []; + + // Allow modification of ticks in callback. + ticks = me.afterBuildTicks(ticks) || ticks; + + me.beforeTickToLabelConversion(); + + // New implementations should return the formatted tick labels but for BACKWARD + // COMPAT, we still support no return (`this.ticks` internally changed by calling + // this method and supposed to contain only string values). + labels = me.convertTicksToLabels(ticks) || me.ticks; + + me.afterTickToLabelConversion(); + + me.ticks = labels; // BACKWARD COMPATIBILITY + + // IMPORTANT: from this point, we consider that `this.ticks` will NEVER change! + + // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`) + for (i = 0, ilen = labels.length; i < ilen; ++i) { + label = labels[i]; + tick = ticks[i]; + if (!tick) { + ticks.push(tick = { + label: label, + major: false + }); + } else { + tick.label = label; + } + } + + me._ticks = ticks; + + // Tick Rotation + me.beforeCalculateTickRotation(); + me.calculateTickRotation(); + me.afterCalculateTickRotation(); + // Fit + me.beforeFit(); + me.fit(); + me.afterFit(); + // + me.afterUpdate(); + + return me.minSize; + + }, + afterUpdate: function() { + helpers$1.callback(this.options.afterUpdate, [this]); + }, + + // + + beforeSetDimensions: function() { + helpers$1.callback(this.options.beforeSetDimensions, [this]); + }, + setDimensions: function() { + var me = this; + // Set the unconstrained dimension before label rotation + if (me.isHorizontal()) { + // Reset position before calculating rotation + me.width = me.maxWidth; + me.left = 0; + me.right = me.width; + } else { + me.height = me.maxHeight; + + // Reset position before calculating rotation + me.top = 0; + me.bottom = me.height; + } + + // Reset padding + me.paddingLeft = 0; + me.paddingTop = 0; + me.paddingRight = 0; + me.paddingBottom = 0; + }, + afterSetDimensions: function() { + helpers$1.callback(this.options.afterSetDimensions, [this]); + }, + + // Data limits + beforeDataLimits: function() { + helpers$1.callback(this.options.beforeDataLimits, [this]); + }, + determineDataLimits: helpers$1.noop, + afterDataLimits: function() { + helpers$1.callback(this.options.afterDataLimits, [this]); + }, + + // + beforeBuildTicks: function() { + helpers$1.callback(this.options.beforeBuildTicks, [this]); + }, + buildTicks: helpers$1.noop, + afterBuildTicks: function(ticks) { + var me = this; + // ticks is empty for old axis implementations here + if (helpers$1.isArray(ticks) && ticks.length) { + return helpers$1.callback(me.options.afterBuildTicks, [me, ticks]); + } + // Support old implementations (that modified `this.ticks` directly in buildTicks) + me.ticks = helpers$1.callback(me.options.afterBuildTicks, [me, me.ticks]) || me.ticks; + return ticks; + }, + + beforeTickToLabelConversion: function() { + helpers$1.callback(this.options.beforeTickToLabelConversion, [this]); + }, + convertTicksToLabels: function() { + var me = this; + // Convert ticks to strings + var tickOpts = me.options.ticks; + me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this); + }, + afterTickToLabelConversion: function() { + helpers$1.callback(this.options.afterTickToLabelConversion, [this]); + }, + + // + + beforeCalculateTickRotation: function() { + helpers$1.callback(this.options.beforeCalculateTickRotation, [this]); + }, + calculateTickRotation: function() { + var me = this; + var context = me.ctx; + var tickOpts = me.options.ticks; + var labels = labelsFromTicks(me._ticks); + + // Get the width of each grid by calculating the difference + // between x offsets between 0 and 1. + var tickFont = helpers$1.options._parseFont(tickOpts); + context.font = tickFont.string; + + var labelRotation = tickOpts.minRotation || 0; + + if (labels.length && me.options.display && me.isHorizontal()) { + var originalLabelWidth = helpers$1.longestText(context, tickFont.string, labels, me.longestTextCache); + var labelWidth = originalLabelWidth; + var cosRotation, sinRotation; + + // Allow 3 pixels x2 padding either side for label readability + var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6; + + // Max label rotation can be set or default to 90 - also act as a loop counter + while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) { + var angleRadians = helpers$1.toRadians(labelRotation); + cosRotation = Math.cos(angleRadians); + sinRotation = Math.sin(angleRadians); + + if (sinRotation * originalLabelWidth > me.maxHeight) { + // go back one step + labelRotation--; + break; + } + + labelRotation++; + labelWidth = cosRotation * originalLabelWidth; + } + } + + me.labelRotation = labelRotation; + }, + afterCalculateTickRotation: function() { + helpers$1.callback(this.options.afterCalculateTickRotation, [this]); + }, + + // + + beforeFit: function() { + helpers$1.callback(this.options.beforeFit, [this]); + }, + fit: function() { + var me = this; + // Reset + var minSize = me.minSize = { + width: 0, + height: 0 + }; + + var labels = labelsFromTicks(me._ticks); + + var opts = me.options; + var tickOpts = opts.ticks; + var scaleLabelOpts = opts.scaleLabel; + var gridLineOpts = opts.gridLines; + var display = me._isVisible(); + var position = opts.position; + var isHorizontal = me.isHorizontal(); + + var parseFont = helpers$1.options._parseFont; + var tickFont = parseFont(tickOpts); + var tickMarkLength = opts.gridLines.tickMarkLength; + + // Width + if (isHorizontal) { + // subtract the margins to line up with the chartArea if we are a full width scale + minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth; + } else { + minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0; + } + + // height + if (isHorizontal) { + minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0; + } else { + minSize.height = me.maxHeight; // fill all the height + } + + // Are we showing a title for the scale? + if (scaleLabelOpts.display && display) { + var scaleLabelFont = parseFont(scaleLabelOpts); + var scaleLabelPadding = helpers$1.options.toPadding(scaleLabelOpts.padding); + var deltaHeight = scaleLabelFont.lineHeight + scaleLabelPadding.height; + + if (isHorizontal) { + minSize.height += deltaHeight; + } else { + minSize.width += deltaHeight; + } + } + + // Don't bother fitting the ticks if we are not showing the labels + if (tickOpts.display && display) { + var largestTextWidth = helpers$1.longestText(me.ctx, tickFont.string, labels, me.longestTextCache); + var tallestLabelHeightInLines = helpers$1.numberOfLabelLines(labels); + var lineSpace = tickFont.size * 0.5; + var tickPadding = me.options.ticks.padding; + + // Store max number of lines and widest label for _autoSkip + me._maxLabelLines = tallestLabelHeightInLines; + me.longestLabelWidth = largestTextWidth; + + if (isHorizontal) { + var angleRadians = helpers$1.toRadians(me.labelRotation); + var cosRotation = Math.cos(angleRadians); + var sinRotation = Math.sin(angleRadians); + + // TODO - improve this calculation + var labelHeight = (sinRotation * largestTextWidth) + + (tickFont.lineHeight * tallestLabelHeightInLines) + + lineSpace; // padding + + minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding); + + me.ctx.font = tickFont.string; + var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.string); + var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.string); + var offsetLeft = me.getPixelForTick(0) - me.left; + var offsetRight = me.right - me.getPixelForTick(labels.length - 1); + var paddingLeft, paddingRight; + + // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned + // which means that the right padding is dominated by the font height + if (me.labelRotation !== 0) { + paddingLeft = position === 'bottom' ? (cosRotation * firstLabelWidth) : (cosRotation * lineSpace); + paddingRight = position === 'bottom' ? (cosRotation * lineSpace) : (cosRotation * lastLabelWidth); + } else { + paddingLeft = firstLabelWidth / 2; + paddingRight = lastLabelWidth / 2; + } + me.paddingLeft = Math.max(paddingLeft - offsetLeft, 0) + 3; // add 3 px to move away from canvas edges + me.paddingRight = Math.max(paddingRight - offsetRight, 0) + 3; + } else { + // A vertical axis is more constrained by the width. Labels are the + // dominant factor here, so get that length first and account for padding + if (tickOpts.mirror) { + largestTextWidth = 0; + } else { + // use lineSpace for consistency with horizontal axis + // tickPadding is not implemented for horizontal + largestTextWidth += tickPadding + lineSpace; + } + + minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth); + + me.paddingTop = tickFont.size / 2; + me.paddingBottom = tickFont.size / 2; + } + } + + me.handleMargins(); + + me.width = minSize.width; + me.height = minSize.height; + }, + + /** + * Handle margins and padding interactions + * @private + */ + handleMargins: function() { + var me = this; + if (me.margins) { + me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0); + me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0); + me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0); + me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0); + } + }, + + afterFit: function() { + helpers$1.callback(this.options.afterFit, [this]); + }, + + // Shared Methods + isHorizontal: function() { + return this.options.position === 'top' || this.options.position === 'bottom'; + }, + isFullWidth: function() { + return (this.options.fullWidth); + }, + + // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not + getRightValue: function(rawValue) { + // Null and undefined values first + if (helpers$1.isNullOrUndef(rawValue)) { + return NaN; + } + // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values + if ((typeof rawValue === 'number' || rawValue instanceof Number) && !isFinite(rawValue)) { + return NaN; + } + // If it is in fact an object, dive in one more level + if (rawValue) { + if (this.isHorizontal()) { + if (rawValue.x !== undefined) { + return this.getRightValue(rawValue.x); + } + } else if (rawValue.y !== undefined) { + return this.getRightValue(rawValue.y); + } + } + + // Value is good, return it + return rawValue; + }, + + /** + * Used to get the value to display in the tooltip for the data at the given index + * @param index + * @param datasetIndex + */ + getLabelForIndex: helpers$1.noop, + + /** + * Returns the location of the given data point. Value can either be an index or a numerical value + * The coordinate (0, 0) is at the upper-left corner of the canvas + * @param value + * @param index + * @param datasetIndex + */ + getPixelForValue: helpers$1.noop, + + /** + * Used to get the data value from a given pixel. This is the inverse of getPixelForValue + * The coordinate (0, 0) is at the upper-left corner of the canvas + * @param pixel + */ + getValueForPixel: helpers$1.noop, + + /** + * Returns the location of the tick at the given index + * The coordinate (0, 0) is at the upper-left corner of the canvas + */ + getPixelForTick: function(index) { + var me = this; + var offset = me.options.offset; + if (me.isHorizontal()) { + var innerWidth = me.width - (me.paddingLeft + me.paddingRight); + var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1); + var pixel = (tickWidth * index) + me.paddingLeft; + + if (offset) { + pixel += tickWidth / 2; + } + + var finalVal = me.left + pixel; + finalVal += me.isFullWidth() ? me.margins.left : 0; + return finalVal; + } + var innerHeight = me.height - (me.paddingTop + me.paddingBottom); + return me.top + (index * (innerHeight / (me._ticks.length - 1))); + }, + + /** + * Utility for getting the pixel location of a percentage of scale + * The coordinate (0, 0) is at the upper-left corner of the canvas + */ + getPixelForDecimal: function(decimal) { + var me = this; + if (me.isHorizontal()) { + var innerWidth = me.width - (me.paddingLeft + me.paddingRight); + var valueOffset = (innerWidth * decimal) + me.paddingLeft; + + var finalVal = me.left + valueOffset; + finalVal += me.isFullWidth() ? me.margins.left : 0; + return finalVal; + } + return me.top + (decimal * me.height); + }, + + /** + * Returns the pixel for the minimum chart value + * The coordinate (0, 0) is at the upper-left corner of the canvas + */ + getBasePixel: function() { + return this.getPixelForValue(this.getBaseValue()); + }, + + getBaseValue: function() { + var me = this; + var min = me.min; + var max = me.max; + + return me.beginAtZero ? 0 : + min < 0 && max < 0 ? max : + min > 0 && max > 0 ? min : + 0; + }, + + /** + * Returns a subset of ticks to be plotted to avoid overlapping labels. + * @private + */ + _autoSkip: function(ticks) { + var me = this; + var isHorizontal = me.isHorizontal(); + var optionTicks = me.options.ticks.minor; + var tickCount = ticks.length; + var skipRatio = false; + var maxTicks = optionTicks.maxTicksLimit; + + // Total space needed to display all ticks. First and last ticks are + // drawn as their center at end of axis, so tickCount-1 + var ticksLength = me._tickSize() * (tickCount - 1); + + // Axis length + var axisLength = isHorizontal + ? me.width - (me.paddingLeft + me.paddingRight) + : me.height - (me.paddingTop + me.PaddingBottom); + + var result = []; + var i, tick; + + if (ticksLength > axisLength) { + skipRatio = 1 + Math.floor(ticksLength / axisLength); + } + + // if they defined a max number of optionTicks, + // increase skipRatio until that number is met + if (tickCount > maxTicks) { + skipRatio = Math.max(skipRatio, 1 + Math.floor(tickCount / maxTicks)); + } + + for (i = 0; i < tickCount; i++) { + tick = ticks[i]; + + if (skipRatio > 1 && i % skipRatio > 0) { + // leave tick in place but make sure it's not displayed (#4635) + delete tick.label; + } + result.push(tick); + } + return result; + }, + + /** + * @private + */ + _tickSize: function() { + var me = this; + var isHorizontal = me.isHorizontal(); + var optionTicks = me.options.ticks.minor; + + // Calculate space needed by label in axis direction. + var rot = helpers$1.toRadians(me.labelRotation); + var cos = Math.abs(Math.cos(rot)); + var sin = Math.abs(Math.sin(rot)); + + var padding = optionTicks.autoSkipPadding || 0; + var w = (me.longestLabelWidth + padding) || 0; + + var tickFont = helpers$1.options._parseFont(optionTicks); + var h = (me._maxLabelLines * tickFont.lineHeight + padding) || 0; + + // Calculate space needed for 1 tick in axis direction. + return isHorizontal + ? h * cos > w * sin ? w / cos : h / sin + : h * sin < w * cos ? h / cos : w / sin; + }, + + /** + * @private + */ + _isVisible: function() { + var me = this; + var chart = me.chart; + var display = me.options.display; + var i, ilen, meta; + + if (display !== 'auto') { + return !!display; + } + + // When 'auto', the scale is visible if at least one associated dataset is visible. + for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { + if (chart.isDatasetVisible(i)) { + meta = chart.getDatasetMeta(i); + if (meta.xAxisID === me.id || meta.yAxisID === me.id) { + return true; + } + } + } + + return false; + }, + + /** + * Actually draw the scale on the canvas + * @param {object} chartArea - the area of the chart to draw full grid lines on + */ + draw: function(chartArea) { + var me = this; + var options = me.options; + + if (!me._isVisible()) { + return; + } + + var chart = me.chart; + var context = me.ctx; + var globalDefaults = core_defaults.global; + var defaultFontColor = globalDefaults.defaultFontColor; + var optionTicks = options.ticks.minor; + var optionMajorTicks = options.ticks.major || optionTicks; + var gridLines = options.gridLines; + var scaleLabel = options.scaleLabel; + var position = options.position; + + var isRotated = me.labelRotation !== 0; + var isMirrored = optionTicks.mirror; + var isHorizontal = me.isHorizontal(); + + var parseFont = helpers$1.options._parseFont; + var ticks = optionTicks.display && optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks(); + var tickFontColor = valueOrDefault$9(optionTicks.fontColor, defaultFontColor); + var tickFont = parseFont(optionTicks); + var lineHeight = tickFont.lineHeight; + var majorTickFontColor = valueOrDefault$9(optionMajorTicks.fontColor, defaultFontColor); + var majorTickFont = parseFont(optionMajorTicks); + var tickPadding = optionTicks.padding; + var labelOffset = optionTicks.labelOffset; + + var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0; + + var scaleLabelFontColor = valueOrDefault$9(scaleLabel.fontColor, defaultFontColor); + var scaleLabelFont = parseFont(scaleLabel); + var scaleLabelPadding = helpers$1.options.toPadding(scaleLabel.padding); + var labelRotationRadians = helpers$1.toRadians(me.labelRotation); + + var itemsToDraw = []; + + var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0; + var alignPixel = helpers$1._alignPixel; + var borderValue, tickStart, tickEnd; + + if (position === 'top') { + borderValue = alignPixel(chart, me.bottom, axisWidth); + tickStart = me.bottom - tl; + tickEnd = borderValue - axisWidth / 2; + } else if (position === 'bottom') { + borderValue = alignPixel(chart, me.top, axisWidth); + tickStart = borderValue + axisWidth / 2; + tickEnd = me.top + tl; + } else if (position === 'left') { + borderValue = alignPixel(chart, me.right, axisWidth); + tickStart = me.right - tl; + tickEnd = borderValue - axisWidth / 2; + } else { + borderValue = alignPixel(chart, me.left, axisWidth); + tickStart = borderValue + axisWidth / 2; + tickEnd = me.left + tl; + } + + var epsilon = 0.0000001; // 0.0000001 is margin in pixels for Accumulated error. + + helpers$1.each(ticks, function(tick, index) { + // autoskipper skipped this tick (#4635) + if (helpers$1.isNullOrUndef(tick.label)) { + return; + } + + var label = tick.label; + var lineWidth, lineColor, borderDash, borderDashOffset; + if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) { + // Draw the first index specially + lineWidth = gridLines.zeroLineWidth; + lineColor = gridLines.zeroLineColor; + borderDash = gridLines.zeroLineBorderDash || []; + borderDashOffset = gridLines.zeroLineBorderDashOffset || 0.0; + } else { + lineWidth = valueAtIndexOrDefault(gridLines.lineWidth, index); + lineColor = valueAtIndexOrDefault(gridLines.color, index); + borderDash = gridLines.borderDash || []; + borderDashOffset = gridLines.borderDashOffset || 0.0; + } + + // Common properties + var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY, textOffset, textAlign; + var labelCount = helpers$1.isArray(label) ? label.length : 1; + var lineValue = getPixelForGridLine(me, index, gridLines.offsetGridLines); + + if (isHorizontal) { + var labelYOffset = tl + tickPadding; + + if (lineValue < me.left - epsilon) { + lineColor = 'rgba(0,0,0,0)'; + } + + tx1 = tx2 = x1 = x2 = alignPixel(chart, lineValue, lineWidth); + ty1 = tickStart; + ty2 = tickEnd; + labelX = me.getPixelForTick(index) + labelOffset; // x values for optionTicks (need to consider offsetLabel option) + + if (position === 'top') { + y1 = alignPixel(chart, chartArea.top, axisWidth) + axisWidth / 2; + y2 = chartArea.bottom; + textOffset = ((!isRotated ? 0.5 : 1) - labelCount) * lineHeight; + textAlign = !isRotated ? 'center' : 'left'; + labelY = me.bottom - labelYOffset; + } else { + y1 = chartArea.top; + y2 = alignPixel(chart, chartArea.bottom, axisWidth) - axisWidth / 2; + textOffset = (!isRotated ? 0.5 : 0) * lineHeight; + textAlign = !isRotated ? 'center' : 'right'; + labelY = me.top + labelYOffset; + } + } else { + var labelXOffset = (isMirrored ? 0 : tl) + tickPadding; + + if (lineValue < me.top - epsilon) { + lineColor = 'rgba(0,0,0,0)'; + } + + tx1 = tickStart; + tx2 = tickEnd; + ty1 = ty2 = y1 = y2 = alignPixel(chart, lineValue, lineWidth); + labelY = me.getPixelForTick(index) + labelOffset; + textOffset = (1 - labelCount) * lineHeight / 2; + + if (position === 'left') { + x1 = alignPixel(chart, chartArea.left, axisWidth) + axisWidth / 2; + x2 = chartArea.right; + textAlign = isMirrored ? 'left' : 'right'; + labelX = me.right - labelXOffset; + } else { + x1 = chartArea.left; + x2 = alignPixel(chart, chartArea.right, axisWidth) - axisWidth / 2; + textAlign = isMirrored ? 'right' : 'left'; + labelX = me.left + labelXOffset; + } + } + + itemsToDraw.push({ + tx1: tx1, + ty1: ty1, + tx2: tx2, + ty2: ty2, + x1: x1, + y1: y1, + x2: x2, + y2: y2, + labelX: labelX, + labelY: labelY, + glWidth: lineWidth, + glColor: lineColor, + glBorderDash: borderDash, + glBorderDashOffset: borderDashOffset, + rotation: -1 * labelRotationRadians, + label: label, + major: tick.major, + textOffset: textOffset, + textAlign: textAlign + }); + }); + + // Draw all of the tick labels, tick marks, and grid lines at the correct places + helpers$1.each(itemsToDraw, function(itemToDraw) { + var glWidth = itemToDraw.glWidth; + var glColor = itemToDraw.glColor; + + if (gridLines.display && glWidth && glColor) { + context.save(); + context.lineWidth = glWidth; + context.strokeStyle = glColor; + if (context.setLineDash) { + context.setLineDash(itemToDraw.glBorderDash); + context.lineDashOffset = itemToDraw.glBorderDashOffset; + } + + context.beginPath(); + + if (gridLines.drawTicks) { + context.moveTo(itemToDraw.tx1, itemToDraw.ty1); + context.lineTo(itemToDraw.tx2, itemToDraw.ty2); + } + + if (gridLines.drawOnChartArea) { + context.moveTo(itemToDraw.x1, itemToDraw.y1); + context.lineTo(itemToDraw.x2, itemToDraw.y2); + } + + context.stroke(); + context.restore(); + } + + if (optionTicks.display) { + // Make sure we draw text in the correct color and font + context.save(); + context.translate(itemToDraw.labelX, itemToDraw.labelY); + context.rotate(itemToDraw.rotation); + context.font = itemToDraw.major ? majorTickFont.string : tickFont.string; + context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor; + context.textBaseline = 'middle'; + context.textAlign = itemToDraw.textAlign; + + var label = itemToDraw.label; + var y = itemToDraw.textOffset; + if (helpers$1.isArray(label)) { + for (var i = 0; i < label.length; ++i) { + // We just make sure the multiline element is a string here.. + context.fillText('' + label[i], 0, y); + y += lineHeight; + } + } else { + context.fillText(label, 0, y); + } + context.restore(); + } + }); + + if (scaleLabel.display) { + // Draw the scale label + var scaleLabelX; + var scaleLabelY; + var rotation = 0; + var halfLineHeight = scaleLabelFont.lineHeight / 2; + + if (isHorizontal) { + scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width + scaleLabelY = position === 'bottom' + ? me.bottom - halfLineHeight - scaleLabelPadding.bottom + : me.top + halfLineHeight + scaleLabelPadding.top; + } else { + var isLeft = position === 'left'; + scaleLabelX = isLeft + ? me.left + halfLineHeight + scaleLabelPadding.top + : me.right - halfLineHeight - scaleLabelPadding.top; + scaleLabelY = me.top + ((me.bottom - me.top) / 2); + rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; + } + + context.save(); + context.translate(scaleLabelX, scaleLabelY); + context.rotate(rotation); + context.textAlign = 'center'; + context.textBaseline = 'middle'; + context.fillStyle = scaleLabelFontColor; // render in correct colour + context.font = scaleLabelFont.string; + context.fillText(scaleLabel.labelString, 0, 0); + context.restore(); + } + + if (axisWidth) { + // Draw the line at the edge of the axis + var firstLineWidth = axisWidth; + var lastLineWidth = valueAtIndexOrDefault(gridLines.lineWidth, ticks.length - 1, 0); + var x1, x2, y1, y2; + + if (isHorizontal) { + x1 = alignPixel(chart, me.left, firstLineWidth) - firstLineWidth / 2; + x2 = alignPixel(chart, me.right, lastLineWidth) + lastLineWidth / 2; + y1 = y2 = borderValue; + } else { + y1 = alignPixel(chart, me.top, firstLineWidth) - firstLineWidth / 2; + y2 = alignPixel(chart, me.bottom, lastLineWidth) + lastLineWidth / 2; + x1 = x2 = borderValue; + } + + context.lineWidth = axisWidth; + context.strokeStyle = valueAtIndexOrDefault(gridLines.color, 0); + context.beginPath(); + context.moveTo(x1, y1); + context.lineTo(x2, y2); + context.stroke(); + } + } +}); + +var defaultConfig = { + position: 'bottom' +}; + +var scale_category = core_scale.extend({ + /** + * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those + * else fall back to data.labels + * @private + */ + getLabels: function() { + var data = this.chart.data; + return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; + }, + + determineDataLimits: function() { + var me = this; + var labels = me.getLabels(); + me.minIndex = 0; + me.maxIndex = labels.length - 1; + var findIndex; + + if (me.options.ticks.min !== undefined) { + // user specified min value + findIndex = labels.indexOf(me.options.ticks.min); + me.minIndex = findIndex !== -1 ? findIndex : me.minIndex; + } + + if (me.options.ticks.max !== undefined) { + // user specified max value + findIndex = labels.indexOf(me.options.ticks.max); + me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex; + } + + me.min = labels[me.minIndex]; + me.max = labels[me.maxIndex]; + }, + + buildTicks: function() { + var me = this; + var labels = me.getLabels(); + // If we are viewing some subset of labels, slice the original array + me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1); + }, + + getLabelForIndex: function(index, datasetIndex) { + var me = this; + var chart = me.chart; + + if (chart.getDatasetMeta(datasetIndex).controller._getValueScaleId() === me.id) { + return me.getRightValue(chart.data.datasets[datasetIndex].data[index]); + } + + return me.ticks[index - me.minIndex]; + }, + + // Used to get data value locations. Value can either be an index or a numerical value + getPixelForValue: function(value, index) { + var me = this; + var offset = me.options.offset; + // 1 is added because we need the length but we have the indexes + var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1); + + // If value is a data object, then index is the index in the data array, + // not the index of the scale. We need to change that. + var valueCategory; + if (value !== undefined && value !== null) { + valueCategory = me.isHorizontal() ? value.x : value.y; + } + if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { + var labels = me.getLabels(); + value = valueCategory || value; + var idx = labels.indexOf(value); + index = idx !== -1 ? idx : index; + } + + if (me.isHorizontal()) { + var valueWidth = me.width / offsetAmt; + var widthOffset = (valueWidth * (index - me.minIndex)); + + if (offset) { + widthOffset += (valueWidth / 2); + } + + return me.left + widthOffset; + } + var valueHeight = me.height / offsetAmt; + var heightOffset = (valueHeight * (index - me.minIndex)); + + if (offset) { + heightOffset += (valueHeight / 2); + } + + return me.top + heightOffset; + }, + + getPixelForTick: function(index) { + return this.getPixelForValue(this.ticks[index], index + this.minIndex, null); + }, + + getValueForPixel: function(pixel) { + var me = this; + var offset = me.options.offset; + var value; + var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1); + var horz = me.isHorizontal(); + var valueDimension = (horz ? me.width : me.height) / offsetAmt; + + pixel -= horz ? me.left : me.top; + + if (offset) { + pixel -= (valueDimension / 2); + } + + if (pixel <= 0) { + value = 0; + } else { + value = Math.round(pixel / valueDimension); + } + + return value + me.minIndex; + }, + + getBasePixel: function() { + return this.bottom; + } +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults = defaultConfig; +scale_category._defaults = _defaults; + +var noop = helpers$1.noop; +var isNullOrUndef = helpers$1.isNullOrUndef; + +/** + * Generate a set of linear ticks + * @param generationOptions the options used to generate the ticks + * @param dataRange the range of the data + * @returns {number[]} array of tick values + */ +function generateTicks(generationOptions, dataRange) { + var ticks = []; + // To get a "nice" value for the tick spacing, we will use the appropriately named + // "nice number" algorithm. See https://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks + // for details. + + var MIN_SPACING = 1e-14; + var stepSize = generationOptions.stepSize; + var unit = stepSize || 1; + var maxNumSpaces = generationOptions.maxTicks - 1; + var min = generationOptions.min; + var max = generationOptions.max; + var precision = generationOptions.precision; + var rmin = dataRange.min; + var rmax = dataRange.max; + var spacing = helpers$1.niceNum((rmax - rmin) / maxNumSpaces / unit) * unit; + var factor, niceMin, niceMax, numSpaces; + + // Beyond MIN_SPACING floating point numbers being to lose precision + // such that we can't do the math necessary to generate ticks + if (spacing < MIN_SPACING && isNullOrUndef(min) && isNullOrUndef(max)) { + return [rmin, rmax]; + } + + numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing); + if (numSpaces > maxNumSpaces) { + // If the calculated num of spaces exceeds maxNumSpaces, recalculate it + spacing = helpers$1.niceNum(numSpaces * spacing / maxNumSpaces / unit) * unit; + } + + if (stepSize || isNullOrUndef(precision)) { + // If a precision is not specified, calculate factor based on spacing + factor = Math.pow(10, helpers$1._decimalPlaces(spacing)); + } else { + // If the user specified a precision, round to that number of decimal places + factor = Math.pow(10, precision); + spacing = Math.ceil(spacing * factor) / factor; + } + + niceMin = Math.floor(rmin / spacing) * spacing; + niceMax = Math.ceil(rmax / spacing) * spacing; + + // If min, max and stepSize is set and they make an evenly spaced scale use it. + if (stepSize) { + // If very close to our whole number, use it. + if (!isNullOrUndef(min) && helpers$1.almostWhole(min / spacing, spacing / 1000)) { + niceMin = min; + } + if (!isNullOrUndef(max) && helpers$1.almostWhole(max / spacing, spacing / 1000)) { + niceMax = max; + } + } + + numSpaces = (niceMax - niceMin) / spacing; + // If very close to our rounded value, use it. + if (helpers$1.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { + numSpaces = Math.round(numSpaces); + } else { + numSpaces = Math.ceil(numSpaces); + } + + niceMin = Math.round(niceMin * factor) / factor; + niceMax = Math.round(niceMax * factor) / factor; + ticks.push(isNullOrUndef(min) ? niceMin : min); + for (var j = 1; j < numSpaces; ++j) { + ticks.push(Math.round((niceMin + j * spacing) * factor) / factor); + } + ticks.push(isNullOrUndef(max) ? niceMax : max); + + return ticks; +} + +var scale_linearbase = core_scale.extend({ + getRightValue: function(value) { + if (typeof value === 'string') { + return +value; + } + return core_scale.prototype.getRightValue.call(this, value); + }, + + handleTickRangeOptions: function() { + var me = this; + var opts = me.options; + var tickOpts = opts.ticks; + + // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, + // do nothing since that would make the chart weird. If the user really wants a weird chart + // axis, they can manually override it + if (tickOpts.beginAtZero) { + var minSign = helpers$1.sign(me.min); + var maxSign = helpers$1.sign(me.max); + + if (minSign < 0 && maxSign < 0) { + // move the top up to 0 + me.max = 0; + } else if (minSign > 0 && maxSign > 0) { + // move the bottom down to 0 + me.min = 0; + } + } + + var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined; + var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined; + + if (tickOpts.min !== undefined) { + me.min = tickOpts.min; + } else if (tickOpts.suggestedMin !== undefined) { + if (me.min === null) { + me.min = tickOpts.suggestedMin; + } else { + me.min = Math.min(me.min, tickOpts.suggestedMin); + } + } + + if (tickOpts.max !== undefined) { + me.max = tickOpts.max; + } else if (tickOpts.suggestedMax !== undefined) { + if (me.max === null) { + me.max = tickOpts.suggestedMax; + } else { + me.max = Math.max(me.max, tickOpts.suggestedMax); + } + } + + if (setMin !== setMax) { + // We set the min or the max but not both. + // So ensure that our range is good + // Inverted or 0 length range can happen when + // ticks.min is set, and no datasets are visible + if (me.min >= me.max) { + if (setMin) { + me.max = me.min + 1; + } else { + me.min = me.max - 1; + } + } + } + + if (me.min === me.max) { + me.max++; + + if (!tickOpts.beginAtZero) { + me.min--; + } + } + }, + + getTickLimit: function() { + var me = this; + var tickOpts = me.options.ticks; + var stepSize = tickOpts.stepSize; + var maxTicksLimit = tickOpts.maxTicksLimit; + var maxTicks; + + if (stepSize) { + maxTicks = Math.ceil(me.max / stepSize) - Math.floor(me.min / stepSize) + 1; + } else { + maxTicks = me._computeTickLimit(); + maxTicksLimit = maxTicksLimit || 11; + } + + if (maxTicksLimit) { + maxTicks = Math.min(maxTicksLimit, maxTicks); + } + + return maxTicks; + }, + + _computeTickLimit: function() { + return Number.POSITIVE_INFINITY; + }, + + handleDirectionalChanges: noop, + + buildTicks: function() { + var me = this; + var opts = me.options; + var tickOpts = opts.ticks; + + // Figure out what the max number of ticks we can support it is based on the size of + // the axis area. For now, we say that the minimum tick spacing in pixels must be 40 + // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on + // the graph. Make sure we always have at least 2 ticks + var maxTicks = me.getTickLimit(); + maxTicks = Math.max(2, maxTicks); + + var numericGeneratorOptions = { + maxTicks: maxTicks, + min: tickOpts.min, + max: tickOpts.max, + precision: tickOpts.precision, + stepSize: helpers$1.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize) + }; + var ticks = me.ticks = generateTicks(numericGeneratorOptions, me); + + me.handleDirectionalChanges(); + + // At this point, we need to update our max and min given the tick values since we have expanded the + // range of the scale + me.max = helpers$1.max(ticks); + me.min = helpers$1.min(ticks); + + if (tickOpts.reverse) { + ticks.reverse(); + + me.start = me.max; + me.end = me.min; + } else { + me.start = me.min; + me.end = me.max; + } + }, + + convertTicksToLabels: function() { + var me = this; + me.ticksAsNumbers = me.ticks.slice(); + me.zeroLineIndex = me.ticks.indexOf(0); + + core_scale.prototype.convertTicksToLabels.call(me); + } +}); + +var defaultConfig$1 = { + position: 'left', + ticks: { + callback: core_ticks.formatters.linear + } +}; + +var scale_linear = scale_linearbase.extend({ + determineDataLimits: function() { + var me = this; + var opts = me.options; + var chart = me.chart; + var data = chart.data; + var datasets = data.datasets; + var isHorizontal = me.isHorizontal(); + var DEFAULT_MIN = 0; + var DEFAULT_MAX = 1; + + function IDMatches(meta) { + return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; + } + + // First Calculate the range + me.min = null; + me.max = null; + + var hasStacks = opts.stacked; + if (hasStacks === undefined) { + helpers$1.each(datasets, function(dataset, datasetIndex) { + if (hasStacks) { + return; + } + + var meta = chart.getDatasetMeta(datasetIndex); + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && + meta.stack !== undefined) { + hasStacks = true; + } + }); + } + + if (opts.stacked || hasStacks) { + var valuesPerStack = {}; + + helpers$1.each(datasets, function(dataset, datasetIndex) { + var meta = chart.getDatasetMeta(datasetIndex); + var key = [ + meta.type, + // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined + ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), + meta.stack + ].join('.'); + + if (valuesPerStack[key] === undefined) { + valuesPerStack[key] = { + positiveValues: [], + negativeValues: [] + }; + } + + // Store these per type + var positiveValues = valuesPerStack[key].positiveValues; + var negativeValues = valuesPerStack[key].negativeValues; + + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { + helpers$1.each(dataset.data, function(rawValue, index) { + var value = +me.getRightValue(rawValue); + if (isNaN(value) || meta.data[index].hidden) { + return; + } + + positiveValues[index] = positiveValues[index] || 0; + negativeValues[index] = negativeValues[index] || 0; + + if (opts.relativePoints) { + positiveValues[index] = 100; + } else if (value < 0) { + negativeValues[index] += value; + } else { + positiveValues[index] += value; + } + }); + } + }); + + helpers$1.each(valuesPerStack, function(valuesForType) { + var values = valuesForType.positiveValues.concat(valuesForType.negativeValues); + var minVal = helpers$1.min(values); + var maxVal = helpers$1.max(values); + me.min = me.min === null ? minVal : Math.min(me.min, minVal); + me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); + }); + + } else { + helpers$1.each(datasets, function(dataset, datasetIndex) { + var meta = chart.getDatasetMeta(datasetIndex); + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { + helpers$1.each(dataset.data, function(rawValue, index) { + var value = +me.getRightValue(rawValue); + if (isNaN(value) || meta.data[index].hidden) { + return; + } + + if (me.min === null) { + me.min = value; + } else if (value < me.min) { + me.min = value; + } + + if (me.max === null) { + me.max = value; + } else if (value > me.max) { + me.max = value; + } + }); + } + }); + } + + me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN; + me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX; + + // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero + this.handleTickRangeOptions(); + }, + + // Returns the maximum number of ticks based on the scale dimension + _computeTickLimit: function() { + var me = this; + var tickFont; + + if (me.isHorizontal()) { + return Math.ceil(me.width / 40); + } + tickFont = helpers$1.options._parseFont(me.options.ticks); + return Math.ceil(me.height / tickFont.lineHeight); + }, + + // Called after the ticks are built. We need + handleDirectionalChanges: function() { + if (!this.isHorizontal()) { + // We are in a vertical orientation. The top value is the highest. So reverse the array + this.ticks.reverse(); + } + }, + + getLabelForIndex: function(index, datasetIndex) { + return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); + }, + + // Utils + getPixelForValue: function(value) { + // This must be called after fit has been run so that + // this.left, this.top, this.right, and this.bottom have been defined + var me = this; + var start = me.start; + + var rightValue = +me.getRightValue(value); + var pixel; + var range = me.end - start; + + if (me.isHorizontal()) { + pixel = me.left + (me.width / range * (rightValue - start)); + } else { + pixel = me.bottom - (me.height / range * (rightValue - start)); + } + return pixel; + }, + + getValueForPixel: function(pixel) { + var me = this; + var isHorizontal = me.isHorizontal(); + var innerDimension = isHorizontal ? me.width : me.height; + var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension; + return me.start + ((me.end - me.start) * offset); + }, + + getPixelForTick: function(index) { + return this.getPixelForValue(this.ticksAsNumbers[index]); + } +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults$1 = defaultConfig$1; +scale_linear._defaults = _defaults$1; + +var valueOrDefault$a = helpers$1.valueOrDefault; + +/** + * Generate a set of logarithmic ticks + * @param generationOptions the options used to generate the ticks + * @param dataRange the range of the data + * @returns {number[]} array of tick values + */ +function generateTicks$1(generationOptions, dataRange) { + var ticks = []; + + var tickVal = valueOrDefault$a(generationOptions.min, Math.pow(10, Math.floor(helpers$1.log10(dataRange.min)))); + + var endExp = Math.floor(helpers$1.log10(dataRange.max)); + var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); + var exp, significand; + + if (tickVal === 0) { + exp = Math.floor(helpers$1.log10(dataRange.minNotZero)); + significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); + + ticks.push(tickVal); + tickVal = significand * Math.pow(10, exp); + } else { + exp = Math.floor(helpers$1.log10(tickVal)); + significand = Math.floor(tickVal / Math.pow(10, exp)); + } + var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1; + + do { + ticks.push(tickVal); + + ++significand; + if (significand === 10) { + significand = 1; + ++exp; + precision = exp >= 0 ? 1 : precision; + } + + tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision; + } while (exp < endExp || (exp === endExp && significand < endSignificand)); + + var lastTick = valueOrDefault$a(generationOptions.max, tickVal); + ticks.push(lastTick); + + return ticks; +} + +var defaultConfig$2 = { + position: 'left', + + // label settings + ticks: { + callback: core_ticks.formatters.logarithmic + } +}; + +// TODO(v3): change this to positiveOrDefault +function nonNegativeOrDefault(value, defaultValue) { + return helpers$1.isFinite(value) && value >= 0 ? value : defaultValue; +} + +var scale_logarithmic = core_scale.extend({ + determineDataLimits: function() { + var me = this; + var opts = me.options; + var chart = me.chart; + var data = chart.data; + var datasets = data.datasets; + var isHorizontal = me.isHorizontal(); + function IDMatches(meta) { + return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; + } + + // Calculate Range + me.min = null; + me.max = null; + me.minNotZero = null; + + var hasStacks = opts.stacked; + if (hasStacks === undefined) { + helpers$1.each(datasets, function(dataset, datasetIndex) { + if (hasStacks) { + return; + } + + var meta = chart.getDatasetMeta(datasetIndex); + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && + meta.stack !== undefined) { + hasStacks = true; + } + }); + } + + if (opts.stacked || hasStacks) { + var valuesPerStack = {}; + + helpers$1.each(datasets, function(dataset, datasetIndex) { + var meta = chart.getDatasetMeta(datasetIndex); + var key = [ + meta.type, + // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined + ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), + meta.stack + ].join('.'); + + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { + if (valuesPerStack[key] === undefined) { + valuesPerStack[key] = []; + } + + helpers$1.each(dataset.data, function(rawValue, index) { + var values = valuesPerStack[key]; + var value = +me.getRightValue(rawValue); + // invalid, hidden and negative values are ignored + if (isNaN(value) || meta.data[index].hidden || value < 0) { + return; + } + values[index] = values[index] || 0; + values[index] += value; + }); + } + }); + + helpers$1.each(valuesPerStack, function(valuesForType) { + if (valuesForType.length > 0) { + var minVal = helpers$1.min(valuesForType); + var maxVal = helpers$1.max(valuesForType); + me.min = me.min === null ? minVal : Math.min(me.min, minVal); + me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); + } + }); + + } else { + helpers$1.each(datasets, function(dataset, datasetIndex) { + var meta = chart.getDatasetMeta(datasetIndex); + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { + helpers$1.each(dataset.data, function(rawValue, index) { + var value = +me.getRightValue(rawValue); + // invalid, hidden and negative values are ignored + if (isNaN(value) || meta.data[index].hidden || value < 0) { + return; + } + + if (me.min === null) { + me.min = value; + } else if (value < me.min) { + me.min = value; + } + + if (me.max === null) { + me.max = value; + } else if (value > me.max) { + me.max = value; + } + + if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) { + me.minNotZero = value; + } + }); + } + }); + } + + // Common base implementation to handle ticks.min, ticks.max + this.handleTickRangeOptions(); + }, + + handleTickRangeOptions: function() { + var me = this; + var tickOpts = me.options.ticks; + var DEFAULT_MIN = 1; + var DEFAULT_MAX = 10; + + me.min = nonNegativeOrDefault(tickOpts.min, me.min); + me.max = nonNegativeOrDefault(tickOpts.max, me.max); + + if (me.min === me.max) { + if (me.min !== 0 && me.min !== null) { + me.min = Math.pow(10, Math.floor(helpers$1.log10(me.min)) - 1); + me.max = Math.pow(10, Math.floor(helpers$1.log10(me.max)) + 1); + } else { + me.min = DEFAULT_MIN; + me.max = DEFAULT_MAX; + } + } + if (me.min === null) { + me.min = Math.pow(10, Math.floor(helpers$1.log10(me.max)) - 1); + } + if (me.max === null) { + me.max = me.min !== 0 + ? Math.pow(10, Math.floor(helpers$1.log10(me.min)) + 1) + : DEFAULT_MAX; + } + if (me.minNotZero === null) { + if (me.min > 0) { + me.minNotZero = me.min; + } else if (me.max < 1) { + me.minNotZero = Math.pow(10, Math.floor(helpers$1.log10(me.max))); + } else { + me.minNotZero = DEFAULT_MIN; + } + } + }, + + buildTicks: function() { + var me = this; + var tickOpts = me.options.ticks; + var reverse = !me.isHorizontal(); + + var generationOptions = { + min: nonNegativeOrDefault(tickOpts.min), + max: nonNegativeOrDefault(tickOpts.max) + }; + var ticks = me.ticks = generateTicks$1(generationOptions, me); + + // At this point, we need to update our max and min given the tick values since we have expanded the + // range of the scale + me.max = helpers$1.max(ticks); + me.min = helpers$1.min(ticks); + + if (tickOpts.reverse) { + reverse = !reverse; + me.start = me.max; + me.end = me.min; + } else { + me.start = me.min; + me.end = me.max; + } + if (reverse) { + ticks.reverse(); + } + }, + + convertTicksToLabels: function() { + this.tickValues = this.ticks.slice(); + + core_scale.prototype.convertTicksToLabels.call(this); + }, + + // Get the correct tooltip label + getLabelForIndex: function(index, datasetIndex) { + return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); + }, + + getPixelForTick: function(index) { + return this.getPixelForValue(this.tickValues[index]); + }, + + /** + * Returns the value of the first tick. + * @param {number} value - The minimum not zero value. + * @return {number} The first tick value. + * @private + */ + _getFirstTickValue: function(value) { + var exp = Math.floor(helpers$1.log10(value)); + var significand = Math.floor(value / Math.pow(10, exp)); + + return significand * Math.pow(10, exp); + }, + + getPixelForValue: function(value) { + var me = this; + var tickOpts = me.options.ticks; + var reverse = tickOpts.reverse; + var log10 = helpers$1.log10; + var firstTickValue = me._getFirstTickValue(me.minNotZero); + var offset = 0; + var innerDimension, pixel, start, end, sign; + + value = +me.getRightValue(value); + if (reverse) { + start = me.end; + end = me.start; + sign = -1; + } else { + start = me.start; + end = me.end; + sign = 1; + } + if (me.isHorizontal()) { + innerDimension = me.width; + pixel = reverse ? me.right : me.left; + } else { + innerDimension = me.height; + sign *= -1; // invert, since the upper-left corner of the canvas is at pixel (0, 0) + pixel = reverse ? me.top : me.bottom; + } + if (value !== start) { + if (start === 0) { // include zero tick + offset = valueOrDefault$a(tickOpts.fontSize, core_defaults.global.defaultFontSize); + innerDimension -= offset; + start = firstTickValue; + } + if (value !== 0) { + offset += innerDimension / (log10(end) - log10(start)) * (log10(value) - log10(start)); + } + pixel += sign * offset; + } + return pixel; + }, + + getValueForPixel: function(pixel) { + var me = this; + var tickOpts = me.options.ticks; + var reverse = tickOpts.reverse; + var log10 = helpers$1.log10; + var firstTickValue = me._getFirstTickValue(me.minNotZero); + var innerDimension, start, end, value; + + if (reverse) { + start = me.end; + end = me.start; + } else { + start = me.start; + end = me.end; + } + if (me.isHorizontal()) { + innerDimension = me.width; + value = reverse ? me.right - pixel : pixel - me.left; + } else { + innerDimension = me.height; + value = reverse ? pixel - me.top : me.bottom - pixel; + } + if (value !== start) { + if (start === 0) { // include zero tick + var offset = valueOrDefault$a(tickOpts.fontSize, core_defaults.global.defaultFontSize); + value -= offset; + innerDimension -= offset; + start = firstTickValue; + } + value *= log10(end) - log10(start); + value /= innerDimension; + value = Math.pow(10, log10(start) + value); + } + return value; + } +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults$2 = defaultConfig$2; +scale_logarithmic._defaults = _defaults$2; + +var valueOrDefault$b = helpers$1.valueOrDefault; +var valueAtIndexOrDefault$1 = helpers$1.valueAtIndexOrDefault; +var resolve$7 = helpers$1.options.resolve; + +var defaultConfig$3 = { + display: true, + + // Boolean - Whether to animate scaling the chart from the centre + animate: true, + position: 'chartArea', + + angleLines: { + display: true, + color: 'rgba(0, 0, 0, 0.1)', + lineWidth: 1, + borderDash: [], + borderDashOffset: 0.0 + }, + + gridLines: { + circular: false + }, + + // label settings + ticks: { + // Boolean - Show a backdrop to the scale label + showLabelBackdrop: true, + + // String - The colour of the label backdrop + backdropColor: 'rgba(255,255,255,0.75)', + + // Number - The backdrop padding above & below the label in pixels + backdropPaddingY: 2, + + // Number - The backdrop padding to the side of the label in pixels + backdropPaddingX: 2, + + callback: core_ticks.formatters.linear + }, + + pointLabels: { + // Boolean - if true, show point labels + display: true, + + // Number - Point label font size in pixels + fontSize: 10, + + // Function - Used to convert point labels + callback: function(label) { + return label; + } + } +}; + +function getValueCount(scale) { + var opts = scale.options; + return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0; +} + +function getTickBackdropHeight(opts) { + var tickOpts = opts.ticks; + + if (tickOpts.display && opts.display) { + return valueOrDefault$b(tickOpts.fontSize, core_defaults.global.defaultFontSize) + tickOpts.backdropPaddingY * 2; + } + return 0; +} + +function measureLabelSize(ctx, lineHeight, label) { + if (helpers$1.isArray(label)) { + return { + w: helpers$1.longestText(ctx, ctx.font, label), + h: label.length * lineHeight + }; + } + + return { + w: ctx.measureText(label).width, + h: lineHeight + }; +} + +function determineLimits(angle, pos, size, min, max) { + if (angle === min || angle === max) { + return { + start: pos - (size / 2), + end: pos + (size / 2) + }; + } else if (angle < min || angle > max) { + return { + start: pos - size, + end: pos + }; + } + + return { + start: pos, + end: pos + size + }; +} + +/** + * Helper function to fit a radial linear scale with point labels + */ +function fitWithPointLabels(scale) { + + // Right, this is really confusing and there is a lot of maths going on here + // The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 + // + // Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif + // + // Solution: + // + // We assume the radius of the polygon is half the size of the canvas at first + // at each index we check if the text overlaps. + // + // Where it does, we store that angle and that index. + // + // After finding the largest index and angle we calculate how much we need to remove + // from the shape radius to move the point inwards by that x. + // + // We average the left and right distances to get the maximum shape radius that can fit in the box + // along with labels. + // + // Once we have that, we can find the centre point for the chart, by taking the x text protrusion + // on each side, removing that from the size, halving it and adding the left x protrusion width. + // + // This will mean we have a shape fitted to the canvas, as large as it can be with the labels + // and position it in the most space efficient manner + // + // https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif + + var plFont = helpers$1.options._parseFont(scale.options.pointLabels); + + // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. + // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points + var furthestLimits = { + l: 0, + r: scale.width, + t: 0, + b: scale.height - scale.paddingTop + }; + var furthestAngles = {}; + var i, textSize, pointPosition; + + scale.ctx.font = plFont.string; + scale._pointLabelSizes = []; + + var valueCount = getValueCount(scale); + for (i = 0; i < valueCount; i++) { + pointPosition = scale.getPointPosition(i, scale.drawingArea + 5); + textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale.pointLabels[i] || ''); + scale._pointLabelSizes[i] = textSize; + + // Add quarter circle to make degree 0 mean top of circle + var angleRadians = scale.getIndexAngle(i); + var angle = helpers$1.toDegrees(angleRadians) % 360; + var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); + var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); + + if (hLimits.start < furthestLimits.l) { + furthestLimits.l = hLimits.start; + furthestAngles.l = angleRadians; + } + + if (hLimits.end > furthestLimits.r) { + furthestLimits.r = hLimits.end; + furthestAngles.r = angleRadians; + } + + if (vLimits.start < furthestLimits.t) { + furthestLimits.t = vLimits.start; + furthestAngles.t = angleRadians; + } + + if (vLimits.end > furthestLimits.b) { + furthestLimits.b = vLimits.end; + furthestAngles.b = angleRadians; + } + } + + scale.setReductions(scale.drawingArea, furthestLimits, furthestAngles); +} + +function getTextAlignForAngle(angle) { + if (angle === 0 || angle === 180) { + return 'center'; + } else if (angle < 180) { + return 'left'; + } + + return 'right'; +} + +function fillText(ctx, text, position, lineHeight) { + var y = position.y + lineHeight / 2; + var i, ilen; + + if (helpers$1.isArray(text)) { + for (i = 0, ilen = text.length; i < ilen; ++i) { + ctx.fillText(text[i], position.x, y); + y += lineHeight; + } + } else { + ctx.fillText(text, position.x, y); + } +} + +function adjustPointPositionForLabelHeight(angle, textSize, position) { + if (angle === 90 || angle === 270) { + position.y -= (textSize.h / 2); + } else if (angle > 270 || angle < 90) { + position.y -= textSize.h; + } +} + +function drawPointLabels(scale) { + var ctx = scale.ctx; + var opts = scale.options; + var angleLineOpts = opts.angleLines; + var gridLineOpts = opts.gridLines; + var pointLabelOpts = opts.pointLabels; + var lineWidth = valueOrDefault$b(angleLineOpts.lineWidth, gridLineOpts.lineWidth); + var lineColor = valueOrDefault$b(angleLineOpts.color, gridLineOpts.color); + var tickBackdropHeight = getTickBackdropHeight(opts); + + ctx.save(); + ctx.lineWidth = lineWidth; + ctx.strokeStyle = lineColor; + if (ctx.setLineDash) { + ctx.setLineDash(resolve$7([angleLineOpts.borderDash, gridLineOpts.borderDash, []])); + ctx.lineDashOffset = resolve$7([angleLineOpts.borderDashOffset, gridLineOpts.borderDashOffset, 0.0]); + } + + var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); + + // Point Label Font + var plFont = helpers$1.options._parseFont(pointLabelOpts); + + ctx.font = plFont.string; + ctx.textBaseline = 'middle'; + + for (var i = getValueCount(scale) - 1; i >= 0; i--) { + if (angleLineOpts.display && lineWidth && lineColor) { + var outerPosition = scale.getPointPosition(i, outerDistance); + ctx.beginPath(); + ctx.moveTo(scale.xCenter, scale.yCenter); + ctx.lineTo(outerPosition.x, outerPosition.y); + ctx.stroke(); + } + + if (pointLabelOpts.display) { + // Extra pixels out for some label spacing + var extra = (i === 0 ? tickBackdropHeight / 2 : 0); + var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5); + + // Keep this in loop since we may support array properties here + var pointLabelFontColor = valueAtIndexOrDefault$1(pointLabelOpts.fontColor, i, core_defaults.global.defaultFontColor); + ctx.fillStyle = pointLabelFontColor; + + var angleRadians = scale.getIndexAngle(i); + var angle = helpers$1.toDegrees(angleRadians); + ctx.textAlign = getTextAlignForAngle(angle); + adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); + fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.lineHeight); + } + } + ctx.restore(); +} + +function drawRadiusLine(scale, gridLineOpts, radius, index) { + var ctx = scale.ctx; + var circular = gridLineOpts.circular; + var valueCount = getValueCount(scale); + var lineColor = valueAtIndexOrDefault$1(gridLineOpts.color, index - 1); + var lineWidth = valueAtIndexOrDefault$1(gridLineOpts.lineWidth, index - 1); + var pointPosition; + + if ((!circular && !valueCount) || !lineColor || !lineWidth) { + return; + } + + ctx.save(); + ctx.strokeStyle = lineColor; + ctx.lineWidth = lineWidth; + if (ctx.setLineDash) { + ctx.setLineDash(gridLineOpts.borderDash || []); + ctx.lineDashOffset = gridLineOpts.borderDashOffset || 0.0; + } + + ctx.beginPath(); + if (circular) { + // Draw circular arcs between the points + ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2); + } else { + // Draw straight lines connecting each index + pointPosition = scale.getPointPosition(0, radius); + ctx.moveTo(pointPosition.x, pointPosition.y); + + for (var i = 1; i < valueCount; i++) { + pointPosition = scale.getPointPosition(i, radius); + ctx.lineTo(pointPosition.x, pointPosition.y); + } + } + ctx.closePath(); + ctx.stroke(); + ctx.restore(); +} + +function numberOrZero(param) { + return helpers$1.isNumber(param) ? param : 0; +} + +var scale_radialLinear = scale_linearbase.extend({ + setDimensions: function() { + var me = this; + + // Set the unconstrained dimension before label rotation + me.width = me.maxWidth; + me.height = me.maxHeight; + me.paddingTop = getTickBackdropHeight(me.options) / 2; + me.xCenter = Math.floor(me.width / 2); + me.yCenter = Math.floor((me.height - me.paddingTop) / 2); + me.drawingArea = Math.min(me.height - me.paddingTop, me.width) / 2; + }, + + determineDataLimits: function() { + var me = this; + var chart = me.chart; + var min = Number.POSITIVE_INFINITY; + var max = Number.NEGATIVE_INFINITY; + + helpers$1.each(chart.data.datasets, function(dataset, datasetIndex) { + if (chart.isDatasetVisible(datasetIndex)) { + var meta = chart.getDatasetMeta(datasetIndex); + + helpers$1.each(dataset.data, function(rawValue, index) { + var value = +me.getRightValue(rawValue); + if (isNaN(value) || meta.data[index].hidden) { + return; + } + + min = Math.min(value, min); + max = Math.max(value, max); + }); + } + }); + + me.min = (min === Number.POSITIVE_INFINITY ? 0 : min); + me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max); + + // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero + me.handleTickRangeOptions(); + }, + + // Returns the maximum number of ticks based on the scale dimension + _computeTickLimit: function() { + return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options)); + }, + + convertTicksToLabels: function() { + var me = this; + + scale_linearbase.prototype.convertTicksToLabels.call(me); + + // Point labels + me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me); + }, + + getLabelForIndex: function(index, datasetIndex) { + return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); + }, + + fit: function() { + var me = this; + var opts = me.options; + + if (opts.display && opts.pointLabels.display) { + fitWithPointLabels(me); + } else { + me.setCenterPoint(0, 0, 0, 0); + } + }, + + /** + * Set radius reductions and determine new radius and center point + * @private + */ + setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) { + var me = this; + var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l); + var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r); + var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t); + var radiusReductionBottom = -Math.max(furthestLimits.b - (me.height - me.paddingTop), 0) / Math.cos(furthestAngles.b); + + radiusReductionLeft = numberOrZero(radiusReductionLeft); + radiusReductionRight = numberOrZero(radiusReductionRight); + radiusReductionTop = numberOrZero(radiusReductionTop); + radiusReductionBottom = numberOrZero(radiusReductionBottom); + + me.drawingArea = Math.min( + Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2), + Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)); + me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom); + }, + + setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) { + var me = this; + var maxRight = me.width - rightMovement - me.drawingArea; + var maxLeft = leftMovement + me.drawingArea; + var maxTop = topMovement + me.drawingArea; + var maxBottom = (me.height - me.paddingTop) - bottomMovement - me.drawingArea; + + me.xCenter = Math.floor(((maxLeft + maxRight) / 2) + me.left); + me.yCenter = Math.floor(((maxTop + maxBottom) / 2) + me.top + me.paddingTop); + }, + + getIndexAngle: function(index) { + var angleMultiplier = (Math.PI * 2) / getValueCount(this); + var startAngle = this.chart.options && this.chart.options.startAngle ? + this.chart.options.startAngle : + 0; + + var startAngleRadians = startAngle * Math.PI * 2 / 360; + + // Start from the top instead of right, so remove a quarter of the circle + return index * angleMultiplier + startAngleRadians; + }, + + getDistanceFromCenterForValue: function(value) { + var me = this; + + if (value === null) { + return 0; // null always in center + } + + // Take into account half font size + the yPadding of the top value + var scalingFactor = me.drawingArea / (me.max - me.min); + if (me.options.ticks.reverse) { + return (me.max - value) * scalingFactor; + } + return (value - me.min) * scalingFactor; + }, + + getPointPosition: function(index, distanceFromCenter) { + var me = this; + var thisAngle = me.getIndexAngle(index) - (Math.PI / 2); + return { + x: Math.cos(thisAngle) * distanceFromCenter + me.xCenter, + y: Math.sin(thisAngle) * distanceFromCenter + me.yCenter + }; + }, + + getPointPositionForValue: function(index, value) { + return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); + }, + + getBasePosition: function() { + var me = this; + var min = me.min; + var max = me.max; + + return me.getPointPositionForValue(0, + me.beginAtZero ? 0 : + min < 0 && max < 0 ? max : + min > 0 && max > 0 ? min : + 0); + }, + + draw: function() { + var me = this; + var opts = me.options; + var gridLineOpts = opts.gridLines; + var tickOpts = opts.ticks; + + if (opts.display) { + var ctx = me.ctx; + var startAngle = this.getIndexAngle(0); + var tickFont = helpers$1.options._parseFont(tickOpts); + + if (opts.angleLines.display || opts.pointLabels.display) { + drawPointLabels(me); + } + + helpers$1.each(me.ticks, function(label, index) { + // Don't draw a centre value (if it is minimum) + if (index > 0 || tickOpts.reverse) { + var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); + + // Draw circular lines around the scale + if (gridLineOpts.display && index !== 0) { + drawRadiusLine(me, gridLineOpts, yCenterOffset, index); + } + + if (tickOpts.display) { + var tickFontColor = valueOrDefault$b(tickOpts.fontColor, core_defaults.global.defaultFontColor); + ctx.font = tickFont.string; + + ctx.save(); + ctx.translate(me.xCenter, me.yCenter); + ctx.rotate(startAngle); + + if (tickOpts.showLabelBackdrop) { + var labelWidth = ctx.measureText(label).width; + ctx.fillStyle = tickOpts.backdropColor; + ctx.fillRect( + -labelWidth / 2 - tickOpts.backdropPaddingX, + -yCenterOffset - tickFont.size / 2 - tickOpts.backdropPaddingY, + labelWidth + tickOpts.backdropPaddingX * 2, + tickFont.size + tickOpts.backdropPaddingY * 2 + ); + } + + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = tickFontColor; + ctx.fillText(label, 0, -yCenterOffset); + ctx.restore(); + } + } + }); + } + } +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults$3 = defaultConfig$3; +scale_radialLinear._defaults = _defaults$3; + +var valueOrDefault$c = helpers$1.valueOrDefault; + +// Integer constants are from the ES6 spec. +var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; +var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; + +var INTERVALS = { + millisecond: { + common: true, + size: 1, + steps: [1, 2, 5, 10, 20, 50, 100, 250, 500] + }, + second: { + common: true, + size: 1000, + steps: [1, 2, 5, 10, 15, 30] + }, + minute: { + common: true, + size: 60000, + steps: [1, 2, 5, 10, 15, 30] + }, + hour: { + common: true, + size: 3600000, + steps: [1, 2, 3, 6, 12] + }, + day: { + common: true, + size: 86400000, + steps: [1, 2, 5] + }, + week: { + common: false, + size: 604800000, + steps: [1, 2, 3, 4] + }, + month: { + common: true, + size: 2.628e9, + steps: [1, 2, 3] + }, + quarter: { + common: false, + size: 7.884e9, + steps: [1, 2, 3, 4] + }, + year: { + common: true, + size: 3.154e10 + } +}; + +var UNITS = Object.keys(INTERVALS); + +function sorter(a, b) { + return a - b; +} + +function arrayUnique(items) { + var hash = {}; + var out = []; + var i, ilen, item; + + for (i = 0, ilen = items.length; i < ilen; ++i) { + item = items[i]; + if (!hash[item]) { + hash[item] = true; + out.push(item); + } + } + + return out; +} + +/** + * Returns an array of {time, pos} objects used to interpolate a specific `time` or position + * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is + * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other + * extremity (left + width or top + height). Note that it would be more optimized to directly + * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need + * to create the lookup table. The table ALWAYS contains at least two items: min and max. + * + * @param {number[]} timestamps - timestamps sorted from lowest to highest. + * @param {string} distribution - If 'linear', timestamps will be spread linearly along the min + * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}. + * If 'series', timestamps will be positioned at the same distance from each other. In this + * case, only timestamps that break the time linearity are registered, meaning that in the + * best case, all timestamps are linear, the table contains only min and max. + */ +function buildLookupTable(timestamps, min, max, distribution) { + if (distribution === 'linear' || !timestamps.length) { + return [ + {time: min, pos: 0}, + {time: max, pos: 1} + ]; + } + + var table = []; + var items = [min]; + var i, ilen, prev, curr, next; + + for (i = 0, ilen = timestamps.length; i < ilen; ++i) { + curr = timestamps[i]; + if (curr > min && curr < max) { + items.push(curr); + } + } + + items.push(max); + + for (i = 0, ilen = items.length; i < ilen; ++i) { + next = items[i + 1]; + prev = items[i - 1]; + curr = items[i]; + + // only add points that breaks the scale linearity + if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) { + table.push({time: curr, pos: i / (ilen - 1)}); + } + } + + return table; +} + +// @see adapted from https://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/ +function lookup(table, key, value) { + var lo = 0; + var hi = table.length - 1; + var mid, i0, i1; + + while (lo >= 0 && lo <= hi) { + mid = (lo + hi) >> 1; + i0 = table[mid - 1] || null; + i1 = table[mid]; + + if (!i0) { + // given value is outside table (before first item) + return {lo: null, hi: i1}; + } else if (i1[key] < value) { + lo = mid + 1; + } else if (i0[key] > value) { + hi = mid - 1; + } else { + return {lo: i0, hi: i1}; + } + } + + // given value is outside table (after last item) + return {lo: i1, hi: null}; +} + +/** + * Linearly interpolates the given source `value` using the table items `skey` values and + * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos') + * returns the position for a timestamp equal to 42. If value is out of bounds, values at + * index [0, 1] or [n - 1, n] are used for the interpolation. + */ +function interpolate$1(table, skey, sval, tkey) { + var range = lookup(table, skey, sval); + + // Note: the lookup table ALWAYS contains at least 2 items (min and max) + var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo; + var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi; + + var span = next[skey] - prev[skey]; + var ratio = span ? (sval - prev[skey]) / span : 0; + var offset = (next[tkey] - prev[tkey]) * ratio; + + return prev[tkey] + offset; +} + +function toTimestamp(scale, input) { + var adapter = scale._adapter; + var options = scale.options.time; + var parser = options.parser; + var format = parser || options.format; + var value = input; + + if (typeof parser === 'function') { + value = parser(value); + } + + // Only parse if its not a timestamp already + if (!helpers$1.isFinite(value)) { + value = typeof format === 'string' + ? adapter.parse(value, format) + : adapter.parse(value); + } + + if (value !== null) { + return +value; + } + + // Labels are in an incompatible format and no `parser` has been provided. + // The user might still use the deprecated `format` option for parsing. + if (!parser && typeof format === 'function') { + value = format(input); + + // `format` could return something else than a timestamp, if so, parse it + if (!helpers$1.isFinite(value)) { + value = adapter.parse(value); + } + } + + return value; +} + +function parse(scale, input) { + if (helpers$1.isNullOrUndef(input)) { + return null; + } + + var options = scale.options.time; + var value = toTimestamp(scale, scale.getRightValue(input)); + if (value === null) { + return value; + } + + if (options.round) { + value = +scale._adapter.startOf(value, options.round); + } + + return value; +} + +/** + * Returns the number of unit to skip to be able to display up to `capacity` number of ticks + * in `unit` for the given `min` / `max` range and respecting the interval steps constraints. + */ +function determineStepSize(min, max, unit, capacity) { + var range = max - min; + var interval = INTERVALS[unit]; + var milliseconds = interval.size; + var steps = interval.steps; + var i, ilen, factor; + + if (!steps) { + return Math.ceil(range / (capacity * milliseconds)); + } + + for (i = 0, ilen = steps.length; i < ilen; ++i) { + factor = steps[i]; + if (Math.ceil(range / (milliseconds * factor)) <= capacity) { + break; + } + } + + return factor; +} + +/** + * Figures out what unit results in an appropriate number of auto-generated ticks + */ +function determineUnitForAutoTicks(minUnit, min, max, capacity) { + var ilen = UNITS.length; + var i, interval, factor; + + for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { + interval = INTERVALS[UNITS[i]]; + factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER; + + if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { + return UNITS[i]; + } + } + + return UNITS[ilen - 1]; +} + +/** + * Figures out what unit to format a set of ticks with + */ +function determineUnitForFormatting(scale, ticks, minUnit, min, max) { + var ilen = UNITS.length; + var i, unit; + + for (i = ilen - 1; i >= UNITS.indexOf(minUnit); i--) { + unit = UNITS[i]; + if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= ticks.length) { + return unit; + } + } + + return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; +} + +function determineMajorUnit(unit) { + for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { + if (INTERVALS[UNITS[i]].common) { + return UNITS[i]; + } + } +} + +/** + * Generates a maximum of `capacity` timestamps between min and max, rounded to the + * `minor` unit, aligned on the `major` unit and using the given scale time `options`. + * Important: this method can return ticks outside the min and max range, it's the + * responsibility of the calling code to clamp values if needed. + */ +function generate(scale, min, max, capacity) { + var adapter = scale._adapter; + var options = scale.options; + var timeOpts = options.time; + var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity); + var major = determineMajorUnit(minor); + var stepSize = valueOrDefault$c(timeOpts.stepSize, timeOpts.unitStepSize); + var weekday = minor === 'week' ? timeOpts.isoWeekday : false; + var majorTicksEnabled = options.ticks.major.enabled; + var interval = INTERVALS[minor]; + var first = min; + var last = max; + var ticks = []; + var time; + + if (!stepSize) { + stepSize = determineStepSize(min, max, minor, capacity); + } + + // For 'week' unit, handle the first day of week option + if (weekday) { + first = +adapter.startOf(first, 'isoWeek', weekday); + last = +adapter.startOf(last, 'isoWeek', weekday); + } + + // Align first/last ticks on unit + first = +adapter.startOf(first, weekday ? 'day' : minor); + last = +adapter.startOf(last, weekday ? 'day' : minor); + + // Make sure that the last tick include max + if (last < max) { + last = +adapter.add(last, 1, minor); + } + + time = first; + + if (majorTicksEnabled && major && !weekday && !timeOpts.round) { + // Align the first tick on the previous `minor` unit aligned on the `major` unit: + // we first aligned time on the previous `major` unit then add the number of full + // stepSize there is between first and the previous major time. + time = +adapter.startOf(time, major); + time = +adapter.add(time, ~~((first - time) / (interval.size * stepSize)) * stepSize, minor); + } + + for (; time < last; time = +adapter.add(time, stepSize, minor)) { + ticks.push(+time); + } + + ticks.push(+time); + + return ticks; +} + +/** + * Returns the start and end offsets from edges in the form of {start, end} + * where each value is a relative width to the scale and ranges between 0 and 1. + * They add extra margins on the both sides by scaling down the original scale. + * Offsets are added when the `offset` option is true. + */ +function computeOffsets(table, ticks, min, max, options) { + var start = 0; + var end = 0; + var first, last; + + if (options.offset && ticks.length) { + if (!options.time.min) { + first = interpolate$1(table, 'time', ticks[0], 'pos'); + if (ticks.length === 1) { + start = 1 - first; + } else { + start = (interpolate$1(table, 'time', ticks[1], 'pos') - first) / 2; + } + } + if (!options.time.max) { + last = interpolate$1(table, 'time', ticks[ticks.length - 1], 'pos'); + if (ticks.length === 1) { + end = last; + } else { + end = (last - interpolate$1(table, 'time', ticks[ticks.length - 2], 'pos')) / 2; + } + } + } + + return {start: start, end: end}; +} + +function ticksFromTimestamps(scale, values, majorUnit) { + var ticks = []; + var i, ilen, value, major; + + for (i = 0, ilen = values.length; i < ilen; ++i) { + value = values[i]; + major = majorUnit ? value === +scale._adapter.startOf(value, majorUnit) : false; + + ticks.push({ + value: value, + major: major + }); + } + + return ticks; +} + +var defaultConfig$4 = { + position: 'bottom', + + /** + * Data distribution along the scale: + * - 'linear': data are spread according to their time (distances can vary), + * - 'series': data are spread at the same distance from each other. + * @see https://github.com/chartjs/Chart.js/pull/4507 + * @since 2.7.0 + */ + distribution: 'linear', + + /** + * Scale boundary strategy (bypassed by min/max time options) + * - `data`: make sure data are fully visible, ticks outside are removed + * - `ticks`: make sure ticks are fully visible, data outside are truncated + * @see https://github.com/chartjs/Chart.js/pull/4556 + * @since 2.7.0 + */ + bounds: 'data', + + adapters: {}, + time: { + parser: false, // false == a pattern string from https://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment + format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from https://momentjs.com/docs/#/parsing/string-format/ + unit: false, // false == automatic or override with week, month, year, etc. + round: false, // none, or override with week, month, year, etc. + displayFormat: false, // DEPRECATED + isoWeekday: false, // override week start day - see https://momentjs.com/docs/#/get-set/iso-weekday/ + minUnit: 'millisecond', + displayFormats: {} + }, + ticks: { + autoSkip: false, + + /** + * Ticks generation input values: + * - 'auto': generates "optimal" ticks based on scale size and time options. + * - 'data': generates ticks from data (including labels from data {t|x|y} objects). + * - 'labels': generates ticks from user given `data.labels` values ONLY. + * @see https://github.com/chartjs/Chart.js/pull/4507 + * @since 2.7.0 + */ + source: 'auto', + + major: { + enabled: false + } + } +}; + +var scale_time = core_scale.extend({ + initialize: function() { + this.mergeTicksOptions(); + core_scale.prototype.initialize.call(this); + }, + + update: function() { + var me = this; + var options = me.options; + var time = options.time || (options.time = {}); + var adapter = me._adapter = new core_adapters._date(options.adapters.date); + + // DEPRECATIONS: output a message only one time per update + if (time.format) { + console.warn('options.time.format is deprecated and replaced by options.time.parser.'); + } + + // Backward compatibility: before introducing adapter, `displayFormats` was + // supposed to contain *all* unit/string pairs but this can't be resolved + // when loading the scale (adapters are loaded afterward), so let's populate + // missing formats on update + helpers$1.mergeIf(time.displayFormats, adapter.formats()); + + return core_scale.prototype.update.apply(me, arguments); + }, + + /** + * Allows data to be referenced via 't' attribute + */ + getRightValue: function(rawValue) { + if (rawValue && rawValue.t !== undefined) { + rawValue = rawValue.t; + } + return core_scale.prototype.getRightValue.call(this, rawValue); + }, + + determineDataLimits: function() { + var me = this; + var chart = me.chart; + var adapter = me._adapter; + var timeOpts = me.options.time; + var unit = timeOpts.unit || 'day'; + var min = MAX_INTEGER; + var max = MIN_INTEGER; + var timestamps = []; + var datasets = []; + var labels = []; + var i, j, ilen, jlen, data, timestamp; + var dataLabels = chart.data.labels || []; + + // Convert labels to timestamps + for (i = 0, ilen = dataLabels.length; i < ilen; ++i) { + labels.push(parse(me, dataLabels[i])); + } + + // Convert data to timestamps + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + if (chart.isDatasetVisible(i)) { + data = chart.data.datasets[i].data; + + // Let's consider that all data have the same format. + if (helpers$1.isObject(data[0])) { + datasets[i] = []; + + for (j = 0, jlen = data.length; j < jlen; ++j) { + timestamp = parse(me, data[j]); + timestamps.push(timestamp); + datasets[i][j] = timestamp; + } + } else { + for (j = 0, jlen = labels.length; j < jlen; ++j) { + timestamps.push(labels[j]); + } + datasets[i] = labels.slice(0); + } + } else { + datasets[i] = []; + } + } + + if (labels.length) { + // Sort labels **after** data have been converted + labels = arrayUnique(labels).sort(sorter); + min = Math.min(min, labels[0]); + max = Math.max(max, labels[labels.length - 1]); + } + + if (timestamps.length) { + timestamps = arrayUnique(timestamps).sort(sorter); + min = Math.min(min, timestamps[0]); + max = Math.max(max, timestamps[timestamps.length - 1]); + } + + min = parse(me, timeOpts.min) || min; + max = parse(me, timeOpts.max) || max; + + // In case there is no valid min/max, set limits based on unit time option + min = min === MAX_INTEGER ? +adapter.startOf(Date.now(), unit) : min; + max = max === MIN_INTEGER ? +adapter.endOf(Date.now(), unit) + 1 : max; + + // Make sure that max is strictly higher than min (required by the lookup table) + me.min = Math.min(min, max); + me.max = Math.max(min + 1, max); + + // PRIVATE + me._horizontal = me.isHorizontal(); + me._table = []; + me._timestamps = { + data: timestamps, + datasets: datasets, + labels: labels + }; + }, + + buildTicks: function() { + var me = this; + var min = me.min; + var max = me.max; + var options = me.options; + var timeOpts = options.time; + var timestamps = []; + var ticks = []; + var i, ilen, timestamp; + + switch (options.ticks.source) { + case 'data': + timestamps = me._timestamps.data; + break; + case 'labels': + timestamps = me._timestamps.labels; + break; + case 'auto': + default: + timestamps = generate(me, min, max, me.getLabelCapacity(min), options); + } + + if (options.bounds === 'ticks' && timestamps.length) { + min = timestamps[0]; + max = timestamps[timestamps.length - 1]; + } + + // Enforce limits with user min/max options + min = parse(me, timeOpts.min) || min; + max = parse(me, timeOpts.max) || max; + + // Remove ticks outside the min/max range + for (i = 0, ilen = timestamps.length; i < ilen; ++i) { + timestamp = timestamps[i]; + if (timestamp >= min && timestamp <= max) { + ticks.push(timestamp); + } + } + + me.min = min; + me.max = max; + + // PRIVATE + me._unit = timeOpts.unit || determineUnitForFormatting(me, ticks, timeOpts.minUnit, me.min, me.max); + me._majorUnit = determineMajorUnit(me._unit); + me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution); + me._offsets = computeOffsets(me._table, ticks, min, max, options); + + if (options.ticks.reverse) { + ticks.reverse(); + } + + return ticksFromTimestamps(me, ticks, me._majorUnit); + }, + + getLabelForIndex: function(index, datasetIndex) { + var me = this; + var adapter = me._adapter; + var data = me.chart.data; + var timeOpts = me.options.time; + var label = data.labels && index < data.labels.length ? data.labels[index] : ''; + var value = data.datasets[datasetIndex].data[index]; + + if (helpers$1.isObject(value)) { + label = me.getRightValue(value); + } + if (timeOpts.tooltipFormat) { + return adapter.format(toTimestamp(me, label), timeOpts.tooltipFormat); + } + if (typeof label === 'string') { + return label; + } + return adapter.format(toTimestamp(me, label), timeOpts.displayFormats.datetime); + }, + + /** + * Function to format an individual tick mark + * @private + */ + tickFormatFunction: function(time, index, ticks, format) { + var me = this; + var adapter = me._adapter; + var options = me.options; + var formats = options.time.displayFormats; + var minorFormat = formats[me._unit]; + var majorUnit = me._majorUnit; + var majorFormat = formats[majorUnit]; + var majorTime = +adapter.startOf(time, majorUnit); + var majorTickOpts = options.ticks.major; + var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime; + var label = adapter.format(time, format ? format : major ? majorFormat : minorFormat); + var tickOpts = major ? majorTickOpts : options.ticks.minor; + var formatter = valueOrDefault$c(tickOpts.callback, tickOpts.userCallback); + + return formatter ? formatter(label, index, ticks) : label; + }, + + convertTicksToLabels: function(ticks) { + var labels = []; + var i, ilen; + + for (i = 0, ilen = ticks.length; i < ilen; ++i) { + labels.push(this.tickFormatFunction(ticks[i].value, i, ticks)); + } + + return labels; + }, + + /** + * @private + */ + getPixelForOffset: function(time) { + var me = this; + var isReverse = me.options.ticks.reverse; + var size = me._horizontal ? me.width : me.height; + var start = me._horizontal ? isReverse ? me.right : me.left : isReverse ? me.bottom : me.top; + var pos = interpolate$1(me._table, 'time', time, 'pos'); + var offset = size * (me._offsets.start + pos) / (me._offsets.start + 1 + me._offsets.end); + + return isReverse ? start - offset : start + offset; + }, + + getPixelForValue: function(value, index, datasetIndex) { + var me = this; + var time = null; + + if (index !== undefined && datasetIndex !== undefined) { + time = me._timestamps.datasets[datasetIndex][index]; + } + + if (time === null) { + time = parse(me, value); + } + + if (time !== null) { + return me.getPixelForOffset(time); + } + }, + + getPixelForTick: function(index) { + var ticks = this.getTicks(); + return index >= 0 && index < ticks.length ? + this.getPixelForOffset(ticks[index].value) : + null; + }, + + getValueForPixel: function(pixel) { + var me = this; + var size = me._horizontal ? me.width : me.height; + var start = me._horizontal ? me.left : me.top; + var pos = (size ? (pixel - start) / size : 0) * (me._offsets.start + 1 + me._offsets.start) - me._offsets.end; + var time = interpolate$1(me._table, 'pos', pos, 'time'); + + // DEPRECATION, we should return time directly + return me._adapter._create(time); + }, + + /** + * Crude approximation of what the label width might be + * @private + */ + getLabelWidth: function(label) { + var me = this; + var ticksOpts = me.options.ticks; + var tickLabelWidth = me.ctx.measureText(label).width; + var angle = helpers$1.toRadians(ticksOpts.maxRotation); + var cosRotation = Math.cos(angle); + var sinRotation = Math.sin(angle); + var tickFontSize = valueOrDefault$c(ticksOpts.fontSize, core_defaults.global.defaultFontSize); + + return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation); + }, + + /** + * @private + */ + getLabelCapacity: function(exampleTime) { + var me = this; + + // pick the longest format (milliseconds) for guestimation + var format = me.options.time.displayFormats.millisecond; + var exampleLabel = me.tickFormatFunction(exampleTime, 0, [], format); + var tickLabelWidth = me.getLabelWidth(exampleLabel); + var innerWidth = me.isHorizontal() ? me.width : me.height; + var capacity = Math.floor(innerWidth / tickLabelWidth); + + return capacity > 0 ? capacity : 1; + } +}); + +// INTERNAL: static default options, registered in src/index.js +var _defaults$4 = defaultConfig$4; +scale_time._defaults = _defaults$4; + +var scales = { + category: scale_category, + linear: scale_linear, + logarithmic: scale_logarithmic, + radialLinear: scale_radialLinear, + time: scale_time +}; + +var moment = createCommonjsModule(function (module, exports) { +(function (global, factory) { + module.exports = factory(); +}(commonjsGlobal, (function () { + 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; + // the date constructor remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0) { + // preserve leap years using a full 400 year cycle, then reset + date = new Date(y + 400, m, d, h, M, s, ms); + if (isFinite(date.getFullYear())) { + date.setFullYear(y); + } + } else { + date = new Date(y, m, d, h, M, s, ms); + } + + return date; + } + + function createUTCDate (y) { + var date; + // the Date.UTC function remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0) { + var args = Array.prototype.slice.call(arguments); + // preserve leap years using a full 400 year cycle, then reset + args[0] = y + 400; + date = new Date(Date.UTC.apply(null, args)); + if (isFinite(date.getUTCFullYear())) { + date.setUTCFullYear(y); + } + } else { + date = new Date(Date.UTC.apply(null, arguments)); + } + + 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 6th 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 + function shiftWeekdays (ws, n) { + return ws.slice(n, 7).concat(ws.slice(0, n)); + } + + var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); + function localeWeekdays (m, format) { + var weekdays = isArray(this._weekdays) ? this._weekdays : + this._weekdays[(m && m !== true && this._weekdays.isFormat.test(format)) ? 'format' : 'standalone']; + return (m === true) ? shiftWeekdays(weekdays, this._week.dow) + : (m) ? weekdays[m.day()] : weekdays; + } + + var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); + function localeWeekdaysShort (m) { + return (m === true) ? shiftWeekdays(this._weekdaysShort, this._week.dow) + : (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort; + } + + var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); + function localeWeekdaysMin (m) { + return (m === true) ? shiftWeekdays(this._weekdaysMin, this._week.dow) + : (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 they want. 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); + + 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 globalLocale; + } + + function loadLocale(name) { + var oldLocale = null; + // TODO: Find a better way to register and load all the locales in Node + if (!locales[name] && ('object' !== 'undefined') && + module && module.exports) { + try { + oldLocale = globalLocale._abbr; + var aliasedRequire = commonjsRequire; + aliasedRequire('./locale/' + 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; + } + else { + if ((typeof console !== 'undefined') && console.warn) { + //warn user if arguments are passed but the locale could not be set + console.warn('Locale ' + key + ' not found. Did you forget to load it?'); + } + } + } + + return globalLocale._abbr; + } + + function defineLocale (name, config) { + if (config !== null) { + var locale, 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 { + locale = loadLocale(config.parentLocale); + if (locale != null) { + parentConfig = locale._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 beginning of week + weekday = w.e + dow; + if (w.e < 0 || w.e > 6) { + weekdayOverflow = true; + } + } else { + // default to beginning 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, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + } + + 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 || normalizedInput.isoWeek || 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 : 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 = {}; + + 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(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(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) { + var localFrom = isMoment(from) ? from : createLocal(from), + localTo = isMoment(to) ? to : createLocal(to); + if (!(this.isValid() && localFrom.isValid() && localTo.isValid())) { + return false; + } + inclusivity = inclusivity || '()'; + return (inclusivity[0] === '(' ? this.isAfter(localFrom, units) : !this.isBefore(localFrom, units)) && + (inclusivity[1] === ')' ? this.isBefore(localTo, units) : !this.isAfter(localTo, 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, + 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.valueOf() + this.utcOffset() * 60 * 1000).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; + } + + var MS_PER_SECOND = 1000; + var MS_PER_MINUTE = 60 * MS_PER_SECOND; + var MS_PER_HOUR = 60 * MS_PER_MINUTE; + var MS_PER_400_YEARS = (365 * 400 + 97) * 24 * MS_PER_HOUR; + + // actual modulo - handles negative numbers (for dates before 1970): + function mod$1(dividend, divisor) { + return (dividend % divisor + divisor) % divisor; + } + + function localStartOfDate(y, m, d) { + // the date constructor remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0) { + // preserve leap years using a full 400 year cycle, then reset + return new Date(y + 400, m, d) - MS_PER_400_YEARS; + } else { + return new Date(y, m, d).valueOf(); + } + } + + function utcStartOfDate(y, m, d) { + // Date.UTC remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0) { + // preserve leap years using a full 400 year cycle, then reset + return Date.UTC(y + 400, m, d) - MS_PER_400_YEARS; + } else { + return Date.UTC(y, m, d); + } + } + + function startOf (units) { + var time; + units = normalizeUnits(units); + if (units === undefined || units === 'millisecond' || !this.isValid()) { + return this; + } + + var startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate; + + switch (units) { + case 'year': + time = startOfDate(this.year(), 0, 1); + break; + case 'quarter': + time = startOfDate(this.year(), this.month() - this.month() % 3, 1); + break; + case 'month': + time = startOfDate(this.year(), this.month(), 1); + break; + case 'week': + time = startOfDate(this.year(), this.month(), this.date() - this.weekday()); + break; + case 'isoWeek': + time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1)); + break; + case 'day': + case 'date': + time = startOfDate(this.year(), this.month(), this.date()); + break; + case 'hour': + time = this._d.valueOf(); + time -= mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR); + break; + case 'minute': + time = this._d.valueOf(); + time -= mod$1(time, MS_PER_MINUTE); + break; + case 'second': + time = this._d.valueOf(); + time -= mod$1(time, MS_PER_SECOND); + break; + } + + this._d.setTime(time); + hooks.updateOffset(this, true); + return this; + } + + function endOf (units) { + var time; + units = normalizeUnits(units); + if (units === undefined || units === 'millisecond' || !this.isValid()) { + return this; + } + + var startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate; + + switch (units) { + case 'year': + time = startOfDate(this.year() + 1, 0, 1) - 1; + break; + case 'quarter': + time = startOfDate(this.year(), this.month() - this.month() % 3 + 3, 1) - 1; + break; + case 'month': + time = startOfDate(this.year(), this.month() + 1, 1) - 1; + break; + case 'week': + time = startOfDate(this.year(), this.month(), this.date() - this.weekday() + 7) - 1; + break; + case 'isoWeek': + time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1) + 7) - 1; + break; + case 'day': + case 'date': + time = startOfDate(this.year(), this.month(), this.date() + 1) - 1; + break; + case 'hour': + time = this._d.valueOf(); + time += MS_PER_HOUR - mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR) - 1; + break; + case 'minute': + time = this._d.valueOf(); + time += MS_PER_MINUTE - mod$1(time, MS_PER_MINUTE) - 1; + break; + case 'second': + time = this._d.valueOf(); + time += MS_PER_SECOND - mod$1(time, MS_PER_SECOND) - 1; + break; + } + + this._d.setTime(time); + hooks.updateOffset(this, true); + return this; + } + + 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'); + + // PRIORITY + 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; + proto.year = getSetYear; + proto.isLeapYear = getIsLeapYear; + proto.weekYear = getSetWeekYear; + proto.isoWeekYear = getSetISOWeekYear; + proto.quarter = proto.quarters = getSetQuarter; + proto.month = getSetMonth; + proto.daysInMonth = getDaysInMonth; + proto.week = proto.weeks = getSetWeek; + proto.isoWeek = proto.isoWeeks = getSetISOWeek; + proto.weeksInYear = getWeeksInYear; + proto.isoWeeksInYear = getISOWeeksInYear; + proto.date = getSetDayOfMonth; + proto.day = proto.days = getSetDayOfWeek; + proto.weekday = getSetLocaleDayOfWeek; + proto.isoWeekday = getSetISODayOfWeek; + proto.dayOfYear = getSetDayOfYear; + proto.hour = proto.hours = getSetHour; + proto.minute = proto.minutes = getSetMinute; + proto.second = proto.seconds = getSetSecond; + proto.millisecond = proto.milliseconds = getSetMillisecond; + 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; + proto.zoneAbbr = getZoneAbbr; + proto.zoneName = getZoneName; + 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; + + proto$1.months = localeMonths; + proto$1.monthsShort = localeMonthsShort; + proto$1.monthsParse = localeMonthsParse; + proto$1.monthsRegex = monthsRegex; + proto$1.monthsShortRegex = monthsShortRegex; + proto$1.week = localeWeek; + proto$1.firstDayOfYear = localeFirstDayOfYear; + proto$1.firstDayOfWeek = localeFirstDayOfWeek; + + 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; + + 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 === 'quarter' || units === 'year') { + days = this._days + milliseconds / 864e5; + months = this._months + daysToMonths(days); + switch (units) { + case 'month': return months; + case 'quarter': return months / 3; + case 'year': return 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 asQuarters = makeAs('Q'); + 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.asQuarters = asQuarters; + 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; + + 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.24.0'; + + 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: 'GGGG-[W]WW', // + MONTH: 'YYYY-MM' // + }; + + return hooks; + +}))); +}); + +var FORMATS = { + datetime: 'MMM D, YYYY, h:mm:ss a', + millisecond: 'h:mm:ss.SSS a', + second: 'h:mm:ss a', + minute: 'h:mm a', + hour: 'hA', + day: 'MMM D', + week: 'll', + month: 'MMM YYYY', + quarter: '[Q]Q - YYYY', + year: 'YYYY' +}; + +core_adapters._date.override(typeof moment === 'function' ? { + _id: 'moment', // DEBUG ONLY + + formats: function() { + return FORMATS; + }, + + parse: function(value, format) { + if (typeof value === 'string' && typeof format === 'string') { + value = moment(value, format); + } else if (!(value instanceof moment)) { + value = moment(value); + } + return value.isValid() ? value.valueOf() : null; + }, + + format: function(time, format) { + return moment(time).format(format); + }, + + add: function(time, amount, unit) { + return moment(time).add(amount, unit).valueOf(); + }, + + diff: function(max, min, unit) { + return moment.duration(moment(max).diff(moment(min))).as(unit); + }, + + startOf: function(time, unit, weekday) { + time = moment(time); + if (unit === 'isoWeek') { + return time.isoWeekday(weekday).valueOf(); + } + return time.startOf(unit).valueOf(); + }, + + endOf: function(time, unit) { + return moment(time).endOf(unit).valueOf(); + }, + + // DEPRECATIONS + + /** + * Provided for backward compatibility with scale.getValueForPixel(). + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ + _create: function(time) { + return moment(time); + }, +} : {}); + +core_defaults._set('global', { + plugins: { + filler: { + propagate: true + } + } +}); + +var mappers = { + dataset: function(source) { + var index = source.fill; + var chart = source.chart; + var meta = chart.getDatasetMeta(index); + var visible = meta && chart.isDatasetVisible(index); + var points = (visible && meta.dataset._children) || []; + var length = points.length || 0; + + return !length ? null : function(point, i) { + return (i < length && points[i]._view) || null; + }; + }, + + boundary: function(source) { + var boundary = source.boundary; + var x = boundary ? boundary.x : null; + var y = boundary ? boundary.y : null; + + return function(point) { + return { + x: x === null ? point.x : x, + y: y === null ? point.y : y, + }; + }; + } +}; + +// @todo if (fill[0] === '#') +function decodeFill(el, index, count) { + var model = el._model || {}; + var fill = model.fill; + var target; + + if (fill === undefined) { + fill = !!model.backgroundColor; + } + + if (fill === false || fill === null) { + return false; + } + + if (fill === true) { + return 'origin'; + } + + target = parseFloat(fill, 10); + if (isFinite(target) && Math.floor(target) === target) { + if (fill[0] === '-' || fill[0] === '+') { + target = index + target; + } + + if (target === index || target < 0 || target >= count) { + return false; + } + + return target; + } + + switch (fill) { + // compatibility + case 'bottom': + return 'start'; + case 'top': + return 'end'; + case 'zero': + return 'origin'; + // supported boundaries + case 'origin': + case 'start': + case 'end': + return fill; + // invalid fill values + default: + return false; + } +} + +function computeBoundary(source) { + var model = source.el._model || {}; + var scale = source.el._scale || {}; + var fill = source.fill; + var target = null; + var horizontal; + + if (isFinite(fill)) { + return null; + } + + // Backward compatibility: until v3, we still need to support boundary values set on + // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and + // controllers might still use it (e.g. the Smith chart). + + if (fill === 'start') { + target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom; + } else if (fill === 'end') { + target = model.scaleTop === undefined ? scale.top : model.scaleTop; + } else if (model.scaleZero !== undefined) { + target = model.scaleZero; + } else if (scale.getBasePosition) { + target = scale.getBasePosition(); + } else if (scale.getBasePixel) { + target = scale.getBasePixel(); + } + + if (target !== undefined && target !== null) { + if (target.x !== undefined && target.y !== undefined) { + return target; + } + + if (helpers$1.isFinite(target)) { + horizontal = scale.isHorizontal(); + return { + x: horizontal ? target : null, + y: horizontal ? null : target + }; + } + } + + return null; +} + +function resolveTarget(sources, index, propagate) { + var source = sources[index]; + var fill = source.fill; + var visited = [index]; + var target; + + if (!propagate) { + return fill; + } + + while (fill !== false && visited.indexOf(fill) === -1) { + if (!isFinite(fill)) { + return fill; + } + + target = sources[fill]; + if (!target) { + return false; + } + + if (target.visible) { + return fill; + } + + visited.push(fill); + fill = target.fill; + } + + return false; +} + +function createMapper(source) { + var fill = source.fill; + var type = 'dataset'; + + if (fill === false) { + return null; + } + + if (!isFinite(fill)) { + type = 'boundary'; + } + + return mappers[type](source); +} + +function isDrawable(point) { + return point && !point.skip; +} + +function drawArea(ctx, curve0, curve1, len0, len1) { + var i; + + if (!len0 || !len1) { + return; + } + + // building first area curve (normal) + ctx.moveTo(curve0[0].x, curve0[0].y); + for (i = 1; i < len0; ++i) { + helpers$1.canvas.lineTo(ctx, curve0[i - 1], curve0[i]); + } + + // joining the two area curves + ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y); + + // building opposite area curve (reverse) + for (i = len1 - 1; i > 0; --i) { + helpers$1.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true); + } +} + +function doFill(ctx, points, mapper, view, color, loop) { + var count = points.length; + var span = view.spanGaps; + var curve0 = []; + var curve1 = []; + var len0 = 0; + var len1 = 0; + var i, ilen, index, p0, p1, d0, d1; + + ctx.beginPath(); + + for (i = 0, ilen = (count + !!loop); i < ilen; ++i) { + index = i % count; + p0 = points[index]._view; + p1 = mapper(p0, index, view); + d0 = isDrawable(p0); + d1 = isDrawable(p1); + + if (d0 && d1) { + len0 = curve0.push(p0); + len1 = curve1.push(p1); + } else if (len0 && len1) { + if (!span) { + drawArea(ctx, curve0, curve1, len0, len1); + len0 = len1 = 0; + curve0 = []; + curve1 = []; + } else { + if (d0) { + curve0.push(p0); + } + if (d1) { + curve1.push(p1); + } + } + } + } + + drawArea(ctx, curve0, curve1, len0, len1); + + ctx.closePath(); + ctx.fillStyle = color; + ctx.fill(); +} + +var plugin_filler = { + id: 'filler', + + afterDatasetsUpdate: function(chart, options) { + var count = (chart.data.datasets || []).length; + var propagate = options.propagate; + var sources = []; + var meta, i, el, source; + + for (i = 0; i < count; ++i) { + meta = chart.getDatasetMeta(i); + el = meta.dataset; + source = null; + + if (el && el._model && el instanceof elements.Line) { + source = { + visible: chart.isDatasetVisible(i), + fill: decodeFill(el, i, count), + chart: chart, + el: el + }; + } + + meta.$filler = source; + sources.push(source); + } + + for (i = 0; i < count; ++i) { + source = sources[i]; + if (!source) { + continue; + } + + source.fill = resolveTarget(sources, i, propagate); + source.boundary = computeBoundary(source); + source.mapper = createMapper(source); + } + }, + + beforeDatasetDraw: function(chart, args) { + var meta = args.meta.$filler; + if (!meta) { + return; + } + + var ctx = chart.ctx; + var el = meta.el; + var view = el._view; + var points = el._children || []; + var mapper = meta.mapper; + var color = view.backgroundColor || core_defaults.global.defaultColor; + + if (mapper && color && points.length) { + helpers$1.canvas.clipArea(ctx, chart.chartArea); + doFill(ctx, points, mapper, view, color, el._loop); + helpers$1.canvas.unclipArea(ctx); + } + } +}; + +var noop$1 = helpers$1.noop; +var valueOrDefault$d = helpers$1.valueOrDefault; + +core_defaults._set('global', { + legend: { + display: true, + position: 'top', + fullWidth: true, + reverse: false, + weight: 1000, + + // a callback that will handle + onClick: function(e, legendItem) { + var index = legendItem.datasetIndex; + var ci = this.chart; + var meta = ci.getDatasetMeta(index); + + // See controller.isDatasetVisible comment + meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null; + + // We hid a dataset ... rerender the chart + ci.update(); + }, + + onHover: null, + onLeave: null, + + labels: { + boxWidth: 40, + padding: 10, + // Generates labels shown in the legend + // Valid properties to return: + // text : text to display + // fillStyle : fill of coloured box + // strokeStyle: stroke of coloured box + // hidden : if this legend item refers to a hidden item + // lineCap : cap style for line + // lineDash + // lineDashOffset : + // lineJoin : + // lineWidth : + generateLabels: function(chart) { + var data = chart.data; + return helpers$1.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { + return { + text: dataset.label, + fillStyle: (!helpers$1.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]), + hidden: !chart.isDatasetVisible(i), + lineCap: dataset.borderCapStyle, + lineDash: dataset.borderDash, + lineDashOffset: dataset.borderDashOffset, + lineJoin: dataset.borderJoinStyle, + lineWidth: dataset.borderWidth, + strokeStyle: dataset.borderColor, + pointStyle: dataset.pointStyle, + + // Below is extra data used for toggling the datasets + datasetIndex: i + }; + }, this) : []; + } + } + }, + + legendCallback: function(chart) { + var text = []; + text.push('
    '); + 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(''); + } +}); + +/** + * Helper function to get the box width based on the usePointStyle option + * @param {object} labelopts - the label options on the legend + * @param {number} fontSize - the label font size + * @return {number} width of the color box area + */ +function getBoxWidth(labelOpts, fontSize) { + return labelOpts.usePointStyle && labelOpts.boxWidth > fontSize ? + fontSize : + labelOpts.boxWidth; +} + +/** + * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! + */ +var Legend = core_element.extend({ + + initialize: function(config) { + helpers$1.extend(this, config); + + // Contains hit boxes for each dataset (in dataset order) + this.legendHitBoxes = []; + + /** + * @private + */ + this._hoveredItem = null; + + // Are we in doughnut mode which has a different data type + this.doughnutMode = false; + }, + + // These methods are ordered by lifecycle. Utilities then follow. + // Any function defined here is inherited by all legend types. + // Any function can be extended by the legend type + + beforeUpdate: noop$1, + update: function(maxWidth, maxHeight, margins) { + var me = this; + + // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) + me.beforeUpdate(); + + // Absorb the master measurements + me.maxWidth = maxWidth; + me.maxHeight = maxHeight; + me.margins = margins; + + // Dimensions + me.beforeSetDimensions(); + me.setDimensions(); + me.afterSetDimensions(); + // Labels + me.beforeBuildLabels(); + me.buildLabels(); + me.afterBuildLabels(); + + // Fit + me.beforeFit(); + me.fit(); + me.afterFit(); + // + me.afterUpdate(); + + return me.minSize; + }, + afterUpdate: noop$1, + + // + + beforeSetDimensions: noop$1, + setDimensions: function() { + var me = this; + // Set the unconstrained dimension before label rotation + if (me.isHorizontal()) { + // Reset position before calculating rotation + me.width = me.maxWidth; + me.left = 0; + me.right = me.width; + } else { + me.height = me.maxHeight; + + // Reset position before calculating rotation + me.top = 0; + me.bottom = me.height; + } + + // Reset padding + me.paddingLeft = 0; + me.paddingTop = 0; + me.paddingRight = 0; + me.paddingBottom = 0; + + // Reset minSize + me.minSize = { + width: 0, + height: 0 + }; + }, + afterSetDimensions: noop$1, + + // + + beforeBuildLabels: noop$1, + buildLabels: function() { + var me = this; + var labelOpts = me.options.labels || {}; + var legendItems = helpers$1.callback(labelOpts.generateLabels, [me.chart], me) || []; + + if (labelOpts.filter) { + legendItems = legendItems.filter(function(item) { + return labelOpts.filter(item, me.chart.data); + }); + } + + if (me.options.reverse) { + legendItems.reverse(); + } + + me.legendItems = legendItems; + }, + afterBuildLabels: noop$1, + + // + + beforeFit: noop$1, + fit: function() { + var me = this; + var opts = me.options; + var labelOpts = opts.labels; + var display = opts.display; + + var ctx = me.ctx; + + var labelFont = helpers$1.options._parseFont(labelOpts); + var fontSize = labelFont.size; + + // Reset hit boxes + var hitboxes = me.legendHitBoxes = []; + + var minSize = me.minSize; + var isHorizontal = me.isHorizontal(); + + if (isHorizontal) { + minSize.width = me.maxWidth; // fill all the width + minSize.height = display ? 10 : 0; + } else { + minSize.width = display ? 10 : 0; + minSize.height = me.maxHeight; // fill all the height + } + + // Increase sizes here + if (display) { + ctx.font = labelFont.string; + + if (isHorizontal) { + // Labels + + // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one + var lineWidths = me.lineWidths = [0]; + var totalHeight = 0; + + ctx.textAlign = 'left'; + ctx.textBaseline = 'top'; + + helpers$1.each(me.legendItems, function(legendItem, i) { + var boxWidth = getBoxWidth(labelOpts, fontSize); + var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; + + if (i === 0 || lineWidths[lineWidths.length - 1] + width + labelOpts.padding > minSize.width) { + totalHeight += fontSize + labelOpts.padding; + lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = labelOpts.padding; + } + + // Store the hitbox width and height here. Final position will be updated in `draw` + hitboxes[i] = { + left: 0, + top: 0, + width: width, + height: fontSize + }; + + lineWidths[lineWidths.length - 1] += width + labelOpts.padding; + }); + + minSize.height += totalHeight; + + } else { + var vPadding = labelOpts.padding; + var columnWidths = me.columnWidths = []; + var totalWidth = labelOpts.padding; + var currentColWidth = 0; + var currentColHeight = 0; + var itemHeight = fontSize + vPadding; + + helpers$1.each(me.legendItems, function(legendItem, i) { + var boxWidth = getBoxWidth(labelOpts, fontSize); + var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; + + // If too tall, go to new column + if (i > 0 && currentColHeight + itemHeight > minSize.height - vPadding) { + totalWidth += currentColWidth + labelOpts.padding; + columnWidths.push(currentColWidth); // previous column width + + currentColWidth = 0; + currentColHeight = 0; + } + + // Get max width + currentColWidth = Math.max(currentColWidth, itemWidth); + currentColHeight += itemHeight; + + // Store the hitbox width and height here. Final position will be updated in `draw` + hitboxes[i] = { + left: 0, + top: 0, + width: itemWidth, + height: fontSize + }; + }); + + totalWidth += currentColWidth; + columnWidths.push(currentColWidth); + minSize.width += totalWidth; + } + } + + me.width = minSize.width; + me.height = minSize.height; + }, + afterFit: noop$1, + + // Shared Methods + isHorizontal: function() { + return this.options.position === 'top' || this.options.position === 'bottom'; + }, + + // Actually draw the legend on the canvas + draw: function() { + var me = this; + var opts = me.options; + var labelOpts = opts.labels; + var globalDefaults = core_defaults.global; + var defaultColor = globalDefaults.defaultColor; + var lineDefault = globalDefaults.elements.line; + var legendWidth = me.width; + var lineWidths = me.lineWidths; + + if (opts.display) { + var ctx = me.ctx; + var fontColor = valueOrDefault$d(labelOpts.fontColor, globalDefaults.defaultFontColor); + var labelFont = helpers$1.options._parseFont(labelOpts); + var fontSize = labelFont.size; + var cursor; + + // Canvas setup + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + ctx.lineWidth = 0.5; + ctx.strokeStyle = fontColor; // for strikethrough effect + ctx.fillStyle = fontColor; // render in correct colour + ctx.font = labelFont.string; + + var boxWidth = getBoxWidth(labelOpts, fontSize); + var hitboxes = me.legendHitBoxes; + + // current position + var drawLegendBox = function(x, y, legendItem) { + if (isNaN(boxWidth) || boxWidth <= 0) { + return; + } + + // Set the ctx for the box + ctx.save(); + + var lineWidth = valueOrDefault$d(legendItem.lineWidth, lineDefault.borderWidth); + ctx.fillStyle = valueOrDefault$d(legendItem.fillStyle, defaultColor); + ctx.lineCap = valueOrDefault$d(legendItem.lineCap, lineDefault.borderCapStyle); + ctx.lineDashOffset = valueOrDefault$d(legendItem.lineDashOffset, lineDefault.borderDashOffset); + ctx.lineJoin = valueOrDefault$d(legendItem.lineJoin, lineDefault.borderJoinStyle); + ctx.lineWidth = lineWidth; + ctx.strokeStyle = valueOrDefault$d(legendItem.strokeStyle, defaultColor); + + if (ctx.setLineDash) { + // IE 9 and 10 do not support line dash + ctx.setLineDash(valueOrDefault$d(legendItem.lineDash, lineDefault.borderDash)); + } + + if (opts.labels && opts.labels.usePointStyle) { + // Recalculate x and y for drawPoint() because its expecting + // x and y to be center of figure (instead of top left) + var radius = boxWidth * Math.SQRT2 / 2; + var centerX = x + boxWidth / 2; + var centerY = y + fontSize / 2; + + // Draw pointStyle as legend symbol + helpers$1.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); + } else { + // Draw box as legend symbol + if (lineWidth !== 0) { + ctx.strokeRect(x, y, boxWidth, fontSize); + } + ctx.fillRect(x, y, boxWidth, fontSize); + } + + ctx.restore(); + }; + var fillText = function(x, y, legendItem, textWidth) { + var halfFontSize = fontSize / 2; + var xLeft = boxWidth + halfFontSize + x; + var yMiddle = y + halfFontSize; + + ctx.fillText(legendItem.text, xLeft, yMiddle); + + if (legendItem.hidden) { + // Strikethrough the text if hidden + ctx.beginPath(); + ctx.lineWidth = 2; + ctx.moveTo(xLeft, yMiddle); + ctx.lineTo(xLeft + textWidth, yMiddle); + ctx.stroke(); + } + }; + + // Horizontal + var isHorizontal = me.isHorizontal(); + if (isHorizontal) { + cursor = { + x: me.left + ((legendWidth - lineWidths[0]) / 2) + labelOpts.padding, + y: me.top + labelOpts.padding, + line: 0 + }; + } else { + cursor = { + x: me.left + labelOpts.padding, + y: me.top + labelOpts.padding, + line: 0 + }; + } + + var itemHeight = fontSize + labelOpts.padding; + helpers$1.each(me.legendItems, function(legendItem, i) { + var textWidth = ctx.measureText(legendItem.text).width; + var width = boxWidth + (fontSize / 2) + textWidth; + var x = cursor.x; + var y = cursor.y; + + // Use (me.left + me.minSize.width) and (me.top + me.minSize.height) + // instead of me.right and me.bottom because me.width and me.height + // may have been changed since me.minSize was calculated + if (isHorizontal) { + if (i > 0 && x + width + labelOpts.padding > me.left + me.minSize.width) { + y = cursor.y += itemHeight; + cursor.line++; + x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2) + labelOpts.padding; + } + } else if (i > 0 && y + itemHeight > me.top + me.minSize.height) { + x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; + y = cursor.y = me.top + labelOpts.padding; + cursor.line++; + } + + drawLegendBox(x, y, legendItem); + + hitboxes[i].left = x; + hitboxes[i].top = y; + + // Fill the actual label + fillText(x, y, legendItem, textWidth); + + if (isHorizontal) { + cursor.x += width + labelOpts.padding; + } else { + cursor.y += itemHeight; + } + + }); + } + }, + + /** + * @private + */ + _getLegendItemAt: function(x, y) { + var me = this; + var i, hitBox, lh; + + if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { + // See if we are touching one of the dataset boxes + lh = me.legendHitBoxes; + for (i = 0; i < lh.length; ++i) { + hitBox = lh[i]; + + if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { + // Touching an element + return me.legendItems[i]; + } + } + } + + return null; + }, + + /** + * Handle an event + * @private + * @param {IEvent} event - The event to handle + */ + handleEvent: function(e) { + var me = this; + var opts = me.options; + var type = e.type === 'mouseup' ? 'click' : e.type; + var hoveredItem; + + if (type === 'mousemove') { + if (!opts.onHover && !opts.onLeave) { + return; + } + } else if (type === 'click') { + if (!opts.onClick) { + return; + } + } else { + return; + } + + // Chart event already has relative position in it + hoveredItem = me._getLegendItemAt(e.x, e.y); + + if (type === 'click') { + if (hoveredItem && opts.onClick) { + // use e.native for backwards compatibility + opts.onClick.call(me, e.native, hoveredItem); + } + } else { + if (opts.onLeave && hoveredItem !== me._hoveredItem) { + if (me._hoveredItem) { + opts.onLeave.call(me, e.native, me._hoveredItem); + } + me._hoveredItem = hoveredItem; + } + + if (opts.onHover && hoveredItem) { + // use e.native for backwards compatibility + opts.onHover.call(me, e.native, hoveredItem); + } + } + } +}); + +function createNewLegendAndAttach(chart, legendOpts) { + var legend = new Legend({ + ctx: chart.ctx, + options: legendOpts, + chart: chart + }); + + core_layouts.configure(chart, legend, legendOpts); + core_layouts.addBox(chart, legend); + chart.legend = legend; +} + +var plugin_legend = { + id: 'legend', + + /** + * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making + * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of + * the plugin, which one will be re-exposed in the chart.js file. + * https://github.com/chartjs/Chart.js/pull/2640 + * @private + */ + _element: Legend, + + beforeInit: function(chart) { + var legendOpts = chart.options.legend; + + if (legendOpts) { + createNewLegendAndAttach(chart, legendOpts); + } + }, + + beforeUpdate: function(chart) { + var legendOpts = chart.options.legend; + var legend = chart.legend; + + if (legendOpts) { + helpers$1.mergeIf(legendOpts, core_defaults.global.legend); + + if (legend) { + core_layouts.configure(chart, legend, legendOpts); + legend.options = legendOpts; + } else { + createNewLegendAndAttach(chart, legendOpts); + } + } else if (legend) { + core_layouts.removeBox(chart, legend); + delete chart.legend; + } + }, + + afterEvent: function(chart, e) { + var legend = chart.legend; + if (legend) { + legend.handleEvent(e); + } + } +}; + +var noop$2 = helpers$1.noop; + +core_defaults._set('global', { + title: { + display: false, + fontStyle: 'bold', + fullWidth: true, + padding: 10, + position: 'top', + text: '', + weight: 2000 // by default greater than legend (1000) to be above + } +}); + +/** + * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! + */ +var Title = core_element.extend({ + initialize: function(config) { + var me = this; + helpers$1.extend(me, config); + + // Contains hit boxes for each dataset (in dataset order) + me.legendHitBoxes = []; + }, + + // These methods are ordered by lifecycle. Utilities then follow. + + beforeUpdate: noop$2, + update: function(maxWidth, maxHeight, margins) { + var me = this; + + // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) + me.beforeUpdate(); + + // Absorb the master measurements + me.maxWidth = maxWidth; + me.maxHeight = maxHeight; + me.margins = margins; + + // Dimensions + me.beforeSetDimensions(); + me.setDimensions(); + me.afterSetDimensions(); + // Labels + me.beforeBuildLabels(); + me.buildLabels(); + me.afterBuildLabels(); + + // Fit + me.beforeFit(); + me.fit(); + me.afterFit(); + // + me.afterUpdate(); + + return me.minSize; + + }, + afterUpdate: noop$2, + + // + + beforeSetDimensions: noop$2, + setDimensions: function() { + var me = this; + // Set the unconstrained dimension before label rotation + if (me.isHorizontal()) { + // Reset position before calculating rotation + me.width = me.maxWidth; + me.left = 0; + me.right = me.width; + } else { + me.height = me.maxHeight; + + // Reset position before calculating rotation + me.top = 0; + me.bottom = me.height; + } + + // Reset padding + me.paddingLeft = 0; + me.paddingTop = 0; + me.paddingRight = 0; + me.paddingBottom = 0; + + // Reset minSize + me.minSize = { + width: 0, + height: 0 + }; + }, + afterSetDimensions: noop$2, + + // + + beforeBuildLabels: noop$2, + buildLabels: noop$2, + afterBuildLabels: noop$2, + + // + + beforeFit: noop$2, + fit: function() { + var me = this; + var opts = me.options; + var display = opts.display; + var minSize = me.minSize; + var lineCount = helpers$1.isArray(opts.text) ? opts.text.length : 1; + var fontOpts = helpers$1.options._parseFont(opts); + var textSize = display ? (lineCount * fontOpts.lineHeight) + (opts.padding * 2) : 0; + + if (me.isHorizontal()) { + minSize.width = me.maxWidth; // fill all the width + minSize.height = textSize; + } else { + minSize.width = textSize; + minSize.height = me.maxHeight; // fill all the height + } + + me.width = minSize.width; + me.height = minSize.height; + + }, + afterFit: noop$2, + + // Shared Methods + isHorizontal: function() { + var pos = this.options.position; + return pos === 'top' || pos === 'bottom'; + }, + + // Actually draw the title block on the canvas + draw: function() { + var me = this; + var ctx = me.ctx; + var opts = me.options; + + if (opts.display) { + var fontOpts = helpers$1.options._parseFont(opts); + var lineHeight = fontOpts.lineHeight; + var offset = lineHeight / 2 + opts.padding; + var rotation = 0; + var top = me.top; + var left = me.left; + var bottom = me.bottom; + var right = me.right; + var maxWidth, titleX, titleY; + + ctx.fillStyle = helpers$1.valueOrDefault(opts.fontColor, core_defaults.global.defaultFontColor); // render in correct colour + ctx.font = fontOpts.string; + + // Horizontal + if (me.isHorizontal()) { + titleX = left + ((right - left) / 2); // midpoint of the width + titleY = top + offset; + maxWidth = right - left; + } else { + titleX = opts.position === 'left' ? left + offset : right - offset; + titleY = top + ((bottom - top) / 2); + maxWidth = bottom - top; + rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); + } + + ctx.save(); + ctx.translate(titleX, titleY); + ctx.rotate(rotation); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + + var text = opts.text; + if (helpers$1.isArray(text)) { + var y = 0; + for (var i = 0; i < text.length; ++i) { + ctx.fillText(text[i], 0, y, maxWidth); + y += lineHeight; + } + } else { + ctx.fillText(text, 0, 0, maxWidth); + } + + ctx.restore(); + } + } +}); + +function createNewTitleBlockAndAttach(chart, titleOpts) { + var title = new Title({ + ctx: chart.ctx, + options: titleOpts, + chart: chart + }); + + core_layouts.configure(chart, title, titleOpts); + core_layouts.addBox(chart, title); + chart.titleBlock = title; +} + +var plugin_title = { + id: 'title', + + /** + * Backward compatibility: since 2.1.5, the title is registered as a plugin, making + * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of + * the plugin, which one will be re-exposed in the chart.js file. + * https://github.com/chartjs/Chart.js/pull/2640 + * @private + */ + _element: Title, + + beforeInit: function(chart) { + var titleOpts = chart.options.title; + + if (titleOpts) { + createNewTitleBlockAndAttach(chart, titleOpts); + } + }, + + beforeUpdate: function(chart) { + var titleOpts = chart.options.title; + var titleBlock = chart.titleBlock; + + if (titleOpts) { + helpers$1.mergeIf(titleOpts, core_defaults.global.title); + + if (titleBlock) { + core_layouts.configure(chart, titleBlock, titleOpts); + titleBlock.options = titleOpts; + } else { + createNewTitleBlockAndAttach(chart, titleOpts); + } + } else if (titleBlock) { + core_layouts.removeBox(chart, titleBlock); + delete chart.titleBlock; + } + } +}; + +var plugins = {}; +var filler = plugin_filler; +var legend = plugin_legend; +var title = plugin_title; +plugins.filler = filler; +plugins.legend = legend; +plugins.title = title; + +/** + * @namespace Chart + */ + + +core_controller.helpers = helpers$1; + +// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests! +core_helpers(core_controller); + +core_controller._adapters = core_adapters; +core_controller.Animation = core_animation; +core_controller.animationService = core_animations; +core_controller.controllers = controllers; +core_controller.DatasetController = core_datasetController; +core_controller.defaults = core_defaults; +core_controller.Element = core_element; +core_controller.elements = elements; +core_controller.Interaction = core_interaction; +core_controller.layouts = core_layouts; +core_controller.platform = platform; +core_controller.plugins = core_plugins; +core_controller.Scale = core_scale; +core_controller.scaleService = core_scaleService; +core_controller.Ticks = core_ticks; +core_controller.Tooltip = core_tooltip; + +// Register built-in scales + +core_controller.helpers.each(scales, function(scale, type) { + core_controller.scaleService.registerScaleType(type, scale, scale._defaults); +}); + +// Load to register built-in adapters (as side effects) + + +// Loading built-in plugins + +for (var k in plugins) { + if (plugins.hasOwnProperty(k)) { + core_controller.plugins.register(plugins[k]); + } +} + +core_controller.platform.initialize(); + +var src = core_controller; +if (typeof window !== 'undefined') { + window.Chart = core_controller; +} + +// DEPRECATIONS + +/** + * Provided for backward compatibility, not available anymore + * @namespace Chart.Chart + * @deprecated since version 2.8.0 + * @todo remove at version 3 + * @private + */ +core_controller.Chart = core_controller; + +/** + * Provided for backward compatibility, not available anymore + * @namespace Chart.Legend + * @deprecated since version 2.1.5 + * @todo remove at version 3 + * @private + */ +core_controller.Legend = plugins.legend._element; + +/** + * Provided for backward compatibility, not available anymore + * @namespace Chart.Title + * @deprecated since version 2.1.5 + * @todo remove at version 3 + * @private + */ +core_controller.Title = plugins.title._element; + +/** + * Provided for backward compatibility, use Chart.plugins instead + * @namespace Chart.pluginService + * @deprecated since version 2.1.5 + * @todo remove at version 3 + * @private + */ +core_controller.pluginService = core_controller.plugins; + +/** + * Provided for backward compatibility, inheriting from Chart.PlugingBase has no + * effect, instead simply create/register plugins via plain JavaScript objects. + * @interface Chart.PluginBase + * @deprecated since version 2.5.0 + * @todo remove at version 3 + * @private + */ +core_controller.PluginBase = core_controller.Element.extend({}); + +/** + * Provided for backward compatibility, use Chart.helpers.canvas instead. + * @namespace Chart.canvasHelpers + * @deprecated since version 2.6.0 + * @todo remove at version 3 + * @private + */ +core_controller.canvasHelpers = core_controller.helpers.canvas; + +/** + * Provided for backward compatibility, use Chart.layouts instead. + * @namespace Chart.layoutService + * @deprecated since version 2.7.3 + * @todo remove at version 3 + * @private + */ +core_controller.layoutService = core_controller.layouts; + +/** + * Provided for backward compatibility, not available anymore. + * @namespace Chart.LinearScaleBase + * @deprecated since version 2.8 + * @todo remove at version 3 + * @private + */ +core_controller.LinearScaleBase = scale_linearbase; + +/** + * Provided for backward compatibility, instead we should create a new Chart + * by setting the type in the config (`new Chart(id, {type: '{chart-type}'}`). + * @deprecated since version 2.8.0 + * @todo remove at version 3 + */ +core_controller.helpers.each( + [ + 'Bar', + 'Bubble', + 'Doughnut', + 'Line', + 'PolarArea', + 'Radar', + 'Scatter' + ], + function(klass) { + core_controller[klass] = function(ctx, cfg) { + return new core_controller(ctx, core_controller.helpers.merge(cfg || {}, { + type: klass.charAt(0).toLowerCase() + klass.slice(1) + })); + }; + } +); + +return src; + +}))); diff --git a/activity_dashboard_mngmnt/static/src/js/lib/columnHeatmap.js b/activity_dashboard_mngmnt/static/src/js/lib/columnHeatmap.js new file mode 100644 index 000000000..9faf9a4f2 --- /dev/null +++ b/activity_dashboard_mngmnt/static/src/js/lib/columnHeatmap.js @@ -0,0 +1,114 @@ +(function($) { + $.columnHeatmap = { + name: 'columnHeatmap', + version: '1.0', + release: '2020-06-15', + author: 'Paulo Kramer', + site: 'https://www.paulokramer.com', + documentation: 'https://github.com/PauloAK/jQuery-columnHeatMap' + }; + + $.fn.columnHeatmap = function(options) { + var settings = $.extend({ + columns: [], // 0 is the first column + contrast: true, // Change text color to white on stronger background colors + inverse: false, // By default, higher are red and lower are green, if this options is enabled, the logic are inversed + animated: true, // Animated background-color and text color transition + animationSpeed: .1, // Speed of transition animation in seconds + fn_parseValue: null, // Custom function to parse cell value + colorStartPoint: 0 // HSL color start point + }, options); + + try { + if (!this.is('table')) + throw 'Selected element isn\'t a table'; + + if (settings.columns.length == 0) + throw 'You need to specify the columns'; + + if (settings.colorStartPoint < 0 || settings.colorStartPoint > 360) + throw `colorStartPoint need to be beetween 0 and 360, current: ${settings.colorStartPoint}`; + + let $table = this; + let rows = $table.find('tbody tr'); + let data = []; + + $.each(settings.columns, (loop, col) => { + rows.each((key, row) => { + if (key == 0) { + data[col.toString()] = new Array; + data[col.toString()]['values'] = new Array; + data[col.toString()]['tds'] = new Array; + } + + let td = $(row).find('td')[col]; + data[col.toString()]['tds'].push(td); + + + + if (typeof settings.fn_parseValue == "function") { + let value = fn_parseValue($(td).text()); + if (typeof value == "undefined") { + throw 'None value returned in fn_parseValue'; + } else { + data[col.toString()]['values'].push(value); + } + } else { + // Get only numbers from the cell, (with float points) + let numbers = $(td).text().match(/[\d|\-|.|,]+/)[0]; + + data[col.toString()]['values'].push(parseFloat(numbers)); + } + }); + }); + + data = data.filter((value) => { return value; }); + + $.each(data, (key, col) => { + if (!col || !col['values']) + return; + + data[key]['min'] = null; + data[key]['max'] = null; + + data[key]['min'] = col['values'].reduce(function(a, b) { + return Math.min(a, b); + }); + + data[key]['max'] = col['values'].reduce(function(a, b) { + return Math.max(a, b); + }); + }); + + $.each(data, (key, col) => { + $.each(col['values'], (key, value) => { + let colorGenerated = colorGenerator(value, col['min'], col['max']); + + if (settings.animated) { + $(col['tds'][key]).css('transition', `background-color ${settings.animationSpeed}s linear, color ${settings.animationSpeed}s linear`); + } + + $(col['tds'][key]).css('background-color', colorGenerated.color); + + if (colorGenerated.perc > 70 && settings.contrast) { + $(col['tds'][key]).css('color', '#fff'); + } + }); + }); + + function colorGenerator(value, min, max) { + var perc = (100 * (value - min)) / (max - min); + + if (settings.inverse) + perc = 100 - perc; + + var hsl = Math.abs((perc - 100) * -1) + settings.colorStartPoint; + + return { color: 'hsl(' + hsl + ', 70%, 65%)', perc: perc }; + } + } catch (error) { + console.error(`[${$.columnHeatmap.name}::Error] ${error}`); + return; + } + }; +}(jQuery)); \ No newline at end of file diff --git a/activity_dashboard_mngmnt/static/src/js/lib/columnHeatmap.min.js b/activity_dashboard_mngmnt/static/src/js/lib/columnHeatmap.min.js new file mode 100644 index 000000000..129ddbdbf --- /dev/null +++ b/activity_dashboard_mngmnt/static/src/js/lib/columnHeatmap.min.js @@ -0,0 +1 @@ +!function(t){t.columnHeatmap={name:"columnHeatmap",version:"1.0",release:"2020-06-15",author:"Paulo Kramer",site:"https://www.paulokramer.com",documentation:"https://github.com/PauloAK/jQuery-columnHeatMap"},t.fn.columnHeatmap=function(e){var n=t.extend({columns:[],contrast:!0,inverse:!1,animated:!0,animationSpeed:.1,fn_parseValue:null,colorStartPoint:0},e);try{if(!this.is("table"))throw"Selected element isn't a table";if(0==n.columns.length)throw"You need to specify the columns";if(n.colorStartPoint<0||n.colorStartPoint>360)throw`colorStartPoint need to be beetween 0 and 360, current: ${n.colorStartPoint}`;let e=this.find("tbody tr"),a=[];t.each(n.columns,(o,r)=>{e.each((e,o)=>{0==e&&(a[r.toString()]=new Array,a[r.toString()].values=new Array,a[r.toString()].tds=new Array);let l=t(o).find("td")[r];if(a[r.toString()].tds.push(l),"function"==typeof n.fn_parseValue){let e=fn_parseValue(t(l).text());if(void 0===e)throw"None value returned in fn_parseValue";a[r.toString()].values.push(e)}else{let e=t(l).text().match(/[\d|\-|.|,]+/)[0];a[r.toString()].values.push(parseFloat(e))}})}),a=a.filter(t=>t),t.each(a,(t,e)=>{e&&e.values&&(a[t].min=null,a[t].max=null,a[t].min=e.values.reduce(function(t,e){return Math.min(t,e)}),a[t].max=e.values.reduce(function(t,e){return Math.max(t,e)}))}),t.each(a,(e,a)=>{t.each(a.values,(e,o)=>{let r=function(t,e,a){var o=100*(t-e)/(a-e);n.inverse&&(o=100-o);return{color:"hsl("+(Math.abs(-1*(o-100))+n.colorStartPoint)+", 70%, 65%)",perc:o}}(o,a.min,a.max);n.animated&&t(a.tds[e]).css("transition",`background-color ${n.animationSpeed}s linear, color ${n.animationSpeed}s linear`),t(a.tds[e]).css("background-color",r.color),r.perc>70&&n.contrast&&t(a.tds[e]).css("color","#fff")})})}catch(e){return void console.error(`[${t.columnHeatmap.name}::Error] ${e}`)}}}(jQuery); diff --git a/activity_dashboard_mngmnt/static/src/js/lib/d3.min.js b/activity_dashboard_mngmnt/static/src/js/lib/d3.min.js new file mode 100644 index 000000000..213a10816 --- /dev/null +++ b/activity_dashboard_mngmnt/static/src/js/lib/d3.min.js @@ -0,0 +1,7828 @@ +! function() { + function n(n) { + return n && (n.ownerDocument || n.document).documentElement + } + + function t(n) { + return n && n.ownerDocument ? n.ownerDocument.defaultView : n + } + + function e(n, t) { + return t > n ? -1 : n > t ? 1 : n >= t ? 0 : 0 / 0 + } + + function r(n) { + return null === n ? 0 / 0 : +n + } + + function u(n) { + return !isNaN(n) + } + + function i(n) { + return { + left: function(t, e, r, u) { + for (arguments.length < 3 && (r = 0), arguments.length < 4 && (u = t.length); u > r;) { + var i = r + u >>> 1; + n(t[i], e) < 0 ? r = i + 1 : u = i + } + return r + }, + right: function(t, e, r, u) { + for (arguments.length < 3 && (r = 0), arguments.length < 4 && (u = t.length); u > r;) { + var i = r + u >>> 1; + n(t[i], e) > 0 ? u = i : r = i + 1 + } + return r + } + } + } + + function o(n) { + return n.length + } + + function a(n) { + for (var t = 1; n * t % 1;) t *= 10; + return t + } + + function c(n, t) { + for (var e in t) Object.defineProperty(n.prototype, e, { + value: t[e], + enumerable: !1 + }) + } + + function l() { + this._ = Object.create(null) + } + + function s(n) { + return (n += "") === pa || n[0] === va ? va + n : n + } + + function f(n) { + return (n += "")[0] === va ? n.slice(1) : n + } + + function h(n) { + return s(n) in this._ + } + + function g(n) { + return (n = s(n)) in this._ && delete this._[n] + } + + function p() { + var n = []; + for (var t in this._) n.push(f(t)); + return n + } + + function v() { + var n = 0; + for (var t in this._) ++n; + return n + } + + function d() { + for (var n in this._) return !1; + return !0 + } + + function m() { + this._ = Object.create(null) + } + + function y(n) { + return n + } + + function M(n, t, e) { + return function() { + var r = e.apply(t, arguments); + return r === t ? n : r + } + } + + function x(n, t) { + if (t in n) return t; + t = t.charAt(0).toUpperCase() + t.slice(1); + for (var e = 0, r = da.length; r > e; ++e) { + var u = da[e] + t; + if (u in n) return u + } + } + + function b() {} + + function _() {} + + function w(n) { + function t() { + for (var t, r = e, u = -1, i = r.length; ++u < i;)(t = r[u].on) && t.apply(this, arguments); + return n + } + var e = [], + r = new l; + return t.on = function(t, u) { + var i, o = r.get(t); + return arguments.length < 2 ? o && o.on : (o && (o.on = null, e = e.slice(0, i = e.indexOf(o)).concat(e.slice(i + 1)), r.remove(t)), u && e.push(r.set(t, { + on: u + })), n) + }, t + } + + function S() { + ta.event.preventDefault() + } + + function k() { + for (var n, t = ta.event; n = t.sourceEvent;) t = n; + return t + } + + function E(n) { + for (var t = new _, e = 0, r = arguments.length; ++e < r;) t[arguments[e]] = w(t); + return t.of = function(e, r) { + return function(u) { + try { + var i = u.sourceEvent = ta.event; + u.target = n, ta.event = u, t[u.type].apply(e, r) + } finally { + ta.event = i + } + } + }, t + } + + function A(n) { + return ya(n, _a), n + } + + function N(n) { + return "function" == typeof n ? n : function() { + return Ma(n, this) + } + } + + function C(n) { + return "function" == typeof n ? n : function() { + return xa(n, this) + } + } + + function z(n, t) { + function e() { + this.removeAttribute(n) + } + + function r() { + this.removeAttributeNS(n.space, n.local) + } + + function u() { + this.setAttribute(n, t) + } + + function i() { + this.setAttributeNS(n.space, n.local, t) + } + + function o() { + var e = t.apply(this, arguments); + null == e ? this.removeAttribute(n) : this.setAttribute(n, e) + } + + function a() { + var e = t.apply(this, arguments); + null == e ? this.removeAttributeNS(n.space, n.local) : this.setAttributeNS(n.space, n.local, e) + } + return n = ta.ns.qualify(n), null == t ? n.local ? r : e : "function" == typeof t ? n.local ? a : o : n.local ? i : u + } + + function q(n) { + return n.trim().replace(/\s+/g, " ") + } + + function L(n) { + return new RegExp("(?:^|\\s+)" + ta.requote(n) + "(?:\\s+|$)", "g") + } + + function T(n) { + return (n + "").trim().split(/^|\s+/) + } + + function R(n, t) { + function e() { + for (var e = -1; ++e < u;) n[e](this, t) + } + + function r() { + for (var e = -1, r = t.apply(this, arguments); ++e < u;) n[e](this, r) + } + n = T(n).map(D); + var u = n.length; + return "function" == typeof t ? r : e + } + + function D(n) { + var t = L(n); + return function(e, r) { + if (u = e.classList) return r ? u.add(n) : u.remove(n); + var u = e.getAttribute("class") || ""; + r ? (t.lastIndex = 0, t.test(u) || e.setAttribute("class", q(u + " " + n))) : e.setAttribute("class", q(u.replace(t, " "))) + } + } + + function P(n, t, e) { + function r() { + this.style.removeProperty(n) + } + + function u() { + this.style.setProperty(n, t, e) + } + + function i() { + var r = t.apply(this, arguments); + null == r ? this.style.removeProperty(n) : this.style.setProperty(n, r, e) + } + return null == t ? r : "function" == typeof t ? i : u + } + + function U(n, t) { + function e() { + delete this[n] + } + + function r() { + this[n] = t + } + + function u() { + var e = t.apply(this, arguments); + null == e ? delete this[n] : this[n] = e + } + return null == t ? e : "function" == typeof t ? u : r + } + + function j(n) { + function t() { + var t = this.ownerDocument, + e = this.namespaceURI; + return e ? t.createElementNS(e, n) : t.createElement(n) + } + + function e() { + return this.ownerDocument.createElementNS(n.space, n.local) + } + return "function" == typeof n ? n : (n = ta.ns.qualify(n)).local ? e : t + } + + function F() { + var n = this.parentNode; + n && n.removeChild(this) + } + + function H(n) { + return { + __data__: n + } + } + + function O(n) { + return function() { + return ba(this, n) + } + } + + function I(n) { + return arguments.length || (n = e), + function(t, e) { + return t && e ? n(t.__data__, e.__data__) : !t - !e + } + } + + function Y(n, t) { + for (var e = 0, r = n.length; r > e; e++) + for (var u, i = n[e], o = 0, a = i.length; a > o; o++)(u = i[o]) && t(u, o, e); + return n + } + + function Z(n) { + return ya(n, Sa), n + } + + function V(n) { + var t, e; + return function(r, u, i) { + var o, a = n[i].update, + c = a.length; + for (i != e && (e = i, t = 0), u >= t && (t = u + 1); !(o = a[t]) && ++t < c;); + return o + } + } + + function X(n, t, e) { + function r() { + var t = this[o]; + t && (this.removeEventListener(n, t, t.$), delete this[o]) + } + + function u() { + var u = c(t, ra(arguments)); + r.call(this), this.addEventListener(n, this[o] = u, u.$ = e), u._ = t + } + + function i() { + var t, e = new RegExp("^__on([^.]+)" + ta.requote(n) + "$"); + for (var r in this) + if (t = r.match(e)) { + var u = this[r]; + this.removeEventListener(t[1], u, u.$), delete this[r] + } + } + var o = "__on" + n, + a = n.indexOf("."), + c = $; + a > 0 && (n = n.slice(0, a)); + var l = ka.get(n); + return l && (n = l, c = B), a ? t ? u : r : t ? b : i + } + + function $(n, t) { + return function(e) { + var r = ta.event; + ta.event = e, t[0] = this.__data__; + try { + n.apply(this, t) + } finally { + ta.event = r + } + } + } + + function B(n, t) { + var e = $(n, t); + return function(n) { + var t = this, + r = n.relatedTarget; + r && (r === t || 8 & r.compareDocumentPosition(t)) || e.call(t, n) + } + } + + function W(e) { + var r = ".dragsuppress-" + ++Aa, + u = "click" + r, + i = ta.select(t(e)).on("touchmove" + r, S).on("dragstart" + r, S).on("selectstart" + r, S); + if (null == Ea && (Ea = "onselectstart" in e ? !1 : x(e.style, "userSelect")), Ea) { + var o = n(e).style, + a = o[Ea]; + o[Ea] = "none" + } + return function(n) { + if (i.on(r, null), Ea && (o[Ea] = a), n) { + var t = function() { + i.on(u, null) + }; + i.on(u, function() { + S(), t() + }, !0), setTimeout(t, 0) + } + } + } + + function J(n, e) { + e.changedTouches && (e = e.changedTouches[0]); + var r = n.ownerSVGElement || n; + if (r.createSVGPoint) { + var u = r.createSVGPoint(); + if (0 > Na) { + var i = t(n); + if (i.scrollX || i.scrollY) { + r = ta.select("body").append("svg").style({ + position: "absolute", + top: 0, + left: 0, + margin: 0, + padding: 0, + border: "none" + }, "important"); + var o = r[0][0].getScreenCTM(); + Na = !(o.f || o.e), r.remove() + } + } + return Na ? (u.x = e.pageX, u.y = e.pageY) : (u.x = e.clientX, u.y = e.clientY), u = u.matrixTransform(n.getScreenCTM().inverse()), [u.x, u.y] + } + var a = n.getBoundingClientRect(); + return [e.clientX - a.left - n.clientLeft, e.clientY - a.top - n.clientTop] + } + + function G() { + return ta.event.changedTouches[0].identifier + } + + function K(n) { + return n > 0 ? 1 : 0 > n ? -1 : 0 + } + + function Q(n, t, e) { + return (t[0] - n[0]) * (e[1] - n[1]) - (t[1] - n[1]) * (e[0] - n[0]) + } + + function nt(n) { + return n > 1 ? 0 : -1 > n ? qa : Math.acos(n) + } + + function tt(n) { + return n > 1 ? Ra : -1 > n ? -Ra : Math.asin(n) + } + + function et(n) { + return ((n = Math.exp(n)) - 1 / n) / 2 + } + + function rt(n) { + return ((n = Math.exp(n)) + 1 / n) / 2 + } + + function ut(n) { + return ((n = Math.exp(2 * n)) - 1) / (n + 1) + } + + function it(n) { + return (n = Math.sin(n / 2)) * n + } + + function ot() {} + + function at(n, t, e) { + return this instanceof at ? (this.h = +n, this.s = +t, void(this.l = +e)) : arguments.length < 2 ? n instanceof at ? new at(n.h, n.s, n.l) : bt("" + n, _t, at) : new at(n, t, e) + } + + function ct(n, t, e) { + function r(n) { + return n > 360 ? n -= 360 : 0 > n && (n += 360), 60 > n ? i + (o - i) * n / 60 : 180 > n ? o : 240 > n ? i + (o - i) * (240 - n) / 60 : i + } + + function u(n) { + return Math.round(255 * r(n)) + } + var i, o; + return n = isNaN(n) ? 0 : (n %= 360) < 0 ? n + 360 : n, t = isNaN(t) ? 0 : 0 > t ? 0 : t > 1 ? 1 : t, e = 0 > e ? 0 : e > 1 ? 1 : e, o = .5 >= e ? e * (1 + t) : e + t - e * t, i = 2 * e - o, new mt(u(n + 120), u(n), u(n - 120)) + } + + function lt(n, t, e) { + return this instanceof lt ? (this.h = +n, this.c = +t, void(this.l = +e)) : arguments.length < 2 ? n instanceof lt ? new lt(n.h, n.c, n.l) : n instanceof ft ? gt(n.l, n.a, n.b) : gt((n = wt((n = ta.rgb(n)).r, n.g, n.b)).l, n.a, n.b) : new lt(n, t, e) + } + + function st(n, t, e) { + return isNaN(n) && (n = 0), isNaN(t) && (t = 0), new ft(e, Math.cos(n *= Da) * t, Math.sin(n) * t) + } + + function ft(n, t, e) { + return this instanceof ft ? (this.l = +n, this.a = +t, void(this.b = +e)) : arguments.length < 2 ? n instanceof ft ? new ft(n.l, n.a, n.b) : n instanceof lt ? st(n.h, n.c, n.l) : wt((n = mt(n)).r, n.g, n.b) : new ft(n, t, e) + } + + function ht(n, t, e) { + var r = (n + 16) / 116, + u = r + t / 500, + i = r - e / 200; + return u = pt(u) * Xa, r = pt(r) * $a, i = pt(i) * Ba, new mt(dt(3.2404542 * u - 1.5371385 * r - .4985314 * i), dt(-.969266 * u + 1.8760108 * r + .041556 * i), dt(.0556434 * u - .2040259 * r + 1.0572252 * i)) + } + + function gt(n, t, e) { + return n > 0 ? new lt(Math.atan2(e, t) * Pa, Math.sqrt(t * t + e * e), n) : new lt(0 / 0, 0 / 0, n) + } + + function pt(n) { + return n > .206893034 ? n * n * n : (n - 4 / 29) / 7.787037 + } + + function vt(n) { + return n > .008856 ? Math.pow(n, 1 / 3) : 7.787037 * n + 4 / 29 + } + + function dt(n) { + return Math.round(255 * (.00304 >= n ? 12.92 * n : 1.055 * Math.pow(n, 1 / 2.4) - .055)) + } + + function mt(n, t, e) { + return this instanceof mt ? (this.r = ~~n, this.g = ~~t, void(this.b = ~~e)) : arguments.length < 2 ? n instanceof mt ? new mt(n.r, n.g, n.b) : bt("" + n, mt, ct) : new mt(n, t, e) + } + + function yt(n) { + return new mt(n >> 16, n >> 8 & 255, 255 & n) + } + + function Mt(n) { + return yt(n) + "" + } + + function xt(n) { + return 16 > n ? "0" + Math.max(0, n).toString(16) : Math.min(255, n).toString(16) + } + + function bt(n, t, e) { + var r, u, i, o = 0, + a = 0, + c = 0; + if (r = /([a-z]+)\((.*)\)/i.exec(n)) switch (u = r[2].split(","), r[1]) { + case "hsl": + return e(parseFloat(u[0]), parseFloat(u[1]) / 100, parseFloat(u[2]) / 100); + case "rgb": + return t(kt(u[0]), kt(u[1]), kt(u[2])) + } + return (i = Ga.get(n.toLowerCase())) ? t(i.r, i.g, i.b) : (null == n || "#" !== n.charAt(0) || isNaN(i = parseInt(n.slice(1), 16)) || (4 === n.length ? (o = (3840 & i) >> 4, o = o >> 4 | o, a = 240 & i, a = a >> 4 | a, c = 15 & i, c = c << 4 | c) : 7 === n.length && (o = (16711680 & i) >> 16, a = (65280 & i) >> 8, c = 255 & i)), t(o, a, c)) + } + + function _t(n, t, e) { + var r, u, i = Math.min(n /= 255, t /= 255, e /= 255), + o = Math.max(n, t, e), + a = o - i, + c = (o + i) / 2; + return a ? (u = .5 > c ? a / (o + i) : a / (2 - o - i), r = n == o ? (t - e) / a + (e > t ? 6 : 0) : t == o ? (e - n) / a + 2 : (n - t) / a + 4, r *= 60) : (r = 0 / 0, u = c > 0 && 1 > c ? 0 : r), new at(r, u, c) + } + + function wt(n, t, e) { + n = St(n), t = St(t), e = St(e); + var r = vt((.4124564 * n + .3575761 * t + .1804375 * e) / Xa), + u = vt((.2126729 * n + .7151522 * t + .072175 * e) / $a), + i = vt((.0193339 * n + .119192 * t + .9503041 * e) / Ba); + return ft(116 * u - 16, 500 * (r - u), 200 * (u - i)) + } + + function St(n) { + return (n /= 255) <= .04045 ? n / 12.92 : Math.pow((n + .055) / 1.055, 2.4) + } + + function kt(n) { + var t = parseFloat(n); + return "%" === n.charAt(n.length - 1) ? Math.round(2.55 * t) : t + } + + function Et(n) { + return "function" == typeof n ? n : function() { + return n + } + } + + function At(n) { + return function(t, e, r) { + return 2 === arguments.length && "function" == typeof e && (r = e, e = null), Nt(t, e, n, r) + } + } + + function Nt(n, t, e, r) { + function u() { + var n, t = c.status; + if (!t && zt(c) || t >= 200 && 300 > t || 304 === t) { + try { + n = e.call(i, c) + } catch (r) { + return void o.error.call(i, r) + } + o.load.call(i, n) + } else o.error.call(i, c) + } + var i = {}, + o = ta.dispatch("beforesend", "progress", "load", "error"), + a = {}, + c = new XMLHttpRequest, + l = null; + return !this.XDomainRequest || "withCredentials" in c || !/^(http(s)?:)?\/\//.test(n) || (c = new XDomainRequest), "onload" in c ? c.onload = c.onerror = u : c.onreadystatechange = function() { + c.readyState > 3 && u() + }, c.onprogress = function(n) { + var t = ta.event; + ta.event = n; + try { + o.progress.call(i, c) + } finally { + ta.event = t + } + }, i.header = function(n, t) { + return n = (n + "").toLowerCase(), arguments.length < 2 ? a[n] : (null == t ? delete a[n] : a[n] = t + "", i) + }, i.mimeType = function(n) { + return arguments.length ? (t = null == n ? null : n + "", i) : t + }, i.responseType = function(n) { + return arguments.length ? (l = n, i) : l + }, i.response = function(n) { + return e = n, i + }, ["get", "post"].forEach(function(n) { + i[n] = function() { + return i.send.apply(i, [n].concat(ra(arguments))) + } + }), i.send = function(e, r, u) { + if (2 === arguments.length && "function" == typeof r && (u = r, r = null), c.open(e, n, !0), null == t || "accept" in a || (a.accept = t + ",*/*"), c.setRequestHeader) + for (var s in a) c.setRequestHeader(s, a[s]); + return null != t && c.overrideMimeType && c.overrideMimeType(t), null != l && (c.responseType = l), null != u && i.on("error", u).on("load", function(n) { + u(null, n) + }), o.beforesend.call(i, c), c.send(null == r ? null : r), i + }, i.abort = function() { + return c.abort(), i + }, ta.rebind(i, o, "on"), null == r ? i : i.get(Ct(r)) + } + + function Ct(n) { + return 1 === n.length ? function(t, e) { + n(null == t ? e : null) + } : n + } + + function zt(n) { + var t = n.responseType; + return t && "text" !== t ? n.response : n.responseText + } + + function qt() { + var n = Lt(), + t = Tt() - n; + t > 24 ? (isFinite(t) && (clearTimeout(tc), tc = setTimeout(qt, t)), nc = 0) : (nc = 1, rc(qt)) + } + + function Lt() { + var n = Date.now(); + for (ec = Ka; ec;) n >= ec.t && (ec.f = ec.c(n - ec.t)), ec = ec.n; + return n + } + + function Tt() { + for (var n, t = Ka, e = 1 / 0; t;) t.f ? t = n ? n.n = t.n : Ka = t.n : (t.t < e && (e = t.t), t = (n = t).n); + return Qa = n, e + } + + function Rt(n, t) { + return t - (n ? Math.ceil(Math.log(n) / Math.LN10) : 1) + } + + function Dt(n, t) { + var e = Math.pow(10, 3 * ga(8 - t)); + return { + scale: t > 8 ? function(n) { + return n / e + } : function(n) { + return n * e + }, + symbol: n + } + } + + function Pt(n) { + var t = n.decimal, + e = n.thousands, + r = n.grouping, + u = n.currency, + i = r && e ? function(n, t) { + for (var u = n.length, i = [], o = 0, a = r[0], c = 0; u > 0 && a > 0 && (c + a + 1 > t && (a = Math.max(1, t - c)), i.push(n.substring(u -= a, u + a)), !((c += a + 1) > t));) a = r[o = (o + 1) % r.length]; + return i.reverse().join(e) + } : y; + return function(n) { + var e = ic.exec(n), + r = e[1] || " ", + o = e[2] || ">", + a = e[3] || "-", + c = e[4] || "", + l = e[5], + s = +e[6], + f = e[7], + h = e[8], + g = e[9], + p = 1, + v = "", + d = "", + m = !1, + y = !0; + switch (h && (h = +h.substring(1)), (l || "0" === r && "=" === o) && (l = r = "0", o = "="), g) { + case "n": + f = !0, g = "g"; + break; + case "%": + p = 100, d = "%", g = "f"; + break; + case "p": + p = 100, d = "%", g = "r"; + break; + case "b": + case "o": + case "x": + case "X": + "#" === c && (v = "0" + g.toLowerCase()); + case "c": + y = !1; + case "d": + m = !0, h = 0; + break; + case "s": + p = -1, g = "r" + } + "$" === c && (v = u[0], d = u[1]), "r" != g || h || (g = "g"), null != h && ("g" == g ? h = Math.max(1, Math.min(21, h)) : ("e" == g || "f" == g) && (h = Math.max(0, Math.min(20, h)))), g = oc.get(g) || Ut; + var M = l && f; + return function(n) { + var e = d; + if (m && n % 1) return ""; + var u = 0 > n || 0 === n && 0 > 1 / n ? (n = -n, "-") : "-" === a ? "" : a; + if (0 > p) { + var c = ta.formatPrefix(n, h); + n = c.scale(n), e = c.symbol + d + } else n *= p; + n = g(n, h); + var x, b, _ = n.lastIndexOf("."); + if (0 > _) { + var w = y ? n.lastIndexOf("e") : -1; + 0 > w ? (x = n, b = "") : (x = n.substring(0, w), b = n.substring(w)) + } else x = n.substring(0, _), b = t + n.substring(_ + 1); + !l && f && (x = i(x, 1 / 0)); + var S = v.length + x.length + b.length + (M ? 0 : u.length), + k = s > S ? new Array(S = s - S + 1).join(r) : ""; + return M && (x = i(k + x, k.length ? s - b.length : 1 / 0)), u += v, n = x + b, ("<" === o ? u + n + k : ">" === o ? k + u + n : "^" === o ? k.substring(0, S >>= 1) + u + n + k.substring(S) : u + (M ? n : k + n)) + e + } + } + } + + function Ut(n) { + return n + "" + } + + function jt() { + this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]) + } + + function Ft(n, t, e) { + function r(t) { + var e = n(t), + r = i(e, 1); + return r - t > t - e ? e : r + } + + function u(e) { + return t(e = n(new cc(e - 1)), 1), e + } + + function i(n, e) { + return t(n = new cc(+n), e), n + } + + function o(n, r, i) { + var o = u(n), + a = []; + if (i > 1) + for (; r > o;) e(o) % i || a.push(new Date(+o)), t(o, 1); + else + for (; r > o;) a.push(new Date(+o)), t(o, 1); + return a + } + + function a(n, t, e) { + try { + cc = jt; + var r = new jt; + return r._ = n, o(r, t, e) + } finally { + cc = Date + } + } + n.floor = n, n.round = r, n.ceil = u, n.offset = i, n.range = o; + var c = n.utc = Ht(n); + return c.floor = c, c.round = Ht(r), c.ceil = Ht(u), c.offset = Ht(i), c.range = a, n + } + + function Ht(n) { + return function(t, e) { + try { + cc = jt; + var r = new jt; + return r._ = t, n(r, e)._ + } finally { + cc = Date + } + } + } + + function Ot(n) { + function t(n) { + function t(t) { + for (var e, u, i, o = [], a = -1, c = 0; ++a < r;) 37 === n.charCodeAt(a) && (o.push(n.slice(c, a)), null != (u = sc[e = n.charAt(++a)]) && (e = n.charAt(++a)), (i = N[e]) && (e = i(t, null == u ? "e" === e ? " " : "0" : u)), o.push(e), c = a + 1); + return o.push(n.slice(c, a)), o.join("") + } + var r = n.length; + return t.parse = function(t) { + var r = { + y: 1900, + m: 0, + d: 1, + H: 0, + M: 0, + S: 0, + L: 0, + Z: null + }, + u = e(r, n, t, 0); + if (u != t.length) return null; + "p" in r && (r.H = r.H % 12 + 12 * r.p); + var i = null != r.Z && cc !== jt, + o = new(i ? jt : cc); + return "j" in r ? o.setFullYear(r.y, 0, r.j) : "w" in r && ("W" in r || "U" in r) ? (o.setFullYear(r.y, 0, 1), o.setFullYear(r.y, 0, "W" in r ? (r.w + 6) % 7 + 7 * r.W - (o.getDay() + 5) % 7 : r.w + 7 * r.U - (o.getDay() + 6) % 7)) : o.setFullYear(r.y, r.m, r.d), o.setHours(r.H + (r.Z / 100 | 0), r.M + r.Z % 100, r.S, r.L), i ? o._ : o + }, t.toString = function() { + return n + }, t + } + + function e(n, t, e, r) { + for (var u, i, o, a = 0, c = t.length, l = e.length; c > a;) { + if (r >= l) return -1; + if (u = t.charCodeAt(a++), 37 === u) { + if (o = t.charAt(a++), i = C[o in sc ? t.charAt(a++) : o], !i || (r = i(n, e, r)) < 0) return -1 + } else if (u != e.charCodeAt(r++)) return -1 + } + return r + } + + function r(n, t, e) { + _.lastIndex = 0; + var r = _.exec(t.slice(e)); + return r ? (n.w = w.get(r[0].toLowerCase()), e + r[0].length) : -1 + } + + function u(n, t, e) { + x.lastIndex = 0; + var r = x.exec(t.slice(e)); + return r ? (n.w = b.get(r[0].toLowerCase()), e + r[0].length) : -1 + } + + function i(n, t, e) { + E.lastIndex = 0; + var r = E.exec(t.slice(e)); + return r ? (n.m = A.get(r[0].toLowerCase()), e + r[0].length) : -1 + } + + function o(n, t, e) { + S.lastIndex = 0; + var r = S.exec(t.slice(e)); + return r ? (n.m = k.get(r[0].toLowerCase()), e + r[0].length) : -1 + } + + function a(n, t, r) { + return e(n, N.c.toString(), t, r) + } + + function c(n, t, r) { + return e(n, N.x.toString(), t, r) + } + + function l(n, t, r) { + return e(n, N.X.toString(), t, r) + } + + function s(n, t, e) { + var r = M.get(t.slice(e, e += 2).toLowerCase()); + return null == r ? -1 : (n.p = r, e) + } + var f = n.dateTime, + h = n.date, + g = n.time, + p = n.periods, + v = n.days, + d = n.shortDays, + m = n.months, + y = n.shortMonths; + t.utc = function(n) { + function e(n) { + try { + cc = jt; + var t = new cc; + return t._ = n, r(t) + } finally { + cc = Date + } + } + var r = t(n); + return e.parse = function(n) { + try { + cc = jt; + var t = r.parse(n); + return t && t._ + } finally { + cc = Date + } + }, e.toString = r.toString, e + }, t.multi = t.utc.multi = ae; + var M = ta.map(), + x = Yt(v), + b = Zt(v), + _ = Yt(d), + w = Zt(d), + S = Yt(m), + k = Zt(m), + E = Yt(y), + A = Zt(y); + p.forEach(function(n, t) { + M.set(n.toLowerCase(), t) + }); + var N = { + a: function(n) { + return d[n.getDay()] + }, + A: function(n) { + return v[n.getDay()] + }, + b: function(n) { + return y[n.getMonth()] + }, + B: function(n) { + return m[n.getMonth()] + }, + c: t(f), + d: function(n, t) { + return It(n.getDate(), t, 2) + }, + e: function(n, t) { + return It(n.getDate(), t, 2) + }, + H: function(n, t) { + return It(n.getHours(), t, 2) + }, + I: function(n, t) { + return It(n.getHours() % 12 || 12, t, 2) + }, + j: function(n, t) { + return It(1 + ac.dayOfYear(n), t, 3) + }, + L: function(n, t) { + return It(n.getMilliseconds(), t, 3) + }, + m: function(n, t) { + return It(n.getMonth() + 1, t, 2) + }, + M: function(n, t) { + return It(n.getMinutes(), t, 2) + }, + p: function(n) { + return p[+(n.getHours() >= 12)] + }, + S: function(n, t) { + return It(n.getSeconds(), t, 2) + }, + U: function(n, t) { + return It(ac.sundayOfYear(n), t, 2) + }, + w: function(n) { + return n.getDay() + }, + W: function(n, t) { + return It(ac.mondayOfYear(n), t, 2) + }, + x: t(h), + X: t(g), + y: function(n, t) { + return It(n.getFullYear() % 100, t, 2) + }, + Y: function(n, t) { + return It(n.getFullYear() % 1e4, t, 4) + }, + Z: ie, + "%": function() { + return "%" + } + }, + C = { + a: r, + A: u, + b: i, + B: o, + c: a, + d: Qt, + e: Qt, + H: te, + I: te, + j: ne, + L: ue, + m: Kt, + M: ee, + p: s, + S: re, + U: Xt, + w: Vt, + W: $t, + x: c, + X: l, + y: Wt, + Y: Bt, + Z: Jt, + "%": oe + }; + return t + } + + function It(n, t, e) { + var r = 0 > n ? "-" : "", + u = (r ? -n : n) + "", + i = u.length; + return r + (e > i ? new Array(e - i + 1).join(t) + u : u) + } + + function Yt(n) { + return new RegExp("^(?:" + n.map(ta.requote).join("|") + ")", "i") + } + + function Zt(n) { + for (var t = new l, e = -1, r = n.length; ++e < r;) t.set(n[e].toLowerCase(), e); + return t + } + + function Vt(n, t, e) { + fc.lastIndex = 0; + var r = fc.exec(t.slice(e, e + 1)); + return r ? (n.w = +r[0], e + r[0].length) : -1 + } + + function Xt(n, t, e) { + fc.lastIndex = 0; + var r = fc.exec(t.slice(e)); + return r ? (n.U = +r[0], e + r[0].length) : -1 + } + + function $t(n, t, e) { + fc.lastIndex = 0; + var r = fc.exec(t.slice(e)); + return r ? (n.W = +r[0], e + r[0].length) : -1 + } + + function Bt(n, t, e) { + fc.lastIndex = 0; + var r = fc.exec(t.slice(e, e + 4)); + return r ? (n.y = +r[0], e + r[0].length) : -1 + } + + function Wt(n, t, e) { + fc.lastIndex = 0; + var r = fc.exec(t.slice(e, e + 2)); + return r ? (n.y = Gt(+r[0]), e + r[0].length) : -1 + } + + function Jt(n, t, e) { + return /^[+-]\d{4}$/.test(t = t.slice(e, e + 5)) ? (n.Z = -t, e + 5) : -1 + } + + function Gt(n) { + return n + (n > 68 ? 1900 : 2e3) + } + + function Kt(n, t, e) { + fc.lastIndex = 0; + var r = fc.exec(t.slice(e, e + 2)); + return r ? (n.m = r[0] - 1, e + r[0].length) : -1 + } + + function Qt(n, t, e) { + fc.lastIndex = 0; + var r = fc.exec(t.slice(e, e + 2)); + return r ? (n.d = +r[0], e + r[0].length) : -1 + } + + function ne(n, t, e) { + fc.lastIndex = 0; + var r = fc.exec(t.slice(e, e + 3)); + return r ? (n.j = +r[0], e + r[0].length) : -1 + } + + function te(n, t, e) { + fc.lastIndex = 0; + var r = fc.exec(t.slice(e, e + 2)); + return r ? (n.H = +r[0], e + r[0].length) : -1 + } + + function ee(n, t, e) { + fc.lastIndex = 0; + var r = fc.exec(t.slice(e, e + 2)); + return r ? (n.M = +r[0], e + r[0].length) : -1 + } + + function re(n, t, e) { + fc.lastIndex = 0; + var r = fc.exec(t.slice(e, e + 2)); + return r ? (n.S = +r[0], e + r[0].length) : -1 + } + + function ue(n, t, e) { + fc.lastIndex = 0; + var r = fc.exec(t.slice(e, e + 3)); + return r ? (n.L = +r[0], e + r[0].length) : -1 + } + + function ie(n) { + var t = n.getTimezoneOffset(), + e = t > 0 ? "-" : "+", + r = ga(t) / 60 | 0, + u = ga(t) % 60; + return e + It(r, "0", 2) + It(u, "0", 2) + } + + function oe(n, t, e) { + hc.lastIndex = 0; + var r = hc.exec(t.slice(e, e + 1)); + return r ? e + r[0].length : -1 + } + + function ae(n) { + for (var t = n.length, e = -1; ++e < t;) n[e][0] = this(n[e][0]); + return function(t) { + for (var e = 0, r = n[e]; !r[1](t);) r = n[++e]; + return r[0](t) + } + } + + function ce() {} + + function le(n, t, e) { + var r = e.s = n + t, + u = r - n, + i = r - u; + e.t = n - i + (t - u) + } + + function se(n, t) { + n && dc.hasOwnProperty(n.type) && dc[n.type](n, t) + } + + function fe(n, t, e) { + var r, u = -1, + i = n.length - e; + for (t.lineStart(); ++u < i;) r = n[u], t.point(r[0], r[1], r[2]); + t.lineEnd() + } + + function he(n, t) { + var e = -1, + r = n.length; + for (t.polygonStart(); ++e < r;) fe(n[e], t, 1); + t.polygonEnd() + } + + function ge() { + function n(n, t) { + n *= Da, t = t * Da / 2 + qa / 4; + var e = n - r, + o = e >= 0 ? 1 : -1, + a = o * e, + c = Math.cos(t), + l = Math.sin(t), + s = i * l, + f = u * c + s * Math.cos(a), + h = s * o * Math.sin(a); + yc.add(Math.atan2(h, f)), r = n, u = c, i = l + } + var t, e, r, u, i; + Mc.point = function(o, a) { + Mc.point = n, r = (t = o) * Da, u = Math.cos(a = (e = a) * Da / 2 + qa / 4), i = Math.sin(a) + }, Mc.lineEnd = function() { + n(t, e) + } + } + + function pe(n) { + var t = n[0], + e = n[1], + r = Math.cos(e); + return [r * Math.cos(t), r * Math.sin(t), Math.sin(e)] + } + + function ve(n, t) { + return n[0] * t[0] + n[1] * t[1] + n[2] * t[2] + } + + function de(n, t) { + return [n[1] * t[2] - n[2] * t[1], n[2] * t[0] - n[0] * t[2], n[0] * t[1] - n[1] * t[0]] + } + + function me(n, t) { + n[0] += t[0], n[1] += t[1], n[2] += t[2] + } + + function ye(n, t) { + return [n[0] * t, n[1] * t, n[2] * t] + } + + function Me(n) { + var t = Math.sqrt(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]); + n[0] /= t, n[1] /= t, n[2] /= t + } + + function xe(n) { + return [Math.atan2(n[1], n[0]), tt(n[2])] + } + + function be(n, t) { + return ga(n[0] - t[0]) < Ca && ga(n[1] - t[1]) < Ca + } + + function _e(n, t) { + n *= Da; + var e = Math.cos(t *= Da); + we(e * Math.cos(n), e * Math.sin(n), Math.sin(t)) + } + + function we(n, t, e) { + ++xc, _c += (n - _c) / xc, wc += (t - wc) / xc, Sc += (e - Sc) / xc + } + + function Se() { + function n(n, u) { + n *= Da; + var i = Math.cos(u *= Da), + o = i * Math.cos(n), + a = i * Math.sin(n), + c = Math.sin(u), + l = Math.atan2(Math.sqrt((l = e * c - r * a) * l + (l = r * o - t * c) * l + (l = t * a - e * o) * l), t * o + e * a + r * c); + bc += l, kc += l * (t + (t = o)), Ec += l * (e + (e = a)), Ac += l * (r + (r = c)), we(t, e, r) + } + var t, e, r; + qc.point = function(u, i) { + u *= Da; + var o = Math.cos(i *= Da); + t = o * Math.cos(u), e = o * Math.sin(u), r = Math.sin(i), qc.point = n, we(t, e, r) + } + } + + function ke() { + qc.point = _e + } + + function Ee() { + function n(n, t) { + n *= Da; + var e = Math.cos(t *= Da), + o = e * Math.cos(n), + a = e * Math.sin(n), + c = Math.sin(t), + l = u * c - i * a, + s = i * o - r * c, + f = r * a - u * o, + h = Math.sqrt(l * l + s * s + f * f), + g = r * o + u * a + i * c, + p = h && -nt(g) / h, + v = Math.atan2(h, g); + Nc += p * l, Cc += p * s, zc += p * f, bc += v, kc += v * (r + (r = o)), Ec += v * (u + (u = a)), Ac += v * (i + (i = c)), we(r, u, i) + } + var t, e, r, u, i; + qc.point = function(o, a) { + t = o, e = a, qc.point = n, o *= Da; + var c = Math.cos(a *= Da); + r = c * Math.cos(o), u = c * Math.sin(o), i = Math.sin(a), we(r, u, i) + }, qc.lineEnd = function() { + n(t, e), qc.lineEnd = ke, qc.point = _e + } + } + + function Ae(n, t) { + function e(e, r) { + return e = n(e, r), t(e[0], e[1]) + } + return n.invert && t.invert && (e.invert = function(e, r) { + return e = t.invert(e, r), e && n.invert(e[0], e[1]) + }), e + } + + function Ne() { + return !0 + } + + function Ce(n, t, e, r, u) { + var i = [], + o = []; + if (n.forEach(function(n) { + if (!((t = n.length - 1) <= 0)) { + var t, e = n[0], + r = n[t]; + if (be(e, r)) { + u.lineStart(); + for (var a = 0; t > a; ++a) u.point((e = n[a])[0], e[1]); + return void u.lineEnd() + } + var c = new qe(e, n, null, !0), + l = new qe(e, null, c, !1); + c.o = l, i.push(c), o.push(l), c = new qe(r, n, null, !1), l = new qe(r, null, c, !0), c.o = l, i.push(c), o.push(l) + } + }), o.sort(t), ze(i), ze(o), i.length) { + for (var a = 0, c = e, l = o.length; l > a; ++a) o[a].e = c = !c; + for (var s, f, h = i[0];;) { + for (var g = h, p = !0; g.v;) + if ((g = g.n) === h) return; + s = g.z, u.lineStart(); + do { + if (g.v = g.o.v = !0, g.e) { + if (p) + for (var a = 0, l = s.length; l > a; ++a) u.point((f = s[a])[0], f[1]); + else r(g.x, g.n.x, 1, u); + g = g.n + } else { + if (p) { + s = g.p.z; + for (var a = s.length - 1; a >= 0; --a) u.point((f = s[a])[0], f[1]) + } else r(g.x, g.p.x, -1, u); + g = g.p + } + g = g.o, s = g.z, p = !p + } while (!g.v); + u.lineEnd() + } + } + } + + function ze(n) { + if (t = n.length) { + for (var t, e, r = 0, u = n[0]; ++r < t;) u.n = e = n[r], e.p = u, u = e; + u.n = e = n[0], e.p = u + } + } + + function qe(n, t, e, r) { + this.x = n, this.z = t, this.o = e, this.e = r, this.v = !1, this.n = this.p = null + } + + function Le(n, t, e, r) { + return function(u, i) { + function o(t, e) { + var r = u(t, e); + n(t = r[0], e = r[1]) && i.point(t, e) + } + + function a(n, t) { + var e = u(n, t); + d.point(e[0], e[1]) + } + + function c() { + y.point = a, d.lineStart() + } + + function l() { + y.point = o, d.lineEnd() + } + + function s(n, t) { + v.push([n, t]); + var e = u(n, t); + x.point(e[0], e[1]) + } + + function f() { + x.lineStart(), v = [] + } + + function h() { + s(v[0][0], v[0][1]), x.lineEnd(); + var n, t = x.clean(), + e = M.buffer(), + r = e.length; + if (v.pop(), p.push(v), v = null, r) + if (1 & t) { + n = e[0]; + var u, r = n.length - 1, + o = -1; + if (r > 0) { + for (b || (i.polygonStart(), b = !0), i.lineStart(); ++o < r;) i.point((u = n[o])[0], u[1]); + i.lineEnd() + } + } else r > 1 && 2 & t && e.push(e.pop().concat(e.shift())), g.push(e.filter(Te)) + } + var g, p, v, d = t(i), + m = u.invert(r[0], r[1]), + y = { + point: o, + lineStart: c, + lineEnd: l, + polygonStart: function() { + y.point = s, y.lineStart = f, y.lineEnd = h, g = [], p = [] + }, + polygonEnd: function() { + y.point = o, y.lineStart = c, y.lineEnd = l, g = ta.merge(g); + var n = Fe(m, p); + g.length ? (b || (i.polygonStart(), b = !0), Ce(g, De, n, e, i)) : n && (b || (i.polygonStart(), b = !0), i.lineStart(), e(null, null, 1, i), i.lineEnd()), b && (i.polygonEnd(), b = !1), g = p = null + }, + sphere: function() { + i.polygonStart(), i.lineStart(), e(null, null, 1, i), i.lineEnd(), i.polygonEnd() + } + }, + M = Re(), + x = t(M), + b = !1; + return y + } + } + + function Te(n) { + return n.length > 1 + } + + function Re() { + var n, t = []; + return { + lineStart: function() { + t.push(n = []) + }, + point: function(t, e) { + n.push([t, e]) + }, + lineEnd: b, + buffer: function() { + var e = t; + return t = [], n = null, e + }, + rejoin: function() { + t.length > 1 && t.push(t.pop().concat(t.shift())) + } + } + } + + function De(n, t) { + return ((n = n.x)[0] < 0 ? n[1] - Ra - Ca : Ra - n[1]) - ((t = t.x)[0] < 0 ? t[1] - Ra - Ca : Ra - t[1]) + } + + function Pe(n) { + var t, e = 0 / 0, + r = 0 / 0, + u = 0 / 0; + return { + lineStart: function() { + n.lineStart(), t = 1 + }, + point: function(i, o) { + var a = i > 0 ? qa : -qa, + c = ga(i - e); + ga(c - qa) < Ca ? (n.point(e, r = (r + o) / 2 > 0 ? Ra : -Ra), n.point(u, r), n.lineEnd(), n.lineStart(), n.point(a, r), n.point(i, r), t = 0) : u !== a && c >= qa && (ga(e - u) < Ca && (e -= u * Ca), ga(i - a) < Ca && (i -= a * Ca), r = Ue(e, r, i, o), n.point(u, r), n.lineEnd(), n.lineStart(), n.point(a, r), t = 0), n.point(e = i, r = o), u = a + }, + lineEnd: function() { + n.lineEnd(), e = r = 0 / 0 + }, + clean: function() { + return 2 - t + } + } + } + + function Ue(n, t, e, r) { + var u, i, o = Math.sin(n - e); + return ga(o) > Ca ? Math.atan((Math.sin(t) * (i = Math.cos(r)) * Math.sin(e) - Math.sin(r) * (u = Math.cos(t)) * Math.sin(n)) / (u * i * o)) : (t + r) / 2 + } + + function je(n, t, e, r) { + var u; + if (null == n) u = e * Ra, r.point(-qa, u), r.point(0, u), r.point(qa, u), r.point(qa, 0), r.point(qa, -u), r.point(0, -u), r.point(-qa, -u), r.point(-qa, 0), r.point(-qa, u); + else if (ga(n[0] - t[0]) > Ca) { + var i = n[0] < t[0] ? qa : -qa; + u = e * i / 2, r.point(-i, u), r.point(0, u), r.point(i, u) + } else r.point(t[0], t[1]) + } + + function Fe(n, t) { + var e = n[0], + r = n[1], + u = [Math.sin(e), -Math.cos(e), 0], + i = 0, + o = 0; + yc.reset(); + for (var a = 0, c = t.length; c > a; ++a) { + var l = t[a], + s = l.length; + if (s) + for (var f = l[0], h = f[0], g = f[1] / 2 + qa / 4, p = Math.sin(g), v = Math.cos(g), d = 1;;) { + d === s && (d = 0), n = l[d]; + var m = n[0], + y = n[1] / 2 + qa / 4, + M = Math.sin(y), + x = Math.cos(y), + b = m - h, + _ = b >= 0 ? 1 : -1, + w = _ * b, + S = w > qa, + k = p * M; + if (yc.add(Math.atan2(k * _ * Math.sin(w), v * x + k * Math.cos(w))), i += S ? b + _ * La : b, S ^ h >= e ^ m >= e) { + var E = de(pe(f), pe(n)); + Me(E); + var A = de(u, E); + Me(A); + var N = (S ^ b >= 0 ? -1 : 1) * tt(A[2]); + (r > N || r === N && (E[0] || E[1])) && (o += S ^ b >= 0 ? 1 : -1) + } + if (!d++) break; + h = m, p = M, v = x, f = n + } + } + return (-Ca > i || Ca > i && 0 > yc) ^ 1 & o + } + + function He(n) { + function t(n, t) { + return Math.cos(n) * Math.cos(t) > i + } + + function e(n) { + var e, i, c, l, s; + return { + lineStart: function() { + l = c = !1, s = 1 + }, + point: function(f, h) { + var g, p = [f, h], + v = t(f, h), + d = o ? v ? 0 : u(f, h) : v ? u(f + (0 > f ? qa : -qa), h) : 0; + if (!e && (l = c = v) && n.lineStart(), v !== c && (g = r(e, p), (be(e, g) || be(p, g)) && (p[0] += Ca, p[1] += Ca, v = t(p[0], p[1]))), v !== c) s = 0, v ? (n.lineStart(), g = r(p, e), n.point(g[0], g[1])) : (g = r(e, p), n.point(g[0], g[1]), n.lineEnd()), e = g; + else if (a && e && o ^ v) { + var m; + d & i || !(m = r(p, e, !0)) || (s = 0, o ? (n.lineStart(), n.point(m[0][0], m[0][1]), n.point(m[1][0], m[1][1]), n.lineEnd()) : (n.point(m[1][0], m[1][1]), n.lineEnd(), n.lineStart(), n.point(m[0][0], m[0][1]))) + }!v || e && be(e, p) || n.point(p[0], p[1]), e = p, c = v, i = d + }, + lineEnd: function() { + c && n.lineEnd(), e = null + }, + clean: function() { + return s | (l && c) << 1 + } + } + } + + function r(n, t, e) { + var r = pe(n), + u = pe(t), + o = [1, 0, 0], + a = de(r, u), + c = ve(a, a), + l = a[0], + s = c - l * l; + if (!s) return !e && n; + var f = i * c / s, + h = -i * l / s, + g = de(o, a), + p = ye(o, f), + v = ye(a, h); + me(p, v); + var d = g, + m = ve(p, d), + y = ve(d, d), + M = m * m - y * (ve(p, p) - 1); + if (!(0 > M)) { + var x = Math.sqrt(M), + b = ye(d, (-m - x) / y); + if (me(b, p), b = xe(b), !e) return b; + var _, w = n[0], + S = t[0], + k = n[1], + E = t[1]; + w > S && (_ = w, w = S, S = _); + var A = S - w, + N = ga(A - qa) < Ca, + C = N || Ca > A; + if (!N && k > E && (_ = k, k = E, E = _), C ? N ? k + E > 0 ^ b[1] < (ga(b[0] - w) < Ca ? k : E) : k <= b[1] && b[1] <= E : A > qa ^ (w <= b[0] && b[0] <= S)) { + var z = ye(d, (-m + x) / y); + return me(z, p), [b, xe(z)] + } + } + } + + function u(t, e) { + var r = o ? n : qa - n, + u = 0; + return -r > t ? u |= 1 : t > r && (u |= 2), -r > e ? u |= 4 : e > r && (u |= 8), u + } + var i = Math.cos(n), + o = i > 0, + a = ga(i) > Ca, + c = gr(n, 6 * Da); + return Le(t, e, c, o ? [0, -n] : [-qa, n - qa]) + } + + function Oe(n, t, e, r) { + return function(u) { + var i, o = u.a, + a = u.b, + c = o.x, + l = o.y, + s = a.x, + f = a.y, + h = 0, + g = 1, + p = s - c, + v = f - l; + if (i = n - c, p || !(i > 0)) { + if (i /= p, 0 > p) { + if (h > i) return; + g > i && (g = i) + } else if (p > 0) { + if (i > g) return; + i > h && (h = i) + } + if (i = e - c, p || !(0 > i)) { + if (i /= p, 0 > p) { + if (i > g) return; + i > h && (h = i) + } else if (p > 0) { + if (h > i) return; + g > i && (g = i) + } + if (i = t - l, v || !(i > 0)) { + if (i /= v, 0 > v) { + if (h > i) return; + g > i && (g = i) + } else if (v > 0) { + if (i > g) return; + i > h && (h = i) + } + if (i = r - l, v || !(0 > i)) { + if (i /= v, 0 > v) { + if (i > g) return; + i > h && (h = i) + } else if (v > 0) { + if (h > i) return; + g > i && (g = i) + } + return h > 0 && (u.a = { + x: c + h * p, + y: l + h * v + }), 1 > g && (u.b = { + x: c + g * p, + y: l + g * v + }), u + } + } + } + } + } + } + + function Ie(n, t, e, r) { + function u(r, u) { + return ga(r[0] - n) < Ca ? u > 0 ? 0 : 3 : ga(r[0] - e) < Ca ? u > 0 ? 2 : 1 : ga(r[1] - t) < Ca ? u > 0 ? 1 : 0 : u > 0 ? 3 : 2 + } + + function i(n, t) { + return o(n.x, t.x) + } + + function o(n, t) { + var e = u(n, 1), + r = u(t, 1); + return e !== r ? e - r : 0 === e ? t[1] - n[1] : 1 === e ? n[0] - t[0] : 2 === e ? n[1] - t[1] : t[0] - n[0] + } + return function(a) { + function c(n) { + for (var t = 0, e = d.length, r = n[1], u = 0; e > u; ++u) + for (var i, o = 1, a = d[u], c = a.length, l = a[0]; c > o; ++o) i = a[o], l[1] <= r ? i[1] > r && Q(l, i, n) > 0 && ++t : i[1] <= r && Q(l, i, n) < 0 && --t, l = i; + return 0 !== t + } + + function l(i, a, c, l) { + var s = 0, + f = 0; + if (null == i || (s = u(i, c)) !== (f = u(a, c)) || o(i, a) < 0 ^ c > 0) { + do l.point(0 === s || 3 === s ? n : e, s > 1 ? r : t); while ((s = (s + c + 4) % 4) !== f) + } else l.point(a[0], a[1]) + } + + function s(u, i) { + return u >= n && e >= u && i >= t && r >= i + } + + function f(n, t) { + s(n, t) && a.point(n, t) + } + + function h() { + C.point = p, d && d.push(m = []), S = !0, w = !1, b = _ = 0 / 0 + } + + function g() { + v && (p(y, M), x && w && A.rejoin(), v.push(A.buffer())), C.point = f, w && a.lineEnd() + } + + function p(n, t) { + n = Math.max(-Tc, Math.min(Tc, n)), t = Math.max(-Tc, Math.min(Tc, t)); + var e = s(n, t); + if (d && m.push([n, t]), S) y = n, M = t, x = e, S = !1, e && (a.lineStart(), a.point(n, t)); + else if (e && w) a.point(n, t); + else { + var r = { + a: { + x: b, + y: _ + }, + b: { + x: n, + y: t + } + }; + N(r) ? (w || (a.lineStart(), a.point(r.a.x, r.a.y)), a.point(r.b.x, r.b.y), e || a.lineEnd(), k = !1) : e && (a.lineStart(), a.point(n, t), k = !1) + } + b = n, _ = t, w = e + } + var v, d, m, y, M, x, b, _, w, S, k, E = a, + A = Re(), + N = Oe(n, t, e, r), + C = { + point: f, + lineStart: h, + lineEnd: g, + polygonStart: function() { + a = A, v = [], d = [], k = !0 + }, + polygonEnd: function() { + a = E, v = ta.merge(v); + var t = c([n, r]), + e = k && t, + u = v.length; + (e || u) && (a.polygonStart(), e && (a.lineStart(), l(null, null, 1, a), a.lineEnd()), u && Ce(v, i, t, l, a), a.polygonEnd()), v = d = m = null + } + }; + return C + } + } + + function Ye(n) { + var t = 0, + e = qa / 3, + r = ir(n), + u = r(t, e); + return u.parallels = function(n) { + return arguments.length ? r(t = n[0] * qa / 180, e = n[1] * qa / 180) : [t / qa * 180, e / qa * 180] + }, u + } + + function Ze(n, t) { + function e(n, t) { + var e = Math.sqrt(i - 2 * u * Math.sin(t)) / u; + return [e * Math.sin(n *= u), o - e * Math.cos(n)] + } + var r = Math.sin(n), + u = (r + Math.sin(t)) / 2, + i = 1 + r * (2 * u - r), + o = Math.sqrt(i) / u; + return e.invert = function(n, t) { + var e = o - t; + return [Math.atan2(n, e) / u, tt((i - (n * n + e * e) * u * u) / (2 * u))] + }, e + } + + function Ve() { + function n(n, t) { + Dc += u * n - r * t, r = n, u = t + } + var t, e, r, u; + Hc.point = function(i, o) { + Hc.point = n, t = r = i, e = u = o + }, Hc.lineEnd = function() { + n(t, e) + } + } + + function Xe(n, t) { + Pc > n && (Pc = n), n > jc && (jc = n), Uc > t && (Uc = t), t > Fc && (Fc = t) + } + + function $e() { + function n(n, t) { + o.push("M", n, ",", t, i) + } + + function t(n, t) { + o.push("M", n, ",", t), a.point = e + } + + function e(n, t) { + o.push("L", n, ",", t) + } + + function r() { + a.point = n + } + + function u() { + o.push("Z") + } + var i = Be(4.5), + o = [], + a = { + point: n, + lineStart: function() { + a.point = t + }, + lineEnd: r, + polygonStart: function() { + a.lineEnd = u + }, + polygonEnd: function() { + a.lineEnd = r, a.point = n + }, + pointRadius: function(n) { + return i = Be(n), a + }, + result: function() { + if (o.length) { + var n = o.join(""); + return o = [], n + } + } + }; + return a + } + + function Be(n) { + return "m0," + n + "a" + n + "," + n + " 0 1,1 0," + -2 * n + "a" + n + "," + n + " 0 1,1 0," + 2 * n + "z" + } + + function We(n, t) { + _c += n, wc += t, ++Sc + } + + function Je() { + function n(n, r) { + var u = n - t, + i = r - e, + o = Math.sqrt(u * u + i * i); + kc += o * (t + n) / 2, Ec += o * (e + r) / 2, Ac += o, We(t = n, e = r) + } + var t, e; + Ic.point = function(r, u) { + Ic.point = n, We(t = r, e = u) + } + } + + function Ge() { + Ic.point = We + } + + function Ke() { + function n(n, t) { + var e = n - r, + i = t - u, + o = Math.sqrt(e * e + i * i); + kc += o * (r + n) / 2, Ec += o * (u + t) / 2, Ac += o, o = u * n - r * t, Nc += o * (r + n), Cc += o * (u + t), zc += 3 * o, We(r = n, u = t) + } + var t, e, r, u; + Ic.point = function(i, o) { + Ic.point = n, We(t = r = i, e = u = o) + }, Ic.lineEnd = function() { + n(t, e) + } + } + + function Qe(n) { + function t(t, e) { + n.moveTo(t + o, e), n.arc(t, e, o, 0, La) + } + + function e(t, e) { + n.moveTo(t, e), a.point = r + } + + function r(t, e) { + n.lineTo(t, e) + } + + function u() { + a.point = t + } + + function i() { + n.closePath() + } + var o = 4.5, + a = { + point: t, + lineStart: function() { + a.point = e + }, + lineEnd: u, + polygonStart: function() { + a.lineEnd = i + }, + polygonEnd: function() { + a.lineEnd = u, a.point = t + }, + pointRadius: function(n) { + return o = n, a + }, + result: b + }; + return a + } + + function nr(n) { + function t(n) { + return (a ? r : e)(n) + } + + function e(t) { + return rr(t, function(e, r) { + e = n(e, r), t.point(e[0], e[1]) + }) + } + + function r(t) { + function e(e, r) { + e = n(e, r), t.point(e[0], e[1]) + } + + function r() { + M = 0 / 0, S.point = i, t.lineStart() + } + + function i(e, r) { + var i = pe([e, r]), + o = n(e, r); + u(M, x, y, b, _, w, M = o[0], x = o[1], y = e, b = i[0], _ = i[1], w = i[2], a, t), t.point(M, x) + } + + function o() { + S.point = e, t.lineEnd() + } + + function c() { + r(), S.point = l, S.lineEnd = s + } + + function l(n, t) { + i(f = n, h = t), g = M, p = x, v = b, d = _, m = w, S.point = i + } + + function s() { + u(M, x, y, b, _, w, g, p, f, v, d, m, a, t), S.lineEnd = o, o() + } + var f, h, g, p, v, d, m, y, M, x, b, _, w, S = { + point: e, + lineStart: r, + lineEnd: o, + polygonStart: function() { + t.polygonStart(), S.lineStart = c + }, + polygonEnd: function() { + t.polygonEnd(), S.lineStart = r + } + }; + return S + } + + function u(t, e, r, a, c, l, s, f, h, g, p, v, d, m) { + var y = s - t, + M = f - e, + x = y * y + M * M; + if (x > 4 * i && d--) { + var b = a + g, + _ = c + p, + w = l + v, + S = Math.sqrt(b * b + _ * _ + w * w), + k = Math.asin(w /= S), + E = ga(ga(w) - 1) < Ca || ga(r - h) < Ca ? (r + h) / 2 : Math.atan2(_, b), + A = n(E, k), + N = A[0], + C = A[1], + z = N - t, + q = C - e, + L = M * z - y * q; + (L * L / x > i || ga((y * z + M * q) / x - .5) > .3 || o > a * g + c * p + l * v) && (u(t, e, r, a, c, l, N, C, E, b /= S, _ /= S, w, d, m), m.point(N, C), u(N, C, E, b, _, w, s, f, h, g, p, v, d, m)) + } + } + var i = .5, + o = Math.cos(30 * Da), + a = 16; + return t.precision = function(n) { + return arguments.length ? (a = (i = n * n) > 0 && 16, t) : Math.sqrt(i) + }, t + } + + function tr(n) { + var t = nr(function(t, e) { + return n([t * Pa, e * Pa]) + }); + return function(n) { + return or(t(n)) + } + } + + function er(n) { + this.stream = n + } + + function rr(n, t) { + return { + point: t, + sphere: function() { + n.sphere() + }, + lineStart: function() { + n.lineStart() + }, + lineEnd: function() { + n.lineEnd() + }, + polygonStart: function() { + n.polygonStart() + }, + polygonEnd: function() { + n.polygonEnd() + } + } + } + + function ur(n) { + return ir(function() { + return n + })() + } + + function ir(n) { + function t(n) { + return n = a(n[0] * Da, n[1] * Da), [n[0] * h + c, l - n[1] * h] + } + + function e(n) { + return n = a.invert((n[0] - c) / h, (l - n[1]) / h), n && [n[0] * Pa, n[1] * Pa] + } + + function r() { + a = Ae(o = lr(m, M, x), i); + var n = i(v, d); + return c = g - n[0] * h, l = p + n[1] * h, u() + } + + function u() { + return s && (s.valid = !1, s = null), t + } + var i, o, a, c, l, s, f = nr(function(n, t) { + return n = i(n, t), [n[0] * h + c, l - n[1] * h] + }), + h = 150, + g = 480, + p = 250, + v = 0, + d = 0, + m = 0, + M = 0, + x = 0, + b = Lc, + _ = y, + w = null, + S = null; + return t.stream = function(n) { + return s && (s.valid = !1), s = or(b(o, f(_(n)))), s.valid = !0, s + }, t.clipAngle = function(n) { + return arguments.length ? (b = null == n ? (w = n, Lc) : He((w = +n) * Da), u()) : w + }, t.clipExtent = function(n) { + return arguments.length ? (S = n, _ = n ? Ie(n[0][0], n[0][1], n[1][0], n[1][1]) : y, u()) : S + }, t.scale = function(n) { + return arguments.length ? (h = +n, r()) : h + }, t.translate = function(n) { + return arguments.length ? (g = +n[0], p = +n[1], r()) : [g, p] + }, t.center = function(n) { + return arguments.length ? (v = n[0] % 360 * Da, d = n[1] % 360 * Da, r()) : [v * Pa, d * Pa] + }, t.rotate = function(n) { + return arguments.length ? (m = n[0] % 360 * Da, M = n[1] % 360 * Da, x = n.length > 2 ? n[2] % 360 * Da : 0, r()) : [m * Pa, M * Pa, x * Pa] + }, ta.rebind(t, f, "precision"), + function() { + return i = n.apply(this, arguments), t.invert = i.invert && e, r() + } + } + + function or(n) { + return rr(n, function(t, e) { + n.point(t * Da, e * Da) + }) + } + + function ar(n, t) { + return [n, t] + } + + function cr(n, t) { + return [n > qa ? n - La : -qa > n ? n + La : n, t] + } + + function lr(n, t, e) { + return n ? t || e ? Ae(fr(n), hr(t, e)) : fr(n) : t || e ? hr(t, e) : cr + } + + function sr(n) { + return function(t, e) { + return t += n, [t > qa ? t - La : -qa > t ? t + La : t, e] + } + } + + function fr(n) { + var t = sr(n); + return t.invert = sr(-n), t + } + + function hr(n, t) { + function e(n, t) { + var e = Math.cos(t), + a = Math.cos(n) * e, + c = Math.sin(n) * e, + l = Math.sin(t), + s = l * r + a * u; + return [Math.atan2(c * i - s * o, a * r - l * u), tt(s * i + c * o)] + } + var r = Math.cos(n), + u = Math.sin(n), + i = Math.cos(t), + o = Math.sin(t); + return e.invert = function(n, t) { + var e = Math.cos(t), + a = Math.cos(n) * e, + c = Math.sin(n) * e, + l = Math.sin(t), + s = l * i - c * o; + return [Math.atan2(c * i + l * o, a * r + s * u), tt(s * r - a * u)] + }, e + } + + function gr(n, t) { + var e = Math.cos(n), + r = Math.sin(n); + return function(u, i, o, a) { + var c = o * t; + null != u ? (u = pr(e, u), i = pr(e, i), (o > 0 ? i > u : u > i) && (u += o * La)) : (u = n + o * La, i = n - .5 * c); + for (var l, s = u; o > 0 ? s > i : i > s; s -= c) a.point((l = xe([e, -r * Math.cos(s), -r * Math.sin(s)]))[0], l[1]) + } + } + + function pr(n, t) { + var e = pe(t); + e[0] -= n, Me(e); + var r = nt(-e[1]); + return ((-e[2] < 0 ? -r : r) + 2 * Math.PI - Ca) % (2 * Math.PI) + } + + function vr(n, t, e) { + var r = ta.range(n, t - Ca, e).concat(t); + return function(n) { + return r.map(function(t) { + return [n, t] + }) + } + } + + function dr(n, t, e) { + var r = ta.range(n, t - Ca, e).concat(t); + return function(n) { + return r.map(function(t) { + return [t, n] + }) + } + } + + function mr(n) { + return n.source + } + + function yr(n) { + return n.target + } + + function Mr(n, t, e, r) { + var u = Math.cos(t), + i = Math.sin(t), + o = Math.cos(r), + a = Math.sin(r), + c = u * Math.cos(n), + l = u * Math.sin(n), + s = o * Math.cos(e), + f = o * Math.sin(e), + h = 2 * Math.asin(Math.sqrt(it(r - t) + u * o * it(e - n))), + g = 1 / Math.sin(h), + p = h ? function(n) { + var t = Math.sin(n *= h) * g, + e = Math.sin(h - n) * g, + r = e * c + t * s, + u = e * l + t * f, + o = e * i + t * a; + return [Math.atan2(u, r) * Pa, Math.atan2(o, Math.sqrt(r * r + u * u)) * Pa] + } : function() { + return [n * Pa, t * Pa] + }; + return p.distance = h, p + } + + function xr() { + function n(n, u) { + var i = Math.sin(u *= Da), + o = Math.cos(u), + a = ga((n *= Da) - t), + c = Math.cos(a); + Yc += Math.atan2(Math.sqrt((a = o * Math.sin(a)) * a + (a = r * i - e * o * c) * a), e * i + r * o * c), t = n, e = i, r = o + } + var t, e, r; + Zc.point = function(u, i) { + t = u * Da, e = Math.sin(i *= Da), r = Math.cos(i), Zc.point = n + }, Zc.lineEnd = function() { + Zc.point = Zc.lineEnd = b + } + } + + function br(n, t) { + function e(t, e) { + var r = Math.cos(t), + u = Math.cos(e), + i = n(r * u); + return [i * u * Math.sin(t), i * Math.sin(e)] + } + return e.invert = function(n, e) { + var r = Math.sqrt(n * n + e * e), + u = t(r), + i = Math.sin(u), + o = Math.cos(u); + return [Math.atan2(n * i, r * o), Math.asin(r && e * i / r)] + }, e + } + + function _r(n, t) { + function e(n, t) { + o > 0 ? -Ra + Ca > t && (t = -Ra + Ca) : t > Ra - Ca && (t = Ra - Ca); + var e = o / Math.pow(u(t), i); + return [e * Math.sin(i * n), o - e * Math.cos(i * n)] + } + var r = Math.cos(n), + u = function(n) { + return Math.tan(qa / 4 + n / 2) + }, + i = n === t ? Math.sin(n) : Math.log(r / Math.cos(t)) / Math.log(u(t) / u(n)), + o = r * Math.pow(u(n), i) / i; + return i ? (e.invert = function(n, t) { + var e = o - t, + r = K(i) * Math.sqrt(n * n + e * e); + return [Math.atan2(n, e) / i, 2 * Math.atan(Math.pow(o / r, 1 / i)) - Ra] + }, e) : Sr + } + + function wr(n, t) { + function e(n, t) { + var e = i - t; + return [e * Math.sin(u * n), i - e * Math.cos(u * n)] + } + var r = Math.cos(n), + u = n === t ? Math.sin(n) : (r - Math.cos(t)) / (t - n), + i = r / u + n; + return ga(u) < Ca ? ar : (e.invert = function(n, t) { + var e = i - t; + return [Math.atan2(n, e) / u, i - K(u) * Math.sqrt(n * n + e * e)] + }, e) + } + + function Sr(n, t) { + return [n, Math.log(Math.tan(qa / 4 + t / 2))] + } + + function kr(n) { + var t, e = ur(n), + r = e.scale, + u = e.translate, + i = e.clipExtent; + return e.scale = function() { + var n = r.apply(e, arguments); + return n === e ? t ? e.clipExtent(null) : e : n + }, e.translate = function() { + var n = u.apply(e, arguments); + return n === e ? t ? e.clipExtent(null) : e : n + }, e.clipExtent = function(n) { + var o = i.apply(e, arguments); + if (o === e) { + if (t = null == n) { + var a = qa * r(), + c = u(); + i([ + [c[0] - a, c[1] - a], + [c[0] + a, c[1] + a] + ]) + } + } else t && (o = null); + return o + }, e.clipExtent(null) + } + + function Er(n, t) { + return [Math.log(Math.tan(qa / 4 + t / 2)), -n] + } + + function Ar(n) { + return n[0] + } + + function Nr(n) { + return n[1] + } + + function Cr(n) { + for (var t = n.length, e = [0, 1], r = 2, u = 2; t > u; u++) { + for (; r > 1 && Q(n[e[r - 2]], n[e[r - 1]], n[u]) <= 0;) --r; + e[r++] = u + } + return e.slice(0, r) + } + + function zr(n, t) { + return n[0] - t[0] || n[1] - t[1] + } + + function qr(n, t, e) { + return (e[0] - t[0]) * (n[1] - t[1]) < (e[1] - t[1]) * (n[0] - t[0]) + } + + function Lr(n, t, e, r) { + var u = n[0], + i = e[0], + o = t[0] - u, + a = r[0] - i, + c = n[1], + l = e[1], + s = t[1] - c, + f = r[1] - l, + h = (a * (c - l) - f * (u - i)) / (f * o - a * s); + return [u + h * o, c + h * s] + } + + function Tr(n) { + var t = n[0], + e = n[n.length - 1]; + return !(t[0] - e[0] || t[1] - e[1]) + } + + function Rr() { + tu(this), this.edge = this.site = this.circle = null + } + + function Dr(n) { + var t = el.pop() || new Rr; + return t.site = n, t + } + + function Pr(n) { + Xr(n), Qc.remove(n), el.push(n), tu(n) + } + + function Ur(n) { + var t = n.circle, + e = t.x, + r = t.cy, + u = { + x: e, + y: r + }, + i = n.P, + o = n.N, + a = [n]; + Pr(n); + for (var c = i; c.circle && ga(e - c.circle.x) < Ca && ga(r - c.circle.cy) < Ca;) i = c.P, a.unshift(c), Pr(c), c = i; + a.unshift(c), Xr(c); + for (var l = o; l.circle && ga(e - l.circle.x) < Ca && ga(r - l.circle.cy) < Ca;) o = l.N, a.push(l), Pr(l), l = o; + a.push(l), Xr(l); + var s, f = a.length; + for (s = 1; f > s; ++s) l = a[s], c = a[s - 1], Kr(l.edge, c.site, l.site, u); + c = a[0], l = a[f - 1], l.edge = Jr(c.site, l.site, null, u), Vr(c), Vr(l) + } + + function jr(n) { + for (var t, e, r, u, i = n.x, o = n.y, a = Qc._; a;) + if (r = Fr(a, o) - i, r > Ca) a = a.L; + else { + if (u = i - Hr(a, o), !(u > Ca)) { + r > -Ca ? (t = a.P, e = a) : u > -Ca ? (t = a, e = a.N) : t = e = a; + break + } + if (!a.R) { + t = a; + break + } + a = a.R + } var c = Dr(n); + if (Qc.insert(t, c), t || e) { + if (t === e) return Xr(t), e = Dr(t.site), Qc.insert(c, e), c.edge = e.edge = Jr(t.site, c.site), Vr(t), void Vr(e); + if (!e) return void(c.edge = Jr(t.site, c.site)); + Xr(t), Xr(e); + var l = t.site, + s = l.x, + f = l.y, + h = n.x - s, + g = n.y - f, + p = e.site, + v = p.x - s, + d = p.y - f, + m = 2 * (h * d - g * v), + y = h * h + g * g, + M = v * v + d * d, + x = { + x: (d * y - g * M) / m + s, + y: (h * M - v * y) / m + f + }; + Kr(e.edge, l, p, x), c.edge = Jr(l, n, null, x), e.edge = Jr(n, p, null, x), Vr(t), Vr(e) + } + } + + function Fr(n, t) { + var e = n.site, + r = e.x, + u = e.y, + i = u - t; + if (!i) return r; + var o = n.P; + if (!o) return -1 / 0; + e = o.site; + var a = e.x, + c = e.y, + l = c - t; + if (!l) return a; + var s = a - r, + f = 1 / i - 1 / l, + h = s / l; + return f ? (-h + Math.sqrt(h * h - 2 * f * (s * s / (-2 * l) - c + l / 2 + u - i / 2))) / f + r : (r + a) / 2 + } + + function Hr(n, t) { + var e = n.N; + if (e) return Fr(e, t); + var r = n.site; + return r.y === t ? r.x : 1 / 0 + } + + function Or(n) { + this.site = n, this.edges = [] + } + + function Ir(n) { + for (var t, e, r, u, i, o, a, c, l, s, f = n[0][0], h = n[1][0], g = n[0][1], p = n[1][1], v = Kc, d = v.length; d--;) + if (i = v[d], i && i.prepare()) + for (a = i.edges, c = a.length, o = 0; c > o;) s = a[o].end(), r = s.x, u = s.y, l = a[++o % c].start(), t = l.x, e = l.y, (ga(r - t) > Ca || ga(u - e) > Ca) && (a.splice(o, 0, new Qr(Gr(i.site, s, ga(r - f) < Ca && p - u > Ca ? { + x: f, + y: ga(t - f) < Ca ? e : p + } : ga(u - p) < Ca && h - r > Ca ? { + x: ga(e - p) < Ca ? t : h, + y: p + } : ga(r - h) < Ca && u - g > Ca ? { + x: h, + y: ga(t - h) < Ca ? e : g + } : ga(u - g) < Ca && r - f > Ca ? { + x: ga(e - g) < Ca ? t : f, + y: g + } : null), i.site, null)), ++c) + } + + function Yr(n, t) { + return t.angle - n.angle + } + + function Zr() { + tu(this), this.x = this.y = this.arc = this.site = this.cy = null + } + + function Vr(n) { + var t = n.P, + e = n.N; + if (t && e) { + var r = t.site, + u = n.site, + i = e.site; + if (r !== i) { + var o = u.x, + a = u.y, + c = r.x - o, + l = r.y - a, + s = i.x - o, + f = i.y - a, + h = 2 * (c * f - l * s); + if (!(h >= -za)) { + var g = c * c + l * l, + p = s * s + f * f, + v = (f * g - l * p) / h, + d = (c * p - s * g) / h, + f = d + a, + m = rl.pop() || new Zr; + m.arc = n, m.site = u, m.x = v + o, m.y = f + Math.sqrt(v * v + d * d), m.cy = f, n.circle = m; + for (var y = null, M = tl._; M;) + if (m.y < M.y || m.y === M.y && m.x <= M.x) { + if (!M.L) { + y = M.P; + break + } + M = M.L + } else { + if (!M.R) { + y = M; + break + } + M = M.R + } tl.insert(y, m), y || (nl = m) + } + } + } + } + + function Xr(n) { + var t = n.circle; + t && (t.P || (nl = t.N), tl.remove(t), rl.push(t), tu(t), n.circle = null) + } + + function $r(n) { + for (var t, e = Gc, r = Oe(n[0][0], n[0][1], n[1][0], n[1][1]), u = e.length; u--;) t = e[u], (!Br(t, n) || !r(t) || ga(t.a.x - t.b.x) < Ca && ga(t.a.y - t.b.y) < Ca) && (t.a = t.b = null, e.splice(u, 1)) + } + + function Br(n, t) { + var e = n.b; + if (e) return !0; + var r, u, i = n.a, + o = t[0][0], + a = t[1][0], + c = t[0][1], + l = t[1][1], + s = n.l, + f = n.r, + h = s.x, + g = s.y, + p = f.x, + v = f.y, + d = (h + p) / 2, + m = (g + v) / 2; + if (v === g) { + if (o > d || d >= a) return; + if (h > p) { + if (i) { + if (i.y >= l) return + } else i = { + x: d, + y: c + }; + e = { + x: d, + y: l + } + } else { + if (i) { + if (i.y < c) return + } else i = { + x: d, + y: l + }; + e = { + x: d, + y: c + } + } + } else if (r = (h - p) / (v - g), u = m - r * d, -1 > r || r > 1) + if (h > p) { + if (i) { + if (i.y >= l) return + } else i = { + x: (c - u) / r, + y: c + }; + e = { + x: (l - u) / r, + y: l + } + } else { + if (i) { + if (i.y < c) return + } else i = { + x: (l - u) / r, + y: l + }; + e = { + x: (c - u) / r, + y: c + } + } + else if (v > g) { + if (i) { + if (i.x >= a) return + } else i = { + x: o, + y: r * o + u + }; + e = { + x: a, + y: r * a + u + } + } else { + if (i) { + if (i.x < o) return + } else i = { + x: a, + y: r * a + u + }; + e = { + x: o, + y: r * o + u + } + } + return n.a = i, n.b = e, !0 + } + + function Wr(n, t) { + this.l = n, this.r = t, this.a = this.b = null + } + + function Jr(n, t, e, r) { + var u = new Wr(n, t); + return Gc.push(u), e && Kr(u, n, t, e), r && Kr(u, t, n, r), Kc[n.i].edges.push(new Qr(u, n, t)), Kc[t.i].edges.push(new Qr(u, t, n)), u + } + + function Gr(n, t, e) { + var r = new Wr(n, null); + return r.a = t, r.b = e, Gc.push(r), r + } + + function Kr(n, t, e, r) { + n.a || n.b ? n.l === e ? n.b = r : n.a = r : (n.a = r, n.l = t, n.r = e) + } + + function Qr(n, t, e) { + var r = n.a, + u = n.b; + this.edge = n, this.site = t, this.angle = e ? Math.atan2(e.y - t.y, e.x - t.x) : n.l === t ? Math.atan2(u.x - r.x, r.y - u.y) : Math.atan2(r.x - u.x, u.y - r.y) + } + + function nu() { + this._ = null + } + + function tu(n) { + n.U = n.C = n.L = n.R = n.P = n.N = null + } + + function eu(n, t) { + var e = t, + r = t.R, + u = e.U; + u ? u.L === e ? u.L = r : u.R = r : n._ = r, r.U = u, e.U = r, e.R = r.L, e.R && (e.R.U = e), r.L = e + } + + function ru(n, t) { + var e = t, + r = t.L, + u = e.U; + u ? u.L === e ? u.L = r : u.R = r : n._ = r, r.U = u, e.U = r, e.L = r.R, e.L && (e.L.U = e), r.R = e + } + + function uu(n) { + for (; n.L;) n = n.L; + return n + } + + function iu(n, t) { + var e, r, u, i = n.sort(ou).pop(); + for (Gc = [], Kc = new Array(n.length), Qc = new nu, tl = new nu;;) + if (u = nl, i && (!u || i.y < u.y || i.y === u.y && i.x < u.x))(i.x !== e || i.y !== r) && (Kc[i.i] = new Or(i), jr(i), e = i.x, r = i.y), i = n.pop(); + else { + if (!u) break; + Ur(u.arc) + } t && ($r(t), Ir(t)); + var o = { + cells: Kc, + edges: Gc + }; + return Qc = tl = Gc = Kc = null, o + } + + function ou(n, t) { + return t.y - n.y || t.x - n.x + } + + function au(n, t, e) { + return (n.x - e.x) * (t.y - n.y) - (n.x - t.x) * (e.y - n.y) + } + + function cu(n) { + return n.x + } + + function lu(n) { + return n.y + } + + function su() { + return { + leaf: !0, + nodes: [], + point: null, + x: null, + y: null + } + } + + function fu(n, t, e, r, u, i) { + if (!n(t, e, r, u, i)) { + var o = .5 * (e + u), + a = .5 * (r + i), + c = t.nodes; + c[0] && fu(n, c[0], e, r, o, a), c[1] && fu(n, c[1], o, r, u, a), c[2] && fu(n, c[2], e, a, o, i), c[3] && fu(n, c[3], o, a, u, i) + } + } + + function hu(n, t, e, r, u, i, o) { + var a, c = 1 / 0; + return function l(n, s, f, h, g) { + if (!(s > i || f > o || r > h || u > g)) { + if (p = n.point) { + var p, v = t - n.x, + d = e - n.y, + m = v * v + d * d; + if (c > m) { + var y = Math.sqrt(c = m); + r = t - y, u = e - y, i = t + y, o = e + y, a = p + } + } + for (var M = n.nodes, x = .5 * (s + h), b = .5 * (f + g), _ = t >= x, w = e >= b, S = w << 1 | _, k = S + 4; k > S; ++S) + if (n = M[3 & S]) switch (3 & S) { + case 0: + l(n, s, f, x, b); + break; + case 1: + l(n, x, f, h, b); + break; + case 2: + l(n, s, b, x, g); + break; + case 3: + l(n, x, b, h, g) + } + } + }(n, r, u, i, o), a + } + + function gu(n, t) { + n = ta.rgb(n), t = ta.rgb(t); + var e = n.r, + r = n.g, + u = n.b, + i = t.r - e, + o = t.g - r, + a = t.b - u; + return function(n) { + return "#" + xt(Math.round(e + i * n)) + xt(Math.round(r + o * n)) + xt(Math.round(u + a * n)) + } + } + + function pu(n, t) { + var e, r = {}, + u = {}; + for (e in n) e in t ? r[e] = mu(n[e], t[e]) : u[e] = n[e]; + for (e in t) e in n || (u[e] = t[e]); + return function(n) { + for (e in r) u[e] = r[e](n); + return u + } + } + + function vu(n, t) { + return n = +n, t = +t, + function(e) { + return n * (1 - e) + t * e + } + } + + function du(n, t) { + var e, r, u, i = il.lastIndex = ol.lastIndex = 0, + o = -1, + a = [], + c = []; + for (n += "", t += ""; + (e = il.exec(n)) && (r = ol.exec(t));)(u = r.index) > i && (u = t.slice(i, u), a[o] ? a[o] += u : a[++o] = u), (e = e[0]) === (r = r[0]) ? a[o] ? a[o] += r : a[++o] = r : (a[++o] = null, c.push({ + i: o, + x: vu(e, r) + })), i = ol.lastIndex; + return i < t.length && (u = t.slice(i), a[o] ? a[o] += u : a[++o] = u), a.length < 2 ? c[0] ? (t = c[0].x, function(n) { + return t(n) + "" + }) : function() { + return t + } : (t = c.length, function(n) { + for (var e, r = 0; t > r; ++r) a[(e = c[r]).i] = e.x(n); + return a.join("") + }) + } + + function mu(n, t) { + for (var e, r = ta.interpolators.length; --r >= 0 && !(e = ta.interpolators[r](n, t));); + return e + } + + function yu(n, t) { + var e, r = [], + u = [], + i = n.length, + o = t.length, + a = Math.min(n.length, t.length); + for (e = 0; a > e; ++e) r.push(mu(n[e], t[e])); + for (; i > e; ++e) u[e] = n[e]; + for (; o > e; ++e) u[e] = t[e]; + return function(n) { + for (e = 0; a > e; ++e) u[e] = r[e](n); + return u + } + } + + function Mu(n) { + return function(t) { + return 0 >= t ? 0 : t >= 1 ? 1 : n(t) + } + } + + function xu(n) { + return function(t) { + return 1 - n(1 - t) + } + } + + function bu(n) { + return function(t) { + return .5 * (.5 > t ? n(2 * t) : 2 - n(2 - 2 * t)) + } + } + + function _u(n) { + return n * n + } + + function wu(n) { + return n * n * n + } + + function Su(n) { + if (0 >= n) return 0; + if (n >= 1) return 1; + var t = n * n, + e = t * n; + return 4 * (.5 > n ? e : 3 * (n - t) + e - .75) + } + + function ku(n) { + return function(t) { + return Math.pow(t, n) + } + } + + function Eu(n) { + return 1 - Math.cos(n * Ra) + } + + function Au(n) { + return Math.pow(2, 10 * (n - 1)) + } + + function Nu(n) { + return 1 - Math.sqrt(1 - n * n) + } + + function Cu(n, t) { + var e; + return arguments.length < 2 && (t = .45), arguments.length ? e = t / La * Math.asin(1 / n) : (n = 1, e = t / 4), + function(r) { + return 1 + n * Math.pow(2, -10 * r) * Math.sin((r - e) * La / t) + } + } + + function zu(n) { + return n || (n = 1.70158), + function(t) { + return t * t * ((n + 1) * t - n) + } + } + + function qu(n) { + return 1 / 2.75 > n ? 7.5625 * n * n : 2 / 2.75 > n ? 7.5625 * (n -= 1.5 / 2.75) * n + .75 : 2.5 / 2.75 > n ? 7.5625 * (n -= 2.25 / 2.75) * n + .9375 : 7.5625 * (n -= 2.625 / 2.75) * n + .984375 + } + + function Lu(n, t) { + n = ta.hcl(n), t = ta.hcl(t); + var e = n.h, + r = n.c, + u = n.l, + i = t.h - e, + o = t.c - r, + a = t.l - u; + return isNaN(o) && (o = 0, r = isNaN(r) ? t.c : r), isNaN(i) ? (i = 0, e = isNaN(e) ? t.h : e) : i > 180 ? i -= 360 : -180 > i && (i += 360), + function(n) { + return st(e + i * n, r + o * n, u + a * n) + "" + } + } + + function Tu(n, t) { + n = ta.hsl(n), t = ta.hsl(t); + var e = n.h, + r = n.s, + u = n.l, + i = t.h - e, + o = t.s - r, + a = t.l - u; + return isNaN(o) && (o = 0, r = isNaN(r) ? t.s : r), isNaN(i) ? (i = 0, e = isNaN(e) ? t.h : e) : i > 180 ? i -= 360 : -180 > i && (i += 360), + function(n) { + return ct(e + i * n, r + o * n, u + a * n) + "" + } + } + + function Ru(n, t) { + n = ta.lab(n), t = ta.lab(t); + var e = n.l, + r = n.a, + u = n.b, + i = t.l - e, + o = t.a - r, + a = t.b - u; + return function(n) { + return ht(e + i * n, r + o * n, u + a * n) + "" + } + } + + function Du(n, t) { + return t -= n, + function(e) { + return Math.round(n + t * e) + } + } + + function Pu(n) { + var t = [n.a, n.b], + e = [n.c, n.d], + r = ju(t), + u = Uu(t, e), + i = ju(Fu(e, t, -u)) || 0; + t[0] * e[1] < e[0] * t[1] && (t[0] *= -1, t[1] *= -1, r *= -1, u *= -1), this.rotate = (r ? Math.atan2(t[1], t[0]) : Math.atan2(-e[0], e[1])) * Pa, this.translate = [n.e, n.f], this.scale = [r, i], this.skew = i ? Math.atan2(u, i) * Pa : 0 + } + + function Uu(n, t) { + return n[0] * t[0] + n[1] * t[1] + } + + function ju(n) { + var t = Math.sqrt(Uu(n, n)); + return t && (n[0] /= t, n[1] /= t), t + } + + function Fu(n, t, e) { + return n[0] += e * t[0], n[1] += e * t[1], n + } + + function Hu(n, t) { + var e, r = [], + u = [], + i = ta.transform(n), + o = ta.transform(t), + a = i.translate, + c = o.translate, + l = i.rotate, + s = o.rotate, + f = i.skew, + h = o.skew, + g = i.scale, + p = o.scale; + return a[0] != c[0] || a[1] != c[1] ? (r.push("translate(", null, ",", null, ")"), u.push({ + i: 1, + x: vu(a[0], c[0]) + }, { + i: 3, + x: vu(a[1], c[1]) + })) : r.push(c[0] || c[1] ? "translate(" + c + ")" : ""), l != s ? (l - s > 180 ? s += 360 : s - l > 180 && (l += 360), u.push({ + i: r.push(r.pop() + "rotate(", null, ")") - 2, + x: vu(l, s) + })) : s && r.push(r.pop() + "rotate(" + s + ")"), f != h ? u.push({ + i: r.push(r.pop() + "skewX(", null, ")") - 2, + x: vu(f, h) + }) : h && r.push(r.pop() + "skewX(" + h + ")"), g[0] != p[0] || g[1] != p[1] ? (e = r.push(r.pop() + "scale(", null, ",", null, ")"), u.push({ + i: e - 4, + x: vu(g[0], p[0]) + }, { + i: e - 2, + x: vu(g[1], p[1]) + })) : (1 != p[0] || 1 != p[1]) && r.push(r.pop() + "scale(" + p + ")"), e = u.length, + function(n) { + for (var t, i = -1; ++i < e;) r[(t = u[i]).i] = t.x(n); + return r.join("") + } + } + + function Ou(n, t) { + return t = (t -= n = +n) || 1 / t, + function(e) { + return (e - n) / t + } + } + + function Iu(n, t) { + return t = (t -= n = +n) || 1 / t, + function(e) { + return Math.max(0, Math.min(1, (e - n) / t)) + } + } + + function Yu(n) { + for (var t = n.source, e = n.target, r = Vu(t, e), u = [t]; t !== r;) t = t.parent, u.push(t); + for (var i = u.length; e !== r;) u.splice(i, 0, e), e = e.parent; + return u + } + + function Zu(n) { + for (var t = [], e = n.parent; null != e;) t.push(n), n = e, e = e.parent; + return t.push(n), t + } + + function Vu(n, t) { + if (n === t) return n; + for (var e = Zu(n), r = Zu(t), u = e.pop(), i = r.pop(), o = null; u === i;) o = u, u = e.pop(), i = r.pop(); + return o + } + + function Xu(n) { + n.fixed |= 2 + } + + function $u(n) { + n.fixed &= -7 + } + + function Bu(n) { + n.fixed |= 4, n.px = n.x, n.py = n.y + } + + function Wu(n) { + n.fixed &= -5 + } + + function Ju(n, t, e) { + var r = 0, + u = 0; + if (n.charge = 0, !n.leaf) + for (var i, o = n.nodes, a = o.length, c = -1; ++c < a;) i = o[c], null != i && (Ju(i, t, e), n.charge += i.charge, r += i.charge * i.cx, u += i.charge * i.cy); + if (n.point) { + n.leaf || (n.point.x += Math.random() - .5, n.point.y += Math.random() - .5); + var l = t * e[n.point.index]; + n.charge += n.pointCharge = l, r += l * n.point.x, u += l * n.point.y + } + n.cx = r / n.charge, n.cy = u / n.charge + } + + function Gu(n, t) { + return ta.rebind(n, t, "sort", "children", "value"), n.nodes = n, n.links = ri, n + } + + function Ku(n, t) { + for (var e = [n]; null != (n = e.pop());) + if (t(n), (u = n.children) && (r = u.length)) + for (var r, u; --r >= 0;) e.push(u[r]) + } + + function Qu(n, t) { + for (var e = [n], r = []; null != (n = e.pop());) + if (r.push(n), (i = n.children) && (u = i.length)) + for (var u, i, o = -1; ++o < u;) e.push(i[o]); + for (; null != (n = r.pop());) t(n) + } + + function ni(n) { + return n.children + } + + function ti(n) { + return n.value + } + + function ei(n, t) { + return t.value - n.value + } + + function ri(n) { + return ta.merge(n.map(function(n) { + return (n.children || []).map(function(t) { + return { + source: n, + target: t + } + }) + })) + } + + function ui(n) { + return n.x + } + + function ii(n) { + return n.y + } + + function oi(n, t, e) { + n.y0 = t, n.y = e + } + + function ai(n) { + return ta.range(n.length) + } + + function ci(n) { + for (var t = -1, e = n[0].length, r = []; ++t < e;) r[t] = 0; + return r + } + + function li(n) { + for (var t, e = 1, r = 0, u = n[0][1], i = n.length; i > e; ++e)(t = n[e][1]) > u && (r = e, u = t); + return r + } + + function si(n) { + return n.reduce(fi, 0) + } + + function fi(n, t) { + return n + t[1] + } + + function hi(n, t) { + return gi(n, Math.ceil(Math.log(t.length) / Math.LN2 + 1)) + } + + function gi(n, t) { + for (var e = -1, r = +n[0], u = (n[1] - r) / t, i = []; ++e <= t;) i[e] = u * e + r; + return i + } + + function pi(n) { + return [ta.min(n), ta.max(n)] + } + + function vi(n, t) { + return n.value - t.value + } + + function di(n, t) { + var e = n._pack_next; + n._pack_next = t, t._pack_prev = n, t._pack_next = e, e._pack_prev = t + } + + function mi(n, t) { + n._pack_next = t, t._pack_prev = n + } + + function yi(n, t) { + var e = t.x - n.x, + r = t.y - n.y, + u = n.r + t.r; + return .999 * u * u > e * e + r * r + } + + function Mi(n) { + function t(n) { + s = Math.min(n.x - n.r, s), f = Math.max(n.x + n.r, f), h = Math.min(n.y - n.r, h), g = Math.max(n.y + n.r, g) + } + if ((e = n.children) && (l = e.length)) { + var e, r, u, i, o, a, c, l, s = 1 / 0, + f = -1 / 0, + h = 1 / 0, + g = -1 / 0; + if (e.forEach(xi), r = e[0], r.x = -r.r, r.y = 0, t(r), l > 1 && (u = e[1], u.x = u.r, u.y = 0, t(u), l > 2)) + for (i = e[2], wi(r, u, i), t(i), di(r, i), r._pack_prev = i, di(i, u), u = r._pack_next, o = 3; l > o; o++) { + wi(r, u, i = e[o]); + var p = 0, + v = 1, + d = 1; + for (a = u._pack_next; a !== u; a = a._pack_next, v++) + if (yi(a, i)) { + p = 1; + break + } if (1 == p) + for (c = r._pack_prev; c !== a._pack_prev && !yi(c, i); c = c._pack_prev, d++); + p ? (d > v || v == d && u.r < r.r ? mi(r, u = a) : mi(r = c, u), o--) : (di(r, i), u = i, t(i)) + } + var m = (s + f) / 2, + y = (h + g) / 2, + M = 0; + for (o = 0; l > o; o++) i = e[o], i.x -= m, i.y -= y, M = Math.max(M, i.r + Math.sqrt(i.x * i.x + i.y * i.y)); + n.r = M, e.forEach(bi) + } + } + + function xi(n) { + n._pack_next = n._pack_prev = n + } + + function bi(n) { + delete n._pack_next, delete n._pack_prev + } + + function _i(n, t, e, r) { + var u = n.children; + if (n.x = t += r * n.x, n.y = e += r * n.y, n.r *= r, u) + for (var i = -1, o = u.length; ++i < o;) _i(u[i], t, e, r) + } + + function wi(n, t, e) { + var r = n.r + e.r, + u = t.x - n.x, + i = t.y - n.y; + if (r && (u || i)) { + var o = t.r + e.r, + a = u * u + i * i; + o *= o, r *= r; + var c = .5 + (r - o) / (2 * a), + l = Math.sqrt(Math.max(0, 2 * o * (r + a) - (r -= a) * r - o * o)) / (2 * a); + e.x = n.x + c * u + l * i, e.y = n.y + c * i - l * u + } else e.x = n.x + r, e.y = n.y + } + + function Si(n, t) { + return n.parent == t.parent ? 1 : 2 + } + + function ki(n) { + var t = n.children; + return t.length ? t[0] : n.t + } + + function Ei(n) { + var t, e = n.children; + return (t = e.length) ? e[t - 1] : n.t + } + + function Ai(n, t, e) { + var r = e / (t.i - n.i); + t.c -= r, t.s += e, n.c += r, t.z += e, t.m += e + } + + function Ni(n) { + for (var t, e = 0, r = 0, u = n.children, i = u.length; --i >= 0;) t = u[i], t.z += e, t.m += e, e += t.s + (r += t.c) + } + + function Ci(n, t, e) { + return n.a.parent === t.parent ? n.a : e + } + + function zi(n) { + return 1 + ta.max(n, function(n) { + return n.y + }) + } + + function qi(n) { + return n.reduce(function(n, t) { + return n + t.x + }, 0) / n.length + } + + function Li(n) { + var t = n.children; + return t && t.length ? Li(t[0]) : n + } + + function Ti(n) { + var t, e = n.children; + return e && (t = e.length) ? Ti(e[t - 1]) : n + } + + function Ri(n) { + return { + x: n.x, + y: n.y, + dx: n.dx, + dy: n.dy + } + } + + function Di(n, t) { + var e = n.x + t[3], + r = n.y + t[0], + u = n.dx - t[1] - t[3], + i = n.dy - t[0] - t[2]; + return 0 > u && (e += u / 2, u = 0), 0 > i && (r += i / 2, i = 0), { + x: e, + y: r, + dx: u, + dy: i + } + } + + function Pi(n) { + var t = n[0], + e = n[n.length - 1]; + return e > t ? [t, e] : [e, t] + } + + function Ui(n) { + return n.rangeExtent ? n.rangeExtent() : Pi(n.range()) + } + + function ji(n, t, e, r) { + var u = e(n[0], n[1]), + i = r(t[0], t[1]); + return function(n) { + return i(u(n)) + } + } + + function Fi(n, t) { + var e, r = 0, + u = n.length - 1, + i = n[r], + o = n[u]; + return i > o && (e = r, r = u, u = e, e = i, i = o, o = e), n[r] = t.floor(i), n[u] = t.ceil(o), n + } + + function Hi(n) { + return n ? { + floor: function(t) { + return Math.floor(t / n) * n + }, + ceil: function(t) { + return Math.ceil(t / n) * n + } + } : ml + } + + function Oi(n, t, e, r) { + var u = [], + i = [], + o = 0, + a = Math.min(n.length, t.length) - 1; + for (n[a] < n[0] && (n = n.slice().reverse(), t = t.slice().reverse()); ++o <= a;) u.push(e(n[o - 1], n[o])), i.push(r(t[o - 1], t[o])); + return function(t) { + var e = ta.bisect(n, t, 1, a) - 1; + return i[e](u[e](t)) + } + } + + function Ii(n, t, e, r) { + function u() { + var u = Math.min(n.length, t.length) > 2 ? Oi : ji, + c = r ? Iu : Ou; + return o = u(n, t, c, e), a = u(t, n, c, mu), i + } + + function i(n) { + return o(n) + } + var o, a; + return i.invert = function(n) { + return a(n) + }, i.domain = function(t) { + return arguments.length ? (n = t.map(Number), u()) : n + }, i.range = function(n) { + return arguments.length ? (t = n, u()) : t + }, i.rangeRound = function(n) { + return i.range(n).interpolate(Du) + }, i.clamp = function(n) { + return arguments.length ? (r = n, u()) : r + }, i.interpolate = function(n) { + return arguments.length ? (e = n, u()) : e + }, i.ticks = function(t) { + return Xi(n, t) + }, i.tickFormat = function(t, e) { + return $i(n, t, e) + }, i.nice = function(t) { + return Zi(n, t), u() + }, i.copy = function() { + return Ii(n, t, e, r) + }, u() + } + + function Yi(n, t) { + return ta.rebind(n, t, "range", "rangeRound", "interpolate", "clamp") + } + + function Zi(n, t) { + return Fi(n, Hi(Vi(n, t)[2])) + } + + function Vi(n, t) { + null == t && (t = 10); + var e = Pi(n), + r = e[1] - e[0], + u = Math.pow(10, Math.floor(Math.log(r / t) / Math.LN10)), + i = t / r * u; + return .15 >= i ? u *= 10 : .35 >= i ? u *= 5 : .75 >= i && (u *= 2), e[0] = Math.ceil(e[0] / u) * u, e[1] = Math.floor(e[1] / u) * u + .5 * u, e[2] = u, e + } + + function Xi(n, t) { + return ta.range.apply(ta, Vi(n, t)) + } + + function $i(n, t, e) { + var r = Vi(n, t); + if (e) { + var u = ic.exec(e); + if (u.shift(), "s" === u[8]) { + var i = ta.formatPrefix(Math.max(ga(r[0]), ga(r[1]))); + return u[7] || (u[7] = "." + Bi(i.scale(r[2]))), u[8] = "f", e = ta.format(u.join("")), + function(n) { + return e(i.scale(n)) + i.symbol + } + } + u[7] || (u[7] = "." + Wi(u[8], r)), e = u.join("") + } else e = ",." + Bi(r[2]) + "f"; + return ta.format(e) + } + + function Bi(n) { + return -Math.floor(Math.log(n) / Math.LN10 + .01) + } + + function Wi(n, t) { + var e = Bi(t[2]); + return n in yl ? Math.abs(e - Bi(Math.max(ga(t[0]), ga(t[1])))) + +("e" !== n) : e - 2 * ("%" === n) + } + + function Ji(n, t, e, r) { + function u(n) { + return (e ? Math.log(0 > n ? 0 : n) : -Math.log(n > 0 ? 0 : -n)) / Math.log(t) + } + + function i(n) { + return e ? Math.pow(t, n) : -Math.pow(t, -n) + } + + function o(t) { + return n(u(t)) + } + return o.invert = function(t) { + return i(n.invert(t)) + }, o.domain = function(t) { + return arguments.length ? (e = t[0] >= 0, n.domain((r = t.map(Number)).map(u)), o) : r + }, o.base = function(e) { + return arguments.length ? (t = +e, n.domain(r.map(u)), o) : t + }, o.nice = function() { + var t = Fi(r.map(u), e ? Math : xl); + return n.domain(t), r = t.map(i), o + }, o.ticks = function() { + var n = Pi(r), + o = [], + a = n[0], + c = n[1], + l = Math.floor(u(a)), + s = Math.ceil(u(c)), + f = t % 1 ? 2 : t; + if (isFinite(s - l)) { + if (e) { + for (; s > l; l++) + for (var h = 1; f > h; h++) o.push(i(l) * h); + o.push(i(l)) + } else + for (o.push(i(l)); l++ < s;) + for (var h = f - 1; h > 0; h--) o.push(i(l) * h); + for (l = 0; o[l] < a; l++); + for (s = o.length; o[s - 1] > c; s--); + o = o.slice(l, s) + } + return o + }, o.tickFormat = function(n, t) { + if (!arguments.length) return Ml; + arguments.length < 2 ? t = Ml : "function" != typeof t && (t = ta.format(t)); + var r, a = Math.max(.1, n / o.ticks().length), + c = e ? (r = 1e-12, Math.ceil) : (r = -1e-12, Math.floor); + return function(n) { + return n / i(c(u(n) + r)) <= a ? t(n) : "" + } + }, o.copy = function() { + return Ji(n.copy(), t, e, r) + }, Yi(o, n) + } + + function Gi(n, t, e) { + function r(t) { + return n(u(t)) + } + var u = Ki(t), + i = Ki(1 / t); + return r.invert = function(t) { + return i(n.invert(t)) + }, r.domain = function(t) { + return arguments.length ? (n.domain((e = t.map(Number)).map(u)), r) : e + }, r.ticks = function(n) { + return Xi(e, n) + }, r.tickFormat = function(n, t) { + return $i(e, n, t) + }, r.nice = function(n) { + return r.domain(Zi(e, n)) + }, r.exponent = function(o) { + return arguments.length ? (u = Ki(t = o), i = Ki(1 / t), n.domain(e.map(u)), r) : t + }, r.copy = function() { + return Gi(n.copy(), t, e) + }, Yi(r, n) + } + + function Ki(n) { + return function(t) { + return 0 > t ? -Math.pow(-t, n) : Math.pow(t, n) + } + } + + function Qi(n, t) { + function e(e) { + return i[((u.get(e) || ("range" === t.t ? u.set(e, n.push(e)) : 0 / 0)) - 1) % i.length] + } + + function r(t, e) { + return ta.range(n.length).map(function(n) { + return t + e * n + }) + } + var u, i, o; + return e.domain = function(r) { + if (!arguments.length) return n; + n = [], u = new l; + for (var i, o = -1, a = r.length; ++o < a;) u.has(i = r[o]) || u.set(i, n.push(i)); + return e[t.t].apply(e, t.a) + }, e.range = function(n) { + return arguments.length ? (i = n, o = 0, t = { + t: "range", + a: arguments + }, e) : i + }, e.rangePoints = function(u, a) { + arguments.length < 2 && (a = 0); + var c = u[0], + l = u[1], + s = n.length < 2 ? (c = (c + l) / 2, 0) : (l - c) / (n.length - 1 + a); + return i = r(c + s * a / 2, s), o = 0, t = { + t: "rangePoints", + a: arguments + }, e + }, e.rangeRoundPoints = function(u, a) { + arguments.length < 2 && (a = 0); + var c = u[0], + l = u[1], + s = n.length < 2 ? (c = l = Math.round((c + l) / 2), 0) : (l - c) / (n.length - 1 + a) | 0; + return i = r(c + Math.round(s * a / 2 + (l - c - (n.length - 1 + a) * s) / 2), s), o = 0, t = { + t: "rangeRoundPoints", + a: arguments + }, e + }, e.rangeBands = function(u, a, c) { + arguments.length < 2 && (a = 0), arguments.length < 3 && (c = a); + var l = u[1] < u[0], + s = u[l - 0], + f = u[1 - l], + h = (f - s) / (n.length - a + 2 * c); + return i = r(s + h * c, h), l && i.reverse(), o = h * (1 - a), t = { + t: "rangeBands", + a: arguments + }, e + }, e.rangeRoundBands = function(u, a, c) { + arguments.length < 2 && (a = 0), arguments.length < 3 && (c = a); + var l = u[1] < u[0], + s = u[l - 0], + f = u[1 - l], + h = Math.floor((f - s) / (n.length - a + 2 * c)); + return i = r(s + Math.round((f - s - (n.length - a) * h) / 2), h), l && i.reverse(), o = Math.round(h * (1 - a)), t = { + t: "rangeRoundBands", + a: arguments + }, e + }, e.rangeBand = function() { + return o + }, e.rangeExtent = function() { + return Pi(t.a[0]) + }, e.copy = function() { + return Qi(n, t) + }, e.domain(n) + } + + function no(n, t) { + function i() { + var e = 0, + r = t.length; + for (a = []; ++e < r;) a[e - 1] = ta.quantile(n, e / r); + return o + } + + function o(n) { + return isNaN(n = +n) ? void 0 : t[ta.bisect(a, n)] + } + var a; + return o.domain = function(t) { + return arguments.length ? (n = t.map(r).filter(u).sort(e), i()) : n + }, o.range = function(n) { + return arguments.length ? (t = n, i()) : t + }, o.quantiles = function() { + return a + }, o.invertExtent = function(e) { + return e = t.indexOf(e), 0 > e ? [0 / 0, 0 / 0] : [e > 0 ? a[e - 1] : n[0], e < a.length ? a[e] : n[n.length - 1]] + }, o.copy = function() { + return no(n, t) + }, i() + } + + function to(n, t, e) { + function r(t) { + return e[Math.max(0, Math.min(o, Math.floor(i * (t - n))))] + } + + function u() { + return i = e.length / (t - n), o = e.length - 1, r + } + var i, o; + return r.domain = function(e) { + return arguments.length ? (n = +e[0], t = +e[e.length - 1], u()) : [n, t] + }, r.range = function(n) { + return arguments.length ? (e = n, u()) : e + }, r.invertExtent = function(t) { + return t = e.indexOf(t), t = 0 > t ? 0 / 0 : t / i + n, [t, t + 1 / i] + }, r.copy = function() { + return to(n, t, e) + }, u() + } + + function eo(n, t) { + function e(e) { + return e >= e ? t[ta.bisect(n, e)] : void 0 + } + return e.domain = function(t) { + return arguments.length ? (n = t, e) : n + }, e.range = function(n) { + return arguments.length ? (t = n, e) : t + }, e.invertExtent = function(e) { + return e = t.indexOf(e), [n[e - 1], n[e]] + }, e.copy = function() { + return eo(n, t) + }, e + } + + function ro(n) { + function t(n) { + return +n + } + return t.invert = t, t.domain = t.range = function(e) { + return arguments.length ? (n = e.map(t), t) : n + }, t.ticks = function(t) { + return Xi(n, t) + }, t.tickFormat = function(t, e) { + return $i(n, t, e) + }, t.copy = function() { + return ro(n) + }, t + } + + function uo() { + return 0 + } + + function io(n) { + return n.innerRadius + } + + function oo(n) { + return n.outerRadius + } + + function ao(n) { + return n.startAngle + } + + function co(n) { + return n.endAngle + } + + function lo(n) { + return n && n.padAngle + } + + function so(n, t, e, r) { + return (n - e) * t - (t - r) * n > 0 ? 0 : 1 + } + + function fo(n, t, e, r, u) { + var i = n[0] - t[0], + o = n[1] - t[1], + a = (u ? r : -r) / Math.sqrt(i * i + o * o), + c = a * o, + l = -a * i, + s = n[0] + c, + f = n[1] + l, + h = t[0] + c, + g = t[1] + l, + p = (s + h) / 2, + v = (f + g) / 2, + d = h - s, + m = g - f, + y = d * d + m * m, + M = e - r, + x = s * g - h * f, + b = (0 > m ? -1 : 1) * Math.sqrt(M * M * y - x * x), + _ = (x * m - d * b) / y, + w = (-x * d - m * b) / y, + S = (x * m + d * b) / y, + k = (-x * d + m * b) / y, + E = _ - p, + A = w - v, + N = S - p, + C = k - v; + return E * E + A * A > N * N + C * C && (_ = S, w = k), [ + [_ - c, w - l], + [_ * e / M, w * e / M] + ] + } + + function ho(n) { + function t(t) { + function o() { + l.push("M", i(n(s), a)) + } + for (var c, l = [], s = [], f = -1, h = t.length, g = Et(e), p = Et(r); ++f < h;) u.call(this, c = t[f], f) ? s.push([+g.call(this, c, f), +p.call(this, c, f)]) : s.length && (o(), s = []); + return s.length && o(), l.length ? l.join("") : null + } + var e = Ar, + r = Nr, + u = Ne, + i = go, + o = i.key, + a = .7; + return t.x = function(n) { + return arguments.length ? (e = n, t) : e + }, t.y = function(n) { + return arguments.length ? (r = n, t) : r + }, t.defined = function(n) { + return arguments.length ? (u = n, t) : u + }, t.interpolate = function(n) { + return arguments.length ? (o = "function" == typeof n ? i = n : (i = El.get(n) || go).key, t) : o + }, t.tension = function(n) { + return arguments.length ? (a = n, t) : a + }, t + } + + function go(n) { + return n.join("L") + } + + function po(n) { + return go(n) + "Z" + } + + function vo(n) { + for (var t = 0, e = n.length, r = n[0], u = [r[0], ",", r[1]]; ++t < e;) u.push("H", (r[0] + (r = n[t])[0]) / 2, "V", r[1]); + return e > 1 && u.push("H", r[0]), u.join("") + } + + function mo(n) { + for (var t = 0, e = n.length, r = n[0], u = [r[0], ",", r[1]]; ++t < e;) u.push("V", (r = n[t])[1], "H", r[0]); + return u.join("") + } + + function yo(n) { + for (var t = 0, e = n.length, r = n[0], u = [r[0], ",", r[1]]; ++t < e;) u.push("H", (r = n[t])[0], "V", r[1]); + return u.join("") + } + + function Mo(n, t) { + return n.length < 4 ? go(n) : n[1] + _o(n.slice(1, -1), wo(n, t)) + } + + function xo(n, t) { + return n.length < 3 ? go(n) : n[0] + _o((n.push(n[0]), n), wo([n[n.length - 2]].concat(n, [n[1]]), t)) + } + + function bo(n, t) { + return n.length < 3 ? go(n) : n[0] + _o(n, wo(n, t)) + } + + function _o(n, t) { + if (t.length < 1 || n.length != t.length && n.length != t.length + 2) return go(n); + var e = n.length != t.length, + r = "", + u = n[0], + i = n[1], + o = t[0], + a = o, + c = 1; + if (e && (r += "Q" + (i[0] - 2 * o[0] / 3) + "," + (i[1] - 2 * o[1] / 3) + "," + i[0] + "," + i[1], u = n[1], c = 2), t.length > 1) { + a = t[1], i = n[c], c++, r += "C" + (u[0] + o[0]) + "," + (u[1] + o[1]) + "," + (i[0] - a[0]) + "," + (i[1] - a[1]) + "," + i[0] + "," + i[1]; + for (var l = 2; l < t.length; l++, c++) i = n[c], a = t[l], r += "S" + (i[0] - a[0]) + "," + (i[1] - a[1]) + "," + i[0] + "," + i[1] + } + if (e) { + var s = n[c]; + r += "Q" + (i[0] + 2 * a[0] / 3) + "," + (i[1] + 2 * a[1] / 3) + "," + s[0] + "," + s[1] + } + return r + } + + function wo(n, t) { + for (var e, r = [], u = (1 - t) / 2, i = n[0], o = n[1], a = 1, c = n.length; ++a < c;) e = i, i = o, o = n[a], r.push([u * (o[0] - e[0]), u * (o[1] - e[1])]); + return r + } + + function So(n) { + if (n.length < 3) return go(n); + var t = 1, + e = n.length, + r = n[0], + u = r[0], + i = r[1], + o = [u, u, u, (r = n[1])[0]], + a = [i, i, i, r[1]], + c = [u, ",", i, "L", No(Cl, o), ",", No(Cl, a)]; + for (n.push(n[e - 1]); ++t <= e;) r = n[t], o.shift(), o.push(r[0]), a.shift(), a.push(r[1]), Co(c, o, a); + return n.pop(), c.push("L", r), c.join("") + } + + function ko(n) { + if (n.length < 4) return go(n); + for (var t, e = [], r = -1, u = n.length, i = [0], o = [0]; ++r < 3;) t = n[r], i.push(t[0]), o.push(t[1]); + for (e.push(No(Cl, i) + "," + No(Cl, o)), --r; ++r < u;) t = n[r], i.shift(), i.push(t[0]), o.shift(), o.push(t[1]), Co(e, i, o); + return e.join("") + } + + function Eo(n) { + for (var t, e, r = -1, u = n.length, i = u + 4, o = [], a = []; ++r < 4;) e = n[r % u], o.push(e[0]), a.push(e[1]); + for (t = [No(Cl, o), ",", No(Cl, a)], --r; ++r < i;) e = n[r % u], o.shift(), o.push(e[0]), a.shift(), a.push(e[1]), Co(t, o, a); + return t.join("") + } + + function Ao(n, t) { + var e = n.length - 1; + if (e) + for (var r, u, i = n[0][0], o = n[0][1], a = n[e][0] - i, c = n[e][1] - o, l = -1; ++l <= e;) r = n[l], u = l / e, r[0] = t * r[0] + (1 - t) * (i + u * a), r[1] = t * r[1] + (1 - t) * (o + u * c); + return So(n) + } + + function No(n, t) { + return n[0] * t[0] + n[1] * t[1] + n[2] * t[2] + n[3] * t[3] + } + + function Co(n, t, e) { + n.push("C", No(Al, t), ",", No(Al, e), ",", No(Nl, t), ",", No(Nl, e), ",", No(Cl, t), ",", No(Cl, e)) + } + + function zo(n, t) { + return (t[1] - n[1]) / (t[0] - n[0]) + } + + function qo(n) { + for (var t = 0, e = n.length - 1, r = [], u = n[0], i = n[1], o = r[0] = zo(u, i); ++t < e;) r[t] = (o + (o = zo(u = i, i = n[t + 1]))) / 2; + return r[t] = o, r + } + + function Lo(n) { + for (var t, e, r, u, i = [], o = qo(n), a = -1, c = n.length - 1; ++a < c;) t = zo(n[a], n[a + 1]), ga(t) < Ca ? o[a] = o[a + 1] = 0 : (e = o[a] / t, r = o[a + 1] / t, u = e * e + r * r, u > 9 && (u = 3 * t / Math.sqrt(u), o[a] = u * e, o[a + 1] = u * r)); + for (a = -1; ++a <= c;) u = (n[Math.min(c, a + 1)][0] - n[Math.max(0, a - 1)][0]) / (6 * (1 + o[a] * o[a])), i.push([u || 0, o[a] * u || 0]); + return i + } + + function To(n) { + return n.length < 3 ? go(n) : n[0] + _o(n, Lo(n)) + } + + function Ro(n) { + for (var t, e, r, u = -1, i = n.length; ++u < i;) t = n[u], e = t[0], r = t[1] - Ra, t[0] = e * Math.cos(r), t[1] = e * Math.sin(r); + return n + } + + function Do(n) { + function t(t) { + function c() { + v.push("M", a(n(m), f), s, l(n(d.reverse()), f), "Z") + } + for (var h, g, p, v = [], d = [], m = [], y = -1, M = t.length, x = Et(e), b = Et(u), _ = e === r ? function() { + return g + } : Et(r), w = u === i ? function() { + return p + } : Et(i); ++y < M;) o.call(this, h = t[y], y) ? (d.push([g = +x.call(this, h, y), p = +b.call(this, h, y)]), m.push([+_.call(this, h, y), +w.call(this, h, y)])) : d.length && (c(), d = [], m = []); + return d.length && c(), v.length ? v.join("") : null + } + var e = Ar, + r = Ar, + u = 0, + i = Nr, + o = Ne, + a = go, + c = a.key, + l = a, + s = "L", + f = .7; + return t.x = function(n) { + return arguments.length ? (e = r = n, t) : r + }, t.x0 = function(n) { + return arguments.length ? (e = n, t) : e + }, t.x1 = function(n) { + return arguments.length ? (r = n, t) : r + }, t.y = function(n) { + return arguments.length ? (u = i = n, t) : i + }, t.y0 = function(n) { + return arguments.length ? (u = n, t) : u + }, t.y1 = function(n) { + return arguments.length ? (i = n, t) : i + }, t.defined = function(n) { + return arguments.length ? (o = n, t) : o + }, t.interpolate = function(n) { + return arguments.length ? (c = "function" == typeof n ? a = n : (a = El.get(n) || go).key, l = a.reverse || a, s = a.closed ? "M" : "L", t) : c + }, t.tension = function(n) { + return arguments.length ? (f = n, t) : f + }, t + } + + function Po(n) { + return n.radius + } + + function Uo(n) { + return [n.x, n.y] + } + + function jo(n) { + return function() { + var t = n.apply(this, arguments), + e = t[0], + r = t[1] - Ra; + return [e * Math.cos(r), e * Math.sin(r)] + } + } + + function Fo() { + return 64 + } + + function Ho() { + return "circle" + } + + function Oo(n) { + var t = Math.sqrt(n / qa); + return "M0," + t + "A" + t + "," + t + " 0 1,1 0," + -t + "A" + t + "," + t + " 0 1,1 0," + t + "Z" + } + + function Io(n) { + return function() { + var t, e; + (t = this[n]) && (e = t[t.active]) && (--t.count ? delete t[t.active] : delete this[n], t.active += .5, e.event && e.event.interrupt.call(this, this.__data__, e.index)) + } + } + + function Yo(n, t, e) { + return ya(n, Pl), n.namespace = t, n.id = e, n + } + + function Zo(n, t, e, r) { + var u = n.id, + i = n.namespace; + return Y(n, "function" == typeof e ? function(n, o, a) { + n[i][u].tween.set(t, r(e.call(n, n.__data__, o, a))) + } : (e = r(e), function(n) { + n[i][u].tween.set(t, e) + })) + } + + function Vo(n) { + return null == n && (n = ""), + function() { + this.textContent = n + } + } + + function Xo(n) { + return null == n ? "__transition__" : "__transition_" + n + "__" + } + + function $o(n, t, e, r, u) { + var i = n[e] || (n[e] = { + active: 0, + count: 0 + }), + o = i[r]; + if (!o) { + var a = u.time; + o = i[r] = { + tween: new l, + time: a, + delay: u.delay, + duration: u.duration, + ease: u.ease, + index: t + }, u = null, ++i.count, ta.timer(function(u) { + function c(e) { + if (i.active > r) return s(); + var u = i[i.active]; + u && (--i.count, delete i[i.active], u.event && u.event.interrupt.call(n, n.__data__, u.index)), i.active = r, o.event && o.event.start.call(n, n.__data__, t), o.tween.forEach(function(e, r) { + (r = r.call(n, n.__data__, t)) && v.push(r) + }), h = o.ease, f = o.duration, ta.timer(function() { + return p.c = l(e || 1) ? Ne : l, 1 + }, 0, a) + } + + function l(e) { + if (i.active !== r) return 1; + for (var u = e / f, a = h(u), c = v.length; c > 0;) v[--c].call(n, a); + return u >= 1 ? (o.event && o.event.end.call(n, n.__data__, t), s()) : void 0 + } + + function s() { + return --i.count ? delete i[r] : delete n[e], 1 + } + var f, h, g = o.delay, + p = ec, + v = []; + return p.t = g + a, u >= g ? c(u - g) : void(p.c = c) + }, 0, a) + } + } + + function Bo(n, t, e) { + n.attr("transform", function(n) { + var r = t(n); + return "translate(" + (isFinite(r) ? r : e(n)) + ",0)" + }) + } + + function Wo(n, t, e) { + n.attr("transform", function(n) { + var r = t(n); + return "translate(0," + (isFinite(r) ? r : e(n)) + ")" + }) + } + + function Jo(n) { + return n.toISOString() + } + + function Go(n, t, e) { + function r(t) { + return n(t) + } + + function u(n, e) { + var r = n[1] - n[0], + u = r / e, + i = ta.bisect(Vl, u); + return i == Vl.length ? [t.year, Vi(n.map(function(n) { + return n / 31536e6 + }), e)[2]] : i ? t[u / Vl[i - 1] < Vl[i] / u ? i - 1 : i] : [Bl, Vi(n, e)[2]] + } + return r.invert = function(t) { + return Ko(n.invert(t)) + }, r.domain = function(t) { + return arguments.length ? (n.domain(t), r) : n.domain().map(Ko) + }, r.nice = function(n, t) { + function e(e) { + return !isNaN(e) && !n.range(e, Ko(+e + 1), t).length + } + var i = r.domain(), + o = Pi(i), + a = null == n ? u(o, 10) : "number" == typeof n && u(o, n); + return a && (n = a[0], t = a[1]), r.domain(Fi(i, t > 1 ? { + floor: function(t) { + for (; e(t = n.floor(t));) t = Ko(t - 1); + return t + }, + ceil: function(t) { + for (; e(t = n.ceil(t));) t = Ko(+t + 1); + return t + } + } : n)) + }, r.ticks = function(n, t) { + var e = Pi(r.domain()), + i = null == n ? u(e, 10) : "number" == typeof n ? u(e, n) : !n.range && [{ + range: n + }, t]; + return i && (n = i[0], t = i[1]), n.range(e[0], Ko(+e[1] + 1), 1 > t ? 1 : t) + }, r.tickFormat = function() { + return e + }, r.copy = function() { + return Go(n.copy(), t, e) + }, Yi(r, n) + } + + function Ko(n) { + return new Date(n) + } + + function Qo(n) { + return JSON.parse(n.responseText) + } + + function na(n) { + var t = ua.createRange(); + return t.selectNode(ua.body), t.createContextualFragment(n.responseText) + } + var ta = { + version: "3.5.4" + }, + ea = [].slice, + ra = function(n) { + return ea.call(n) + }, + ua = this.document; + if (ua) try { + ra(ua.documentElement.childNodes)[0].nodeType + } catch (ia) { + ra = function(n) { + for (var t = n.length, e = new Array(t); t--;) e[t] = n[t]; + return e + } + } + if (Date.now || (Date.now = function() { + return +new Date + }), ua) try { + ua.createElement("DIV").style.setProperty("opacity", 0, "") + } catch (oa) { + var aa = this.Element.prototype, + ca = aa.setAttribute, + la = aa.setAttributeNS, + sa = this.CSSStyleDeclaration.prototype, + fa = sa.setProperty; + aa.setAttribute = function(n, t) { + ca.call(this, n, t + "") + }, aa.setAttributeNS = function(n, t, e) { + la.call(this, n, t, e + "") + }, sa.setProperty = function(n, t, e) { + fa.call(this, n, t + "", e) + } + } + ta.ascending = e, ta.descending = function(n, t) { + return n > t ? -1 : t > n ? 1 : t >= n ? 0 : 0 / 0 + }, ta.min = function(n, t) { + var e, r, u = -1, + i = n.length; + if (1 === arguments.length) { + for (; ++u < i;) + if (null != (r = n[u]) && r >= r) { + e = r; + break + } for (; ++u < i;) null != (r = n[u]) && e > r && (e = r) + } else { + for (; ++u < i;) + if (null != (r = t.call(n, n[u], u)) && r >= r) { + e = r; + break + } for (; ++u < i;) null != (r = t.call(n, n[u], u)) && e > r && (e = r) + } + return e + }, ta.max = function(n, t) { + var e, r, u = -1, + i = n.length; + if (1 === arguments.length) { + for (; ++u < i;) + if (null != (r = n[u]) && r >= r) { + e = r; + break + } for (; ++u < i;) null != (r = n[u]) && r > e && (e = r) + } else { + for (; ++u < i;) + if (null != (r = t.call(n, n[u], u)) && r >= r) { + e = r; + break + } for (; ++u < i;) null != (r = t.call(n, n[u], u)) && r > e && (e = r) + } + return e + }, ta.extent = function(n, t) { + var e, r, u, i = -1, + o = n.length; + if (1 === arguments.length) { + for (; ++i < o;) + if (null != (r = n[i]) && r >= r) { + e = u = r; + break + } for (; ++i < o;) null != (r = n[i]) && (e > r && (e = r), r > u && (u = r)) + } else { + for (; ++i < o;) + if (null != (r = t.call(n, n[i], i)) && r >= r) { + e = u = r; + break + } for (; ++i < o;) null != (r = t.call(n, n[i], i)) && (e > r && (e = r), r > u && (u = r)) + } + return [e, u] + }, ta.sum = function(n, t) { + var e, r = 0, + i = n.length, + o = -1; + if (1 === arguments.length) + for (; ++o < i;) u(e = +n[o]) && (r += e); + else + for (; ++o < i;) u(e = +t.call(n, n[o], o)) && (r += e); + return r + }, ta.mean = function(n, t) { + var e, i = 0, + o = n.length, + a = -1, + c = o; + if (1 === arguments.length) + for (; ++a < o;) u(e = r(n[a])) ? i += e : --c; + else + for (; ++a < o;) u(e = r(t.call(n, n[a], a))) ? i += e : --c; + return c ? i / c : void 0 + }, ta.quantile = function(n, t) { + var e = (n.length - 1) * t + 1, + r = Math.floor(e), + u = +n[r - 1], + i = e - r; + return i ? u + i * (n[r] - u) : u + }, ta.median = function(n, t) { + var i, o = [], + a = n.length, + c = -1; + if (1 === arguments.length) + for (; ++c < a;) u(i = r(n[c])) && o.push(i); + else + for (; ++c < a;) u(i = r(t.call(n, n[c], c))) && o.push(i); + return o.length ? ta.quantile(o.sort(e), .5) : void 0 + }, ta.variance = function(n, t) { + var e, i, o = n.length, + a = 0, + c = 0, + l = -1, + s = 0; + if (1 === arguments.length) + for (; ++l < o;) u(e = r(n[l])) && (i = e - a, a += i / ++s, c += i * (e - a)); + else + for (; ++l < o;) u(e = r(t.call(n, n[l], l))) && (i = e - a, a += i / ++s, c += i * (e - a)); + return s > 1 ? c / (s - 1) : void 0 + }, ta.deviation = function() { + var n = ta.variance.apply(this, arguments); + return n ? Math.sqrt(n) : n + }; + var ha = i(e); + ta.bisectLeft = ha.left, ta.bisect = ta.bisectRight = ha.right, ta.bisector = function(n) { + return i(1 === n.length ? function(t, r) { + return e(n(t), r) + } : n) + }, ta.shuffle = function(n, t, e) { + (i = arguments.length) < 3 && (e = n.length, 2 > i && (t = 0)); + for (var r, u, i = e - t; i;) u = Math.random() * i-- | 0, r = n[i + t], n[i + t] = n[u + t], n[u + t] = r; + return n + }, ta.permute = function(n, t) { + for (var e = t.length, r = new Array(e); e--;) r[e] = n[t[e]]; + return r + }, ta.pairs = function(n) { + for (var t, e = 0, r = n.length - 1, u = n[0], i = new Array(0 > r ? 0 : r); r > e;) i[e] = [t = u, u = n[++e]]; + return i + }, ta.zip = function() { + if (!(r = arguments.length)) return []; + for (var n = -1, t = ta.min(arguments, o), e = new Array(t); ++n < t;) + for (var r, u = -1, i = e[n] = new Array(r); ++u < r;) i[u] = arguments[u][n]; + return e + }, ta.transpose = function(n) { + return ta.zip.apply(ta, n) + }, ta.keys = function(n) { + var t = []; + for (var e in n) t.push(e); + return t + }, ta.values = function(n) { + var t = []; + for (var e in n) t.push(n[e]); + return t + }, ta.entries = function(n) { + var t = []; + for (var e in n) t.push({ + key: e, + value: n[e] + }); + return t + }, ta.merge = function(n) { + for (var t, e, r, u = n.length, i = -1, o = 0; ++i < u;) o += n[i].length; + for (e = new Array(o); --u >= 0;) + for (r = n[u], t = r.length; --t >= 0;) e[--o] = r[t]; + return e + }; + var ga = Math.abs; + ta.range = function(n, t, e) { + if (arguments.length < 3 && (e = 1, arguments.length < 2 && (t = n, n = 0)), (t - n) / e === 1 / 0) throw new Error("infinite range"); + var r, u = [], + i = a(ga(e)), + o = -1; + if (n *= i, t *= i, e *= i, 0 > e) + for (; + (r = n + e * ++o) > t;) u.push(r / i); + else + for (; + (r = n + e * ++o) < t;) u.push(r / i); + return u + }, ta.map = function(n, t) { + var e = new l; + if (n instanceof l) n.forEach(function(n, t) { + e.set(n, t) + }); + else if (Array.isArray(n)) { + var r, u = -1, + i = n.length; + if (1 === arguments.length) + for (; ++u < i;) e.set(u, n[u]); + else + for (; ++u < i;) e.set(t.call(n, r = n[u], u), r) + } else + for (var o in n) e.set(o, n[o]); + return e + }; + var pa = "__proto__", + va = "\x00"; + c(l, { + has: h, + get: function(n) { + return this._[s(n)] + }, + set: function(n, t) { + return this._[s(n)] = t + }, + remove: g, + keys: p, + values: function() { + var n = []; + for (var t in this._) n.push(this._[t]); + return n + }, + entries: function() { + var n = []; + for (var t in this._) n.push({ + key: f(t), + value: this._[t] + }); + return n + }, + size: v, + empty: d, + forEach: function(n) { + for (var t in this._) n.call(this, f(t), this._[t]) + } + }), ta.nest = function() { + function n(t, o, a) { + if (a >= i.length) return r ? r.call(u, o) : e ? o.sort(e) : o; + for (var c, s, f, h, g = -1, p = o.length, v = i[a++], d = new l; ++g < p;)(h = d.get(c = v(s = o[g]))) ? h.push(s) : d.set(c, [s]); + return t ? (s = t(), f = function(e, r) { + s.set(e, n(t, r, a)) + }) : (s = {}, f = function(e, r) { + s[e] = n(t, r, a) + }), d.forEach(f), s + } + + function t(n, e) { + if (e >= i.length) return n; + var r = [], + u = o[e++]; + return n.forEach(function(n, u) { + r.push({ + key: n, + values: t(u, e) + }) + }), u ? r.sort(function(n, t) { + return u(n.key, t.key) + }) : r + } + var e, r, u = {}, + i = [], + o = []; + return u.map = function(t, e) { + return n(e, t, 0) + }, u.entries = function(e) { + return t(n(ta.map, e, 0), 0) + }, u.key = function(n) { + return i.push(n), u + }, u.sortKeys = function(n) { + return o[i.length - 1] = n, u + }, u.sortValues = function(n) { + return e = n, u + }, u.rollup = function(n) { + return r = n, u + }, u + }, ta.set = function(n) { + var t = new m; + if (n) + for (var e = 0, r = n.length; r > e; ++e) t.add(n[e]); + return t + }, c(m, { + has: h, + add: function(n) { + return this._[s(n += "")] = !0, n + }, + remove: g, + values: p, + size: v, + empty: d, + forEach: function(n) { + for (var t in this._) n.call(this, f(t)) + } + }), ta.behavior = {}, ta.rebind = function(n, t) { + for (var e, r = 1, u = arguments.length; ++r < u;) n[e = arguments[r]] = M(n, t, t[e]); + return n + }; + var da = ["webkit", "ms", "moz", "Moz", "o", "O"]; + ta.dispatch = function() { + for (var n = new _, t = -1, e = arguments.length; ++t < e;) n[arguments[t]] = w(n); + return n + }, _.prototype.on = function(n, t) { + var e = n.indexOf("."), + r = ""; + if (e >= 0 && (r = n.slice(e + 1), n = n.slice(0, e)), n) return arguments.length < 2 ? this[n].on(r) : this[n].on(r, t); + if (2 === arguments.length) { + if (null == t) + for (n in this) this.hasOwnProperty(n) && this[n].on(r, null); + return this + } + }, ta.event = null, ta.requote = function(n) { + return n.replace(ma, "\\$&") + }; + var ma = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g, + ya = {}.__proto__ ? function(n, t) { + n.__proto__ = t + } : function(n, t) { + for (var e in t) n[e] = t[e] + }, + Ma = function(n, t) { + return t.querySelector(n) + }, + xa = function(n, t) { + return t.querySelectorAll(n) + }, + ba = function(n, t) { + var e = n.matches || n[x(n, "matchesSelector")]; + return (ba = function(n, t) { + return e.call(n, t) + })(n, t) + }; + "function" == typeof Sizzle && (Ma = function(n, t) { + return Sizzle(n, t)[0] || null + }, xa = Sizzle, ba = Sizzle.matchesSelector), ta.selection = function() { + return ta.select(ua.documentElement) + }; + var _a = ta.selection.prototype = []; + _a.select = function(n) { + var t, e, r, u, i = []; + n = N(n); + for (var o = -1, a = this.length; ++o < a;) { + i.push(t = []), t.parentNode = (r = this[o]).parentNode; + for (var c = -1, l = r.length; ++c < l;)(u = r[c]) ? (t.push(e = n.call(u, u.__data__, c, o)), e && "__data__" in u && (e.__data__ = u.__data__)) : t.push(null) + } + return A(i) + }, _a.selectAll = function(n) { + var t, e, r = []; + n = C(n); + for (var u = -1, i = this.length; ++u < i;) + for (var o = this[u], a = -1, c = o.length; ++a < c;)(e = o[a]) && (r.push(t = ra(n.call(e, e.__data__, a, u))), t.parentNode = e); + return A(r) + }; + var wa = { + svg: "http://www.w3.org/2000/svg", + xhtml: "http://www.w3.org/1999/xhtml", + xlink: "http://www.w3.org/1999/xlink", + xml: "http://www.w3.org/XML/1998/namespace", + xmlns: "http://www.w3.org/2000/xmlns/" + }; + ta.ns = { + prefix: wa, + qualify: function(n) { + var t = n.indexOf(":"), + e = n; + return t >= 0 && (e = n.slice(0, t), n = n.slice(t + 1)), wa.hasOwnProperty(e) ? { + space: wa[e], + local: n + } : n + } + }, _a.attr = function(n, t) { + if (arguments.length < 2) { + if ("string" == typeof n) { + var e = this.node(); + return n = ta.ns.qualify(n), n.local ? e.getAttributeNS(n.space, n.local) : e.getAttribute(n) + } + for (t in n) this.each(z(t, n[t])); + return this + } + return this.each(z(n, t)) + }, _a.classed = function(n, t) { + if (arguments.length < 2) { + if ("string" == typeof n) { + var e = this.node(), + r = (n = T(n)).length, + u = -1; + if (t = e.classList) { + for (; ++u < r;) + if (!t.contains(n[u])) return !1 + } else + for (t = e.getAttribute("class"); ++u < r;) + if (!L(n[u]).test(t)) return !1; + return !0 + } + for (t in n) this.each(R(t, n[t])); + return this + } + return this.each(R(n, t)) + }, _a.style = function(n, e, r) { + var u = arguments.length; + if (3 > u) { + if ("string" != typeof n) { + 2 > u && (e = ""); + for (r in n) this.each(P(r, n[r], e)); + return this + } + if (2 > u) { + var i = this.node(); + return t(i).getComputedStyle(i, null).getPropertyValue(n) + } + r = "" + } + return this.each(P(n, e, r)) + }, _a.property = function(n, t) { + if (arguments.length < 2) { + if ("string" == typeof n) return this.node()[n]; + for (t in n) this.each(U(t, n[t])); + return this + } + return this.each(U(n, t)) + }, _a.text = function(n) { + return arguments.length ? this.each("function" == typeof n ? function() { + var t = n.apply(this, arguments); + this.textContent = null == t ? "" : t + } : null == n ? function() { + this.textContent = "" + } : function() { + this.textContent = n + }) : this.node().textContent + }, _a.html = function(n) { + return arguments.length ? this.each("function" == typeof n ? function() { + var t = n.apply(this, arguments); + this.innerHTML = null == t ? "" : t + } : null == n ? function() { + this.innerHTML = "" + } : function() { + this.innerHTML = n + }) : this.node().innerHTML + }, _a.append = function(n) { + return n = j(n), this.select(function() { + return this.appendChild(n.apply(this, arguments)) + }) + }, _a.insert = function(n, t) { + return n = j(n), t = N(t), this.select(function() { + return this.insertBefore(n.apply(this, arguments), t.apply(this, arguments) || null) + }) + }, _a.remove = function() { + return this.each(F) + }, _a.data = function(n, t) { + function e(n, e) { + var r, u, i, o = n.length, + f = e.length, + h = Math.min(o, f), + g = new Array(f), + p = new Array(f), + v = new Array(o); + if (t) { + var d, m = new l, + y = new Array(o); + for (r = -1; ++r < o;) m.has(d = t.call(u = n[r], u.__data__, r)) ? v[r] = u : m.set(d, u), y[r] = d; + for (r = -1; ++r < f;)(u = m.get(d = t.call(e, i = e[r], r))) ? u !== !0 && (g[r] = u, u.__data__ = i) : p[r] = H(i), m.set(d, !0); + for (r = -1; ++r < o;) m.get(y[r]) !== !0 && (v[r] = n[r]) + } else { + for (r = -1; ++r < h;) u = n[r], i = e[r], u ? (u.__data__ = i, g[r] = u) : p[r] = H(i); + for (; f > r; ++r) p[r] = H(e[r]); + for (; o > r; ++r) v[r] = n[r] + } + p.update = g, p.parentNode = g.parentNode = v.parentNode = n.parentNode, a.push(p), c.push(g), s.push(v) + } + var r, u, i = -1, + o = this.length; + if (!arguments.length) { + for (n = new Array(o = (r = this[0]).length); ++i < o;)(u = r[i]) && (n[i] = u.__data__); + return n + } + var a = Z([]), + c = A([]), + s = A([]); + if ("function" == typeof n) + for (; ++i < o;) e(r = this[i], n.call(r, r.parentNode.__data__, i)); + else + for (; ++i < o;) e(r = this[i], n); + return c.enter = function() { + return a + }, c.exit = function() { + return s + }, c + }, _a.datum = function(n) { + return arguments.length ? this.property("__data__", n) : this.property("__data__") + }, _a.filter = function(n) { + var t, e, r, u = []; + "function" != typeof n && (n = O(n)); + for (var i = 0, o = this.length; o > i; i++) { + u.push(t = []), t.parentNode = (e = this[i]).parentNode; + for (var a = 0, c = e.length; c > a; a++)(r = e[a]) && n.call(r, r.__data__, a, i) && t.push(r) + } + return A(u) + }, _a.order = function() { + for (var n = -1, t = this.length; ++n < t;) + for (var e, r = this[n], u = r.length - 1, i = r[u]; --u >= 0;)(e = r[u]) && (i && i !== e.nextSibling && i.parentNode.insertBefore(e, i), i = e); + return this + }, _a.sort = function(n) { + n = I.apply(this, arguments); + for (var t = -1, e = this.length; ++t < e;) this[t].sort(n); + return this.order() + }, _a.each = function(n) { + return Y(this, function(t, e, r) { + n.call(t, t.__data__, e, r) + }) + }, _a.call = function(n) { + var t = ra(arguments); + return n.apply(t[0] = this, t), this + }, _a.empty = function() { + return !this.node() + }, _a.node = function() { + for (var n = 0, t = this.length; t > n; n++) + for (var e = this[n], r = 0, u = e.length; u > r; r++) { + var i = e[r]; + if (i) return i + } + return null + }, _a.size = function() { + var n = 0; + return Y(this, function() { + ++n + }), n + }; + var Sa = []; + ta.selection.enter = Z, ta.selection.enter.prototype = Sa, Sa.append = _a.append, Sa.empty = _a.empty, Sa.node = _a.node, Sa.call = _a.call, Sa.size = _a.size, Sa.select = function(n) { + for (var t, e, r, u, i, o = [], a = -1, c = this.length; ++a < c;) { + r = (u = this[a]).update, o.push(t = []), t.parentNode = u.parentNode; + for (var l = -1, s = u.length; ++l < s;)(i = u[l]) ? (t.push(r[l] = e = n.call(u.parentNode, i.__data__, l, a)), e.__data__ = i.__data__) : t.push(null) + } + return A(o) + }, Sa.insert = function(n, t) { + return arguments.length < 2 && (t = V(this)), _a.insert.call(this, n, t) + }, ta.select = function(t) { + var e; + return "string" == typeof t ? (e = [Ma(t, ua)], e.parentNode = ua.documentElement) : (e = [t], e.parentNode = n(t)), A([e]) + }, ta.selectAll = function(n) { + var t; + return "string" == typeof n ? (t = ra(xa(n, ua)), t.parentNode = ua.documentElement) : (t = n, t.parentNode = null), A([t]) + }, _a.on = function(n, t, e) { + var r = arguments.length; + if (3 > r) { + if ("string" != typeof n) { + 2 > r && (t = !1); + for (e in n) this.each(X(e, n[e], t)); + return this + } + if (2 > r) return (r = this.node()["__on" + n]) && r._; + e = !1 + } + return this.each(X(n, t, e)) + }; + var ka = ta.map({ + mouseenter: "mouseover", + mouseleave: "mouseout" + }); + ua && ka.forEach(function(n) { + "on" + n in ua && ka.remove(n) + }); + var Ea, Aa = 0; + ta.mouse = function(n) { + return J(n, k()) + }; + var Na = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0; + ta.touch = function(n, t, e) { + if (arguments.length < 3 && (e = t, t = k().changedTouches), t) + for (var r, u = 0, i = t.length; i > u; ++u) + if ((r = t[u]).identifier === e) return J(n, r) + }, ta.behavior.drag = function() { + function n() { + this.on("mousedown.drag", i).on("touchstart.drag", o) + } + + function e(n, t, e, i, o) { + return function() { + function a() { + var n, e, r = t(h, v); + r && (n = r[0] - M[0], e = r[1] - M[1], p |= n | e, M = r, g({ + type: "drag", + x: r[0] + l[0], + y: r[1] + l[1], + dx: n, + dy: e + })) + } + + function c() { + t(h, v) && (m.on(i + d, null).on(o + d, null), y(p && ta.event.target === f), g({ + type: "dragend" + })) + } + var l, s = this, + f = ta.event.target, + h = s.parentNode, + g = r.of(s, arguments), + p = 0, + v = n(), + d = ".drag" + (null == v ? "" : "-" + v), + m = ta.select(e(f)).on(i + d, a).on(o + d, c), + y = W(f), + M = t(h, v); + u ? (l = u.apply(s, arguments), l = [l.x - M[0], l.y - M[1]]) : l = [0, 0], g({ + type: "dragstart" + }) + } + } + var r = E(n, "drag", "dragstart", "dragend"), + u = null, + i = e(b, ta.mouse, t, "mousemove", "mouseup"), + o = e(G, ta.touch, y, "touchmove", "touchend"); + return n.origin = function(t) { + return arguments.length ? (u = t, n) : u + }, ta.rebind(n, r, "on") + }, ta.touches = function(n, t) { + return arguments.length < 2 && (t = k().touches), t ? ra(t).map(function(t) { + var e = J(n, t); + return e.identifier = t.identifier, e + }) : [] + }; + var Ca = 1e-6, + za = Ca * Ca, + qa = Math.PI, + La = 2 * qa, + Ta = La - Ca, + Ra = qa / 2, + Da = qa / 180, + Pa = 180 / qa, + Ua = Math.SQRT2, + ja = 2, + Fa = 4; + ta.interpolateZoom = function(n, t) { + function e(n) { + var t = n * y; + if (m) { + var e = rt(v), + o = i / (ja * h) * (e * ut(Ua * t + v) - et(v)); + return [r + o * l, u + o * s, i * e / rt(Ua * t + v)] + } + return [r + n * l, u + n * s, i * Math.exp(Ua * t)] + } + var r = n[0], + u = n[1], + i = n[2], + o = t[0], + a = t[1], + c = t[2], + l = o - r, + s = a - u, + f = l * l + s * s, + h = Math.sqrt(f), + g = (c * c - i * i + Fa * f) / (2 * i * ja * h), + p = (c * c - i * i - Fa * f) / (2 * c * ja * h), + v = Math.log(Math.sqrt(g * g + 1) - g), + d = Math.log(Math.sqrt(p * p + 1) - p), + m = d - v, + y = (m || Math.log(c / i)) / Ua; + return e.duration = 1e3 * y, e + }, ta.behavior.zoom = function() { + function n(n) { + n.on(q, f).on(Oa + ".zoom", g).on("dblclick.zoom", p).on(R, h) + } + + function e(n) { + return [(n[0] - k.x) / k.k, (n[1] - k.y) / k.k] + } + + function r(n) { + return [n[0] * k.k + k.x, n[1] * k.k + k.y] + } + + function u(n) { + k.k = Math.max(N[0], Math.min(N[1], n)) + } + + function i(n, t) { + t = r(t), k.x += n[0] - t[0], k.y += n[1] - t[1] + } + + function o(t, e, r, o) { + t.__chart__ = { + x: k.x, + y: k.y, + k: k.k + }, u(Math.pow(2, o)), i(d = e, r), t = ta.select(t), C > 0 && (t = t.transition().duration(C)), t.call(n.event) + } + + function a() { + b && b.domain(x.range().map(function(n) { + return (n - k.x) / k.k + }).map(x.invert)), w && w.domain(_.range().map(function(n) { + return (n - k.y) / k.k + }).map(_.invert)) + } + + function c(n) { + z++ || n({ + type: "zoomstart" + }) + } + + function l(n) { + a(), n({ + type: "zoom", + scale: k.k, + translate: [k.x, k.y] + }) + } + + function s(n) { + --z || n({ + type: "zoomend" + }), d = null + } + + function f() { + function n() { + f = 1, i(ta.mouse(u), g), l(a) + } + + function r() { + h.on(L, null).on(T, null), p(f && ta.event.target === o), s(a) + } + var u = this, + o = ta.event.target, + a = D.of(u, arguments), + f = 0, + h = ta.select(t(u)).on(L, n).on(T, r), + g = e(ta.mouse(u)), + p = W(u); + Dl.call(u), c(a) + } + + function h() { + function n() { + var n = ta.touches(p); + return g = k.k, n.forEach(function(n) { + n.identifier in d && (d[n.identifier] = e(n)) + }), n + } + + function t() { + var t = ta.event.target; + ta.select(t).on(x, r).on(b, a), _.push(t); + for (var e = ta.event.changedTouches, u = 0, i = e.length; i > u; ++u) d[e[u].identifier] = null; + var c = n(), + l = Date.now(); + if (1 === c.length) { + if (500 > l - M) { + var s = c[0]; + o(p, s, d[s.identifier], Math.floor(Math.log(k.k) / Math.LN2) + 1), S() + } + M = l + } else if (c.length > 1) { + var s = c[0], + f = c[1], + h = s[0] - f[0], + g = s[1] - f[1]; + m = h * h + g * g + } + } + + function r() { + var n, t, e, r, o = ta.touches(p); + Dl.call(p); + for (var a = 0, c = o.length; c > a; ++a, r = null) + if (e = o[a], r = d[e.identifier]) { + if (t) break; + n = e, t = r + } if (r) { + var s = (s = e[0] - n[0]) * s + (s = e[1] - n[1]) * s, + f = m && Math.sqrt(s / m); + n = [(n[0] + e[0]) / 2, (n[1] + e[1]) / 2], t = [(t[0] + r[0]) / 2, (t[1] + r[1]) / 2], u(f * g) + } + M = null, i(n, t), l(v) + } + + function a() { + if (ta.event.touches.length) { + for (var t = ta.event.changedTouches, e = 0, r = t.length; r > e; ++e) delete d[t[e].identifier]; + for (var u in d) return void n() + } + ta.selectAll(_).on(y, null), w.on(q, f).on(R, h), E(), s(v) + } + var g, p = this, + v = D.of(p, arguments), + d = {}, + m = 0, + y = ".zoom-" + ta.event.changedTouches[0].identifier, + x = "touchmove" + y, + b = "touchend" + y, + _ = [], + w = ta.select(p), + E = W(p); + t(), c(v), w.on(q, null).on(R, t) + } + + function g() { + var n = D.of(this, arguments); + y ? clearTimeout(y) : (v = e(d = m || ta.mouse(this)), Dl.call(this), c(n)), y = setTimeout(function() { + y = null, s(n) + }, 50), S(), u(Math.pow(2, .002 * Ha()) * k.k), i(d, v), l(n) + } + + function p() { + var n = ta.mouse(this), + t = Math.log(k.k) / Math.LN2; + o(this, n, e(n), ta.event.shiftKey ? Math.ceil(t) - 1 : Math.floor(t) + 1) + } + var v, d, m, y, M, x, b, _, w, k = { + x: 0, + y: 0, + k: 1 + }, + A = [960, 500], + N = Ia, + C = 250, + z = 0, + q = "mousedown.zoom", + L = "mousemove.zoom", + T = "mouseup.zoom", + R = "touchstart.zoom", + D = E(n, "zoomstart", "zoom", "zoomend"); + return Oa || (Oa = "onwheel" in ua ? (Ha = function() { + return -ta.event.deltaY * (ta.event.deltaMode ? 120 : 1) + }, "wheel") : "onmousewheel" in ua ? (Ha = function() { + return ta.event.wheelDelta + }, "mousewheel") : (Ha = function() { + return -ta.event.detail + }, "MozMousePixelScroll")), n.event = function(n) { + n.each(function() { + var n = D.of(this, arguments), + t = k; + Tl ? ta.select(this).transition().each("start.zoom", function() { + k = this.__chart__ || { + x: 0, + y: 0, + k: 1 + }, c(n) + }).tween("zoom:zoom", function() { + var e = A[0], + r = A[1], + u = d ? d[0] : e / 2, + i = d ? d[1] : r / 2, + o = ta.interpolateZoom([(u - k.x) / k.k, (i - k.y) / k.k, e / k.k], [(u - t.x) / t.k, (i - t.y) / t.k, e / t.k]); + return function(t) { + var r = o(t), + a = e / r[2]; + this.__chart__ = k = { + x: u - r[0] * a, + y: i - r[1] * a, + k: a + }, l(n) + } + }).each("interrupt.zoom", function() { + s(n) + }).each("end.zoom", function() { + s(n) + }) : (this.__chart__ = k, c(n), l(n), s(n)) + }) + }, n.translate = function(t) { + return arguments.length ? (k = { + x: +t[0], + y: +t[1], + k: k.k + }, a(), n) : [k.x, k.y] + }, n.scale = function(t) { + return arguments.length ? (k = { + x: k.x, + y: k.y, + k: +t + }, a(), n) : k.k + }, n.scaleExtent = function(t) { + return arguments.length ? (N = null == t ? Ia : [+t[0], +t[1]], n) : N + }, n.center = function(t) { + return arguments.length ? (m = t && [+t[0], +t[1]], n) : m + }, n.size = function(t) { + return arguments.length ? (A = t && [+t[0], +t[1]], n) : A + }, n.duration = function(t) { + return arguments.length ? (C = +t, n) : C + }, n.x = function(t) { + return arguments.length ? (b = t, x = t.copy(), k = { + x: 0, + y: 0, + k: 1 + }, n) : b + }, n.y = function(t) { + return arguments.length ? (w = t, _ = t.copy(), k = { + x: 0, + y: 0, + k: 1 + }, n) : w + }, ta.rebind(n, D, "on") + }; + var Ha, Oa, Ia = [0, 1 / 0]; + ta.color = ot, ot.prototype.toString = function() { + return this.rgb() + "" + }, ta.hsl = at; + var Ya = at.prototype = new ot; + Ya.brighter = function(n) { + return n = Math.pow(.7, arguments.length ? n : 1), new at(this.h, this.s, this.l / n) + }, Ya.darker = function(n) { + return n = Math.pow(.7, arguments.length ? n : 1), new at(this.h, this.s, n * this.l) + }, Ya.rgb = function() { + return ct(this.h, this.s, this.l) + }, ta.hcl = lt; + var Za = lt.prototype = new ot; + Za.brighter = function(n) { + return new lt(this.h, this.c, Math.min(100, this.l + Va * (arguments.length ? n : 1))) + }, Za.darker = function(n) { + return new lt(this.h, this.c, Math.max(0, this.l - Va * (arguments.length ? n : 1))) + }, Za.rgb = function() { + return st(this.h, this.c, this.l).rgb() + }, ta.lab = ft; + var Va = 18, + Xa = .95047, + $a = 1, + Ba = 1.08883, + Wa = ft.prototype = new ot; + Wa.brighter = function(n) { + return new ft(Math.min(100, this.l + Va * (arguments.length ? n : 1)), this.a, this.b) + }, Wa.darker = function(n) { + return new ft(Math.max(0, this.l - Va * (arguments.length ? n : 1)), this.a, this.b) + }, Wa.rgb = function() { + return ht(this.l, this.a, this.b) + }, ta.rgb = mt; + var Ja = mt.prototype = new ot; + Ja.brighter = function(n) { + n = Math.pow(.7, arguments.length ? n : 1); + var t = this.r, + e = this.g, + r = this.b, + u = 30; + return t || e || r ? (t && u > t && (t = u), e && u > e && (e = u), r && u > r && (r = u), new mt(Math.min(255, t / n), Math.min(255, e / n), Math.min(255, r / n))) : new mt(u, u, u) + }, Ja.darker = function(n) { + return n = Math.pow(.7, arguments.length ? n : 1), new mt(n * this.r, n * this.g, n * this.b) + }, Ja.hsl = function() { + return _t(this.r, this.g, this.b) + }, Ja.toString = function() { + return "#" + xt(this.r) + xt(this.g) + xt(this.b) + }; + var Ga = ta.map({ + aliceblue: 15792383, + antiquewhite: 16444375, + aqua: 65535, + aquamarine: 8388564, + azure: 15794175, + beige: 16119260, + bisque: 16770244, + black: 0, + blanchedalmond: 16772045, + blue: 255, + blueviolet: 9055202, + brown: 10824234, + burlywood: 14596231, + cadetblue: 6266528, + chartreuse: 8388352, + chocolate: 13789470, + coral: 16744272, + cornflowerblue: 6591981, + cornsilk: 16775388, + crimson: 14423100, + cyan: 65535, + darkblue: 139, + darkcyan: 35723, + darkgoldenrod: 12092939, + darkgray: 11119017, + darkgreen: 25600, + darkgrey: 11119017, + darkkhaki: 12433259, + darkmagenta: 9109643, + darkolivegreen: 5597999, + darkorange: 16747520, + darkorchid: 10040012, + darkred: 9109504, + darksalmon: 15308410, + darkseagreen: 9419919, + darkslateblue: 4734347, + darkslategray: 3100495, + darkslategrey: 3100495, + darkturquoise: 52945, + darkviolet: 9699539, + deeppink: 16716947, + deepskyblue: 49151, + dimgray: 6908265, + dimgrey: 6908265, + dodgerblue: 2003199, + firebrick: 11674146, + floralwhite: 16775920, + forestgreen: 2263842, + fuchsia: 16711935, + gainsboro: 14474460, + ghostwhite: 16316671, + gold: 16766720, + goldenrod: 14329120, + gray: 8421504, + green: 32768, + greenyellow: 11403055, + grey: 8421504, + honeydew: 15794160, + hotpink: 16738740, + indianred: 13458524, + indigo: 4915330, + ivory: 16777200, + khaki: 15787660, + lavender: 15132410, + lavenderblush: 16773365, + lawngreen: 8190976, + lemonchiffon: 16775885, + lightblue: 11393254, + lightcoral: 15761536, + lightcyan: 14745599, + lightgoldenrodyellow: 16448210, + lightgray: 13882323, + lightgreen: 9498256, + lightgrey: 13882323, + lightpink: 16758465, + lightsalmon: 16752762, + lightseagreen: 2142890, + lightskyblue: 8900346, + lightslategray: 7833753, + lightslategrey: 7833753, + lightsteelblue: 11584734, + lightyellow: 16777184, + lime: 65280, + limegreen: 3329330, + linen: 16445670, + magenta: 16711935, + maroon: 8388608, + mediumaquamarine: 6737322, + mediumblue: 205, + mediumorchid: 12211667, + mediumpurple: 9662683, + mediumseagreen: 3978097, + mediumslateblue: 8087790, + mediumspringgreen: 64154, + mediumturquoise: 4772300, + mediumvioletred: 13047173, + midnightblue: 1644912, + mintcream: 16121850, + mistyrose: 16770273, + moccasin: 16770229, + navajowhite: 16768685, + navy: 128, + oldlace: 16643558, + olive: 8421376, + olivedrab: 7048739, + orange: 16753920, + orangered: 16729344, + orchid: 14315734, + palegoldenrod: 15657130, + palegreen: 10025880, + paleturquoise: 11529966, + palevioletred: 14381203, + papayawhip: 16773077, + peachpuff: 16767673, + peru: 13468991, + pink: 16761035, + plum: 14524637, + powderblue: 11591910, + purple: 8388736, + rebeccapurple: 6697881, + red: 16711680, + rosybrown: 12357519, + royalblue: 4286945, + saddlebrown: 9127187, + salmon: 16416882, + sandybrown: 16032864, + seagreen: 3050327, + seashell: 16774638, + sienna: 10506797, + silver: 12632256, + skyblue: 8900331, + slateblue: 6970061, + slategray: 7372944, + slategrey: 7372944, + snow: 16775930, + springgreen: 65407, + steelblue: 4620980, + tan: 13808780, + teal: 32896, + thistle: 14204888, + tomato: 16737095, + turquoise: 4251856, + violet: 15631086, + wheat: 16113331, + white: 16777215, + whitesmoke: 16119285, + yellow: 16776960, + yellowgreen: 10145074 + }); + Ga.forEach(function(n, t) { + Ga.set(n, yt(t)) + }), ta.functor = Et, ta.xhr = At(y), ta.dsv = function(n, t) { + function e(n, e, i) { + arguments.length < 3 && (i = e, e = null); + var o = Nt(n, t, null == e ? r : u(e), i); + return o.row = function(n) { + return arguments.length ? o.response(null == (e = n) ? r : u(n)) : e + }, o + } + + function r(n) { + return e.parse(n.responseText) + } + + function u(n) { + return function(t) { + return e.parse(t.responseText, n) + } + } + + function i(t) { + return t.map(o).join(n) + } + + function o(n) { + return a.test(n) ? '"' + n.replace(/\"/g, '""') + '"' : n + } + var a = new RegExp('["' + n + "\n]"), + c = n.charCodeAt(0); + return e.parse = function(n, t) { + var r; + return e.parseRows(n, function(n, e) { + if (r) return r(n, e - 1); + var u = new Function("d", "return {" + n.map(function(n, t) { + return JSON.stringify(n) + ": d[" + t + "]" + }).join(",") + "}"); + r = t ? function(n, e) { + return t(u(n), e) + } : u + }) + }, e.parseRows = function(n, t) { + function e() { + if (s >= l) return o; + if (u) return u = !1, i; + var t = s; + if (34 === n.charCodeAt(t)) { + for (var e = t; e++ < l;) + if (34 === n.charCodeAt(e)) { + if (34 !== n.charCodeAt(e + 1)) break; + ++e + } s = e + 2; + var r = n.charCodeAt(e + 1); + return 13 === r ? (u = !0, 10 === n.charCodeAt(e + 2) && ++s) : 10 === r && (u = !0), n.slice(t + 1, e).replace(/""/g, '"') + } + for (; l > s;) { + var r = n.charCodeAt(s++), + a = 1; + if (10 === r) u = !0; + else if (13 === r) u = !0, 10 === n.charCodeAt(s) && (++s, ++a); + else if (r !== c) continue; + return n.slice(t, s - a) + } + return n.slice(t) + } + for (var r, u, i = {}, o = {}, a = [], l = n.length, s = 0, f = 0; + (r = e()) !== o;) { + for (var h = []; r !== i && r !== o;) h.push(r), r = e(); + t && null == (h = t(h, f++)) || a.push(h) + } + return a + }, e.format = function(t) { + if (Array.isArray(t[0])) return e.formatRows(t); + var r = new m, + u = []; + return t.forEach(function(n) { + for (var t in n) r.has(t) || u.push(r.add(t)) + }), [u.map(o).join(n)].concat(t.map(function(t) { + return u.map(function(n) { + return o(t[n]) + }).join(n) + })).join("\n") + }, e.formatRows = function(n) { + return n.map(i).join("\n") + }, e + }, ta.csv = ta.dsv(",", "text/csv"), ta.tsv = ta.dsv(" ", "text/tab-separated-values"); + var Ka, Qa, nc, tc, ec, rc = this[x(this, "requestAnimationFrame")] || function(n) { + setTimeout(n, 17) + }; + ta.timer = function(n, t, e) { + var r = arguments.length; + 2 > r && (t = 0), 3 > r && (e = Date.now()); + var u = e + t, + i = { + c: n, + t: u, + f: !1, + n: null + }; + Qa ? Qa.n = i : Ka = i, Qa = i, nc || (tc = clearTimeout(tc), nc = 1, rc(qt)) + }, ta.timer.flush = function() { + Lt(), Tt() + }, ta.round = function(n, t) { + return t ? Math.round(n * (t = Math.pow(10, t))) / t : Math.round(n) + }; + var uc = ["y", "z", "a", "f", "p", "n", "\xb5", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"].map(Dt); + ta.formatPrefix = function(n, t) { + var e = 0; + return n && (0 > n && (n *= -1), t && (n = ta.round(n, Rt(n, t))), e = 1 + Math.floor(1e-12 + Math.log(n) / Math.LN10), e = Math.max(-24, Math.min(24, 3 * Math.floor((e - 1) / 3)))), uc[8 + e / 3] + }; + var ic = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i, + oc = ta.map({ + b: function(n) { + return n.toString(2) + }, + c: function(n) { + return String.fromCharCode(n) + }, + o: function(n) { + return n.toString(8) + }, + x: function(n) { + return n.toString(16) + }, + X: function(n) { + return n.toString(16).toUpperCase() + }, + g: function(n, t) { + return n.toPrecision(t) + }, + e: function(n, t) { + return n.toExponential(t) + }, + f: function(n, t) { + return n.toFixed(t) + }, + r: function(n, t) { + return (n = ta.round(n, Rt(n, t))).toFixed(Math.max(0, Math.min(20, Rt(n * (1 + 1e-15), t)))) + } + }), + ac = ta.time = {}, + cc = Date; + jt.prototype = { + getDate: function() { + return this._.getUTCDate() + }, + getDay: function() { + return this._.getUTCDay() + }, + getFullYear: function() { + return this._.getUTCFullYear() + }, + getHours: function() { + return this._.getUTCHours() + }, + getMilliseconds: function() { + return this._.getUTCMilliseconds() + }, + getMinutes: function() { + return this._.getUTCMinutes() + }, + getMonth: function() { + return this._.getUTCMonth() + }, + getSeconds: function() { + return this._.getUTCSeconds() + }, + getTime: function() { + return this._.getTime() + }, + getTimezoneOffset: function() { + return 0 + }, + valueOf: function() { + return this._.valueOf() + }, + setDate: function() { + lc.setUTCDate.apply(this._, arguments) + }, + setDay: function() { + lc.setUTCDay.apply(this._, arguments) + }, + setFullYear: function() { + lc.setUTCFullYear.apply(this._, arguments) + }, + setHours: function() { + lc.setUTCHours.apply(this._, arguments) + }, + setMilliseconds: function() { + lc.setUTCMilliseconds.apply(this._, arguments) + }, + setMinutes: function() { + lc.setUTCMinutes.apply(this._, arguments) + }, + setMonth: function() { + lc.setUTCMonth.apply(this._, arguments) + }, + setSeconds: function() { + lc.setUTCSeconds.apply(this._, arguments) + }, + setTime: function() { + lc.setTime.apply(this._, arguments) + } + }; + var lc = Date.prototype; + ac.year = Ft(function(n) { + return n = ac.day(n), n.setMonth(0, 1), n + }, function(n, t) { + n.setFullYear(n.getFullYear() + t) + }, function(n) { + return n.getFullYear() + }), ac.years = ac.year.range, ac.years.utc = ac.year.utc.range, ac.day = Ft(function(n) { + var t = new cc(2e3, 0); + return t.setFullYear(n.getFullYear(), n.getMonth(), n.getDate()), t + }, function(n, t) { + n.setDate(n.getDate() + t) + }, function(n) { + return n.getDate() - 1 + }), ac.days = ac.day.range, ac.days.utc = ac.day.utc.range, ac.dayOfYear = function(n) { + var t = ac.year(n); + return Math.floor((n - t - 6e4 * (n.getTimezoneOffset() - t.getTimezoneOffset())) / 864e5) + }, ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"].forEach(function(n, t) { + t = 7 - t; + var e = ac[n] = Ft(function(n) { + return (n = ac.day(n)).setDate(n.getDate() - (n.getDay() + t) % 7), n + }, function(n, t) { + n.setDate(n.getDate() + 7 * Math.floor(t)) + }, function(n) { + var e = ac.year(n).getDay(); + return Math.floor((ac.dayOfYear(n) + (e + t) % 7) / 7) - (e !== t) + }); + ac[n + "s"] = e.range, ac[n + "s"].utc = e.utc.range, ac[n + "OfYear"] = function(n) { + var e = ac.year(n).getDay(); + return Math.floor((ac.dayOfYear(n) + (e + t) % 7) / 7) + } + }), ac.week = ac.sunday, ac.weeks = ac.sunday.range, ac.weeks.utc = ac.sunday.utc.range, ac.weekOfYear = ac.sundayOfYear; + var sc = { + "-": "", + _: " ", + 0: "0" + }, + fc = /^\s*\d+/, + hc = /^%/; + ta.locale = function(n) { + return { + numberFormat: Pt(n), + timeFormat: Ot(n) + } + }; + var gc = ta.locale({ + decimal: ".", + thousands: ",", + grouping: [3], + currency: ["$", ""], + dateTime: "%a %b %e %X %Y", + date: "%m/%d/%Y", + time: "%H:%M:%S", + periods: ["AM", "PM"], + days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], + months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], + shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + }); + ta.format = gc.numberFormat, ta.geo = {}, ce.prototype = { + s: 0, + t: 0, + add: function(n) { + le(n, this.t, pc), le(pc.s, this.s, this), this.s ? this.t += pc.t : this.s = pc.t + }, + reset: function() { + this.s = this.t = 0 + }, + valueOf: function() { + return this.s + } + }; + var pc = new ce; + ta.geo.stream = function(n, t) { + n && vc.hasOwnProperty(n.type) ? vc[n.type](n, t) : se(n, t) + }; + var vc = { + Feature: function(n, t) { + se(n.geometry, t) + }, + FeatureCollection: function(n, t) { + for (var e = n.features, r = -1, u = e.length; ++r < u;) se(e[r].geometry, t) + } + }, + dc = { + Sphere: function(n, t) { + t.sphere() + }, + Point: function(n, t) { + n = n.coordinates, t.point(n[0], n[1], n[2]) + }, + MultiPoint: function(n, t) { + for (var e = n.coordinates, r = -1, u = e.length; ++r < u;) n = e[r], t.point(n[0], n[1], n[2]) + }, + LineString: function(n, t) { + fe(n.coordinates, t, 0) + }, + MultiLineString: function(n, t) { + for (var e = n.coordinates, r = -1, u = e.length; ++r < u;) fe(e[r], t, 0) + }, + Polygon: function(n, t) { + he(n.coordinates, t) + }, + MultiPolygon: function(n, t) { + for (var e = n.coordinates, r = -1, u = e.length; ++r < u;) he(e[r], t) + }, + GeometryCollection: function(n, t) { + for (var e = n.geometries, r = -1, u = e.length; ++r < u;) se(e[r], t) + } + }; + ta.geo.area = function(n) { + return mc = 0, ta.geo.stream(n, Mc), mc + }; + var mc, yc = new ce, + Mc = { + sphere: function() { + mc += 4 * qa + }, + point: b, + lineStart: b, + lineEnd: b, + polygonStart: function() { + yc.reset(), Mc.lineStart = ge + }, + polygonEnd: function() { + var n = 2 * yc; + mc += 0 > n ? 4 * qa + n : n, Mc.lineStart = Mc.lineEnd = Mc.point = b + } + }; + ta.geo.bounds = function() { + function n(n, t) { + M.push(x = [s = n, h = n]), f > t && (f = t), t > g && (g = t) + } + + function t(t, e) { + var r = pe([t * Da, e * Da]); + if (m) { + var u = de(m, r), + i = [u[1], -u[0], 0], + o = de(i, u); + Me(o), o = xe(o); + var c = t - p, + l = c > 0 ? 1 : -1, + v = o[0] * Pa * l, + d = ga(c) > 180; + if (d ^ (v > l * p && l * t > v)) { + var y = o[1] * Pa; + y > g && (g = y) + } else if (v = (v + 360) % 360 - 180, d ^ (v > l * p && l * t > v)) { + var y = -o[1] * Pa; + f > y && (f = y) + } else f > e && (f = e), e > g && (g = e); + d ? p > t ? a(s, t) > a(s, h) && (h = t) : a(t, h) > a(s, h) && (s = t) : h >= s ? (s > t && (s = t), t > h && (h = t)) : t > p ? a(s, t) > a(s, h) && (h = t) : a(t, h) > a(s, h) && (s = t) + } else n(t, e); + m = r, p = t + } + + function e() { + b.point = t + } + + function r() { + x[0] = s, x[1] = h, b.point = n, m = null + } + + function u(n, e) { + if (m) { + var r = n - p; + y += ga(r) > 180 ? r + (r > 0 ? 360 : -360) : r + } else v = n, d = e; + Mc.point(n, e), t(n, e) + } + + function i() { + Mc.lineStart() + } + + function o() { + u(v, d), Mc.lineEnd(), ga(y) > Ca && (s = -(h = 180)), x[0] = s, x[1] = h, m = null + } + + function a(n, t) { + return (t -= n) < 0 ? t + 360 : t + } + + function c(n, t) { + return n[0] - t[0] + } + + function l(n, t) { + return t[0] <= t[1] ? t[0] <= n && n <= t[1] : n < t[0] || t[1] < n + } + var s, f, h, g, p, v, d, m, y, M, x, b = { + point: n, + lineStart: e, + lineEnd: r, + polygonStart: function() { + b.point = u, b.lineStart = i, b.lineEnd = o, y = 0, Mc.polygonStart() + }, + polygonEnd: function() { + Mc.polygonEnd(), b.point = n, b.lineStart = e, b.lineEnd = r, 0 > yc ? (s = -(h = 180), f = -(g = 90)) : y > Ca ? g = 90 : -Ca > y && (f = -90), x[0] = s, x[1] = h + } + }; + return function(n) { + g = h = -(s = f = 1 / 0), M = [], ta.geo.stream(n, b); + var t = M.length; + if (t) { + M.sort(c); + for (var e, r = 1, u = M[0], i = [u]; t > r; ++r) e = M[r], l(e[0], u) || l(e[1], u) ? (a(u[0], e[1]) > a(u[0], u[1]) && (u[1] = e[1]), a(e[0], u[1]) > a(u[0], u[1]) && (u[0] = e[0])) : i.push(u = e); + for (var o, e, p = -1 / 0, t = i.length - 1, r = 0, u = i[t]; t >= r; u = e, ++r) e = i[r], (o = a(u[1], e[0])) > p && (p = o, s = e[0], h = u[1]) + } + return M = x = null, 1 / 0 === s || 1 / 0 === f ? [ + [0 / 0, 0 / 0], + [0 / 0, 0 / 0] + ] : [ + [s, f], + [h, g] + ] + } + }(), ta.geo.centroid = function(n) { + xc = bc = _c = wc = Sc = kc = Ec = Ac = Nc = Cc = zc = 0, ta.geo.stream(n, qc); + var t = Nc, + e = Cc, + r = zc, + u = t * t + e * e + r * r; + return za > u && (t = kc, e = Ec, r = Ac, Ca > bc && (t = _c, e = wc, r = Sc), u = t * t + e * e + r * r, za > u) ? [0 / 0, 0 / 0] : [Math.atan2(e, t) * Pa, tt(r / Math.sqrt(u)) * Pa] + }; + var xc, bc, _c, wc, Sc, kc, Ec, Ac, Nc, Cc, zc, qc = { + sphere: b, + point: _e, + lineStart: Se, + lineEnd: ke, + polygonStart: function() { + qc.lineStart = Ee + }, + polygonEnd: function() { + qc.lineStart = Se + } + }, + Lc = Le(Ne, Pe, je, [-qa, -qa / 2]), + Tc = 1e9; + ta.geo.clipExtent = function() { + var n, t, e, r, u, i, o = { + stream: function(n) { + return u && (u.valid = !1), u = i(n), u.valid = !0, u + }, + extent: function(a) { + return arguments.length ? (i = Ie(n = +a[0][0], t = +a[0][1], e = +a[1][0], r = +a[1][1]), u && (u.valid = !1, u = null), o) : [ + [n, t], + [e, r] + ] + } + }; + return o.extent([ + [0, 0], + [960, 500] + ]) + }, (ta.geo.conicEqualArea = function() { + return Ye(Ze) + }).raw = Ze, ta.geo.albers = function() { + return ta.geo.conicEqualArea().rotate([96, 0]).center([-.6, 38.7]).parallels([29.5, 45.5]).scale(1070) + }, ta.geo.albersUsa = function() { + function n(n) { + var i = n[0], + o = n[1]; + return t = null, e(i, o), t || (r(i, o), t) || u(i, o), t + } + var t, e, r, u, i = ta.geo.albers(), + o = ta.geo.conicEqualArea().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]), + a = ta.geo.conicEqualArea().rotate([157, 0]).center([-3, 19.9]).parallels([8, 18]), + c = { + point: function(n, e) { + t = [n, e] + } + }; + return n.invert = function(n) { + var t = i.scale(), + e = i.translate(), + r = (n[0] - e[0]) / t, + u = (n[1] - e[1]) / t; + return (u >= .12 && .234 > u && r >= -.425 && -.214 > r ? o : u >= .166 && .234 > u && r >= -.214 && -.115 > r ? a : i).invert(n) + }, n.stream = function(n) { + var t = i.stream(n), + e = o.stream(n), + r = a.stream(n); + return { + point: function(n, u) { + t.point(n, u), e.point(n, u), r.point(n, u) + }, + sphere: function() { + t.sphere(), e.sphere(), r.sphere() + }, + lineStart: function() { + t.lineStart(), e.lineStart(), r.lineStart() + }, + lineEnd: function() { + t.lineEnd(), e.lineEnd(), r.lineEnd() + }, + polygonStart: function() { + t.polygonStart(), e.polygonStart(), r.polygonStart() + }, + polygonEnd: function() { + t.polygonEnd(), e.polygonEnd(), r.polygonEnd() + } + } + }, n.precision = function(t) { + return arguments.length ? (i.precision(t), o.precision(t), a.precision(t), n) : i.precision() + }, n.scale = function(t) { + return arguments.length ? (i.scale(t), o.scale(.35 * t), a.scale(t), n.translate(i.translate())) : i.scale() + }, n.translate = function(t) { + if (!arguments.length) return i.translate(); + var l = i.scale(), + s = +t[0], + f = +t[1]; + return e = i.translate(t).clipExtent([ + [s - .455 * l, f - .238 * l], + [s + .455 * l, f + .238 * l] + ]).stream(c).point, r = o.translate([s - .307 * l, f + .201 * l]).clipExtent([ + [s - .425 * l + Ca, f + .12 * l + Ca], + [s - .214 * l - Ca, f + .234 * l - Ca] + ]).stream(c).point, u = a.translate([s - .205 * l, f + .212 * l]).clipExtent([ + [s - .214 * l + Ca, f + .166 * l + Ca], + [s - .115 * l - Ca, f + .234 * l - Ca] + ]).stream(c).point, n + }, n.scale(1070) + }; + var Rc, Dc, Pc, Uc, jc, Fc, Hc = { + point: b, + lineStart: b, + lineEnd: b, + polygonStart: function() { + Dc = 0, Hc.lineStart = Ve + }, + polygonEnd: function() { + Hc.lineStart = Hc.lineEnd = Hc.point = b, Rc += ga(Dc / 2) + } + }, + Oc = { + point: Xe, + lineStart: b, + lineEnd: b, + polygonStart: b, + polygonEnd: b + }, + Ic = { + point: We, + lineStart: Je, + lineEnd: Ge, + polygonStart: function() { + Ic.lineStart = Ke + }, + polygonEnd: function() { + Ic.point = We, Ic.lineStart = Je, Ic.lineEnd = Ge + } + }; + ta.geo.path = function() { + function n(n) { + return n && ("function" == typeof a && i.pointRadius(+a.apply(this, arguments)), o && o.valid || (o = u(i)), ta.geo.stream(n, o)), i.result() + } + + function t() { + return o = null, n + } + var e, r, u, i, o, a = 4.5; + return n.area = function(n) { + return Rc = 0, ta.geo.stream(n, u(Hc)), Rc + }, n.centroid = function(n) { + return _c = wc = Sc = kc = Ec = Ac = Nc = Cc = zc = 0, ta.geo.stream(n, u(Ic)), zc ? [Nc / zc, Cc / zc] : Ac ? [kc / Ac, Ec / Ac] : Sc ? [_c / Sc, wc / Sc] : [0 / 0, 0 / 0] + }, n.bounds = function(n) { + return jc = Fc = -(Pc = Uc = 1 / 0), ta.geo.stream(n, u(Oc)), [ + [Pc, Uc], + [jc, Fc] + ] + }, n.projection = function(n) { + return arguments.length ? (u = (e = n) ? n.stream || tr(n) : y, t()) : e + }, n.context = function(n) { + return arguments.length ? (i = null == (r = n) ? new $e : new Qe(n), "function" != typeof a && i.pointRadius(a), t()) : r + }, n.pointRadius = function(t) { + return arguments.length ? (a = "function" == typeof t ? t : (i.pointRadius(+t), +t), n) : a + }, n.projection(ta.geo.albersUsa()).context(null) + }, ta.geo.transform = function(n) { + return { + stream: function(t) { + var e = new er(t); + for (var r in n) e[r] = n[r]; + return e + } + } + }, er.prototype = { + point: function(n, t) { + this.stream.point(n, t) + }, + sphere: function() { + this.stream.sphere() + }, + lineStart: function() { + this.stream.lineStart() + }, + lineEnd: function() { + this.stream.lineEnd() + }, + polygonStart: function() { + this.stream.polygonStart() + }, + polygonEnd: function() { + this.stream.polygonEnd() + } + }, ta.geo.projection = ur, ta.geo.projectionMutator = ir, (ta.geo.equirectangular = function() { + return ur(ar) + }).raw = ar.invert = ar, ta.geo.rotation = function(n) { + function t(t) { + return t = n(t[0] * Da, t[1] * Da), t[0] *= Pa, t[1] *= Pa, t + } + return n = lr(n[0] % 360 * Da, n[1] * Da, n.length > 2 ? n[2] * Da : 0), t.invert = function(t) { + return t = n.invert(t[0] * Da, t[1] * Da), t[0] *= Pa, t[1] *= Pa, t + }, t + }, cr.invert = ar, ta.geo.circle = function() { + function n() { + var n = "function" == typeof r ? r.apply(this, arguments) : r, + t = lr(-n[0] * Da, -n[1] * Da, 0).invert, + u = []; + return e(null, null, 1, { + point: function(n, e) { + u.push(n = t(n, e)), n[0] *= Pa, n[1] *= Pa + } + }), { + type: "Polygon", + coordinates: [u] + } + } + var t, e, r = [0, 0], + u = 6; + return n.origin = function(t) { + return arguments.length ? (r = t, n) : r + }, n.angle = function(r) { + return arguments.length ? (e = gr((t = +r) * Da, u * Da), n) : t + }, n.precision = function(r) { + return arguments.length ? (e = gr(t * Da, (u = +r) * Da), n) : u + }, n.angle(90) + }, ta.geo.distance = function(n, t) { + var e, r = (t[0] - n[0]) * Da, + u = n[1] * Da, + i = t[1] * Da, + o = Math.sin(r), + a = Math.cos(r), + c = Math.sin(u), + l = Math.cos(u), + s = Math.sin(i), + f = Math.cos(i); + return Math.atan2(Math.sqrt((e = f * o) * e + (e = l * s - c * f * a) * e), c * s + l * f * a) + }, ta.geo.graticule = function() { + function n() { + return { + type: "MultiLineString", + coordinates: t() + } + } + + function t() { + return ta.range(Math.ceil(i / d) * d, u, d).map(h).concat(ta.range(Math.ceil(l / m) * m, c, m).map(g)).concat(ta.range(Math.ceil(r / p) * p, e, p).filter(function(n) { + return ga(n % d) > Ca + }).map(s)).concat(ta.range(Math.ceil(a / v) * v, o, v).filter(function(n) { + return ga(n % m) > Ca + }).map(f)) + } + var e, r, u, i, o, a, c, l, s, f, h, g, p = 10, + v = p, + d = 90, + m = 360, + y = 2.5; + return n.lines = function() { + return t().map(function(n) { + return { + type: "LineString", + coordinates: n + } + }) + }, n.outline = function() { + return { + type: "Polygon", + coordinates: [h(i).concat(g(c).slice(1), h(u).reverse().slice(1), g(l).reverse().slice(1))] + } + }, n.extent = function(t) { + return arguments.length ? n.majorExtent(t).minorExtent(t) : n.minorExtent() + }, n.majorExtent = function(t) { + return arguments.length ? (i = +t[0][0], u = +t[1][0], l = +t[0][1], c = +t[1][1], i > u && (t = i, i = u, u = t), l > c && (t = l, l = c, c = t), n.precision(y)) : [ + [i, l], + [u, c] + ] + }, n.minorExtent = function(t) { + return arguments.length ? (r = +t[0][0], e = +t[1][0], a = +t[0][1], o = +t[1][1], r > e && (t = r, r = e, e = t), a > o && (t = a, a = o, o = t), n.precision(y)) : [ + [r, a], + [e, o] + ] + }, n.step = function(t) { + return arguments.length ? n.majorStep(t).minorStep(t) : n.minorStep() + }, n.majorStep = function(t) { + return arguments.length ? (d = +t[0], m = +t[1], n) : [d, m] + }, n.minorStep = function(t) { + return arguments.length ? (p = +t[0], v = +t[1], n) : [p, v] + }, n.precision = function(t) { + return arguments.length ? (y = +t, s = vr(a, o, 90), f = dr(r, e, y), h = vr(l, c, 90), g = dr(i, u, y), n) : y + }, n.majorExtent([ + [-180, -90 + Ca], + [180, 90 - Ca] + ]).minorExtent([ + [-180, -80 - Ca], + [180, 80 + Ca] + ]) + }, ta.geo.greatArc = function() { + function n() { + return { + type: "LineString", + coordinates: [t || r.apply(this, arguments), e || u.apply(this, arguments)] + } + } + var t, e, r = mr, + u = yr; + return n.distance = function() { + return ta.geo.distance(t || r.apply(this, arguments), e || u.apply(this, arguments)) + }, n.source = function(e) { + return arguments.length ? (r = e, t = "function" == typeof e ? null : e, n) : r + }, n.target = function(t) { + return arguments.length ? (u = t, e = "function" == typeof t ? null : t, n) : u + }, n.precision = function() { + return arguments.length ? n : 0 + }, n + }, ta.geo.interpolate = function(n, t) { + return Mr(n[0] * Da, n[1] * Da, t[0] * Da, t[1] * Da) + }, ta.geo.length = function(n) { + return Yc = 0, ta.geo.stream(n, Zc), Yc + }; + var Yc, Zc = { + sphere: b, + point: b, + lineStart: xr, + lineEnd: b, + polygonStart: b, + polygonEnd: b + }, + Vc = br(function(n) { + return Math.sqrt(2 / (1 + n)) + }, function(n) { + return 2 * Math.asin(n / 2) + }); + (ta.geo.azimuthalEqualArea = function() { + return ur(Vc) + }).raw = Vc; + var Xc = br(function(n) { + var t = Math.acos(n); + return t && t / Math.sin(t) + }, y); + (ta.geo.azimuthalEquidistant = function() { + return ur(Xc) + }).raw = Xc, (ta.geo.conicConformal = function() { + return Ye(_r) + }).raw = _r, (ta.geo.conicEquidistant = function() { + return Ye(wr) + }).raw = wr; + var $c = br(function(n) { + return 1 / n + }, Math.atan); + (ta.geo.gnomonic = function() { + return ur($c) + }).raw = $c, Sr.invert = function(n, t) { + return [n, 2 * Math.atan(Math.exp(t)) - Ra] + }, (ta.geo.mercator = function() { + return kr(Sr) + }).raw = Sr; + var Bc = br(function() { + return 1 + }, Math.asin); + (ta.geo.orthographic = function() { + return ur(Bc) + }).raw = Bc; + var Wc = br(function(n) { + return 1 / (1 + n) + }, function(n) { + return 2 * Math.atan(n) + }); + (ta.geo.stereographic = function() { + return ur(Wc) + }).raw = Wc, Er.invert = function(n, t) { + return [-t, 2 * Math.atan(Math.exp(n)) - Ra] + }, (ta.geo.transverseMercator = function() { + var n = kr(Er), + t = n.center, + e = n.rotate; + return n.center = function(n) { + return n ? t([-n[1], n[0]]) : (n = t(), [n[1], -n[0]]) + }, n.rotate = function(n) { + return n ? e([n[0], n[1], n.length > 2 ? n[2] + 90 : 90]) : (n = e(), [n[0], n[1], n[2] - 90]) + }, e([0, 0, 90]) + }).raw = Er, ta.geom = {}, ta.geom.hull = function(n) { + function t(n) { + if (n.length < 3) return []; + var t, u = Et(e), + i = Et(r), + o = n.length, + a = [], + c = []; + for (t = 0; o > t; t++) a.push([+u.call(this, n[t], t), +i.call(this, n[t], t), t]); + for (a.sort(zr), t = 0; o > t; t++) c.push([a[t][0], -a[t][1]]); + var l = Cr(a), + s = Cr(c), + f = s[0] === l[0], + h = s[s.length - 1] === l[l.length - 1], + g = []; + for (t = l.length - 1; t >= 0; --t) g.push(n[a[l[t]][2]]); + for (t = +f; t < s.length - h; ++t) g.push(n[a[s[t]][2]]); + return g + } + var e = Ar, + r = Nr; + return arguments.length ? t(n) : (t.x = function(n) { + return arguments.length ? (e = n, t) : e + }, t.y = function(n) { + return arguments.length ? (r = n, t) : r + }, t) + }, ta.geom.polygon = function(n) { + return ya(n, Jc), n + }; + var Jc = ta.geom.polygon.prototype = []; + Jc.area = function() { + for (var n, t = -1, e = this.length, r = this[e - 1], u = 0; ++t < e;) n = r, r = this[t], u += n[1] * r[0] - n[0] * r[1]; + return .5 * u + }, Jc.centroid = function(n) { + var t, e, r = -1, + u = this.length, + i = 0, + o = 0, + a = this[u - 1]; + for (arguments.length || (n = -1 / (6 * this.area())); ++r < u;) t = a, a = this[r], e = t[0] * a[1] - a[0] * t[1], i += (t[0] + a[0]) * e, o += (t[1] + a[1]) * e; + return [i * n, o * n] + }, Jc.clip = function(n) { + for (var t, e, r, u, i, o, a = Tr(n), c = -1, l = this.length - Tr(this), s = this[l - 1]; ++c < l;) { + for (t = n.slice(), n.length = 0, u = this[c], i = t[(r = t.length - a) - 1], e = -1; ++e < r;) o = t[e], qr(o, s, u) ? (qr(i, s, u) || n.push(Lr(i, o, s, u)), n.push(o)) : qr(i, s, u) && n.push(Lr(i, o, s, u)), i = o; + a && n.push(n[0]), s = u + } + return n + }; + var Gc, Kc, Qc, nl, tl, el = [], + rl = []; + Or.prototype.prepare = function() { + for (var n, t = this.edges, e = t.length; e--;) n = t[e].edge, n.b && n.a || t.splice(e, 1); + return t.sort(Yr), t.length + }, Qr.prototype = { + start: function() { + return this.edge.l === this.site ? this.edge.a : this.edge.b + }, + end: function() { + return this.edge.l === this.site ? this.edge.b : this.edge.a + } + }, nu.prototype = { + insert: function(n, t) { + var e, r, u; + if (n) { + if (t.P = n, t.N = n.N, n.N && (n.N.P = t), n.N = t, n.R) { + for (n = n.R; n.L;) n = n.L; + n.L = t + } else n.R = t; + e = n + } else this._ ? (n = uu(this._), t.P = null, t.N = n, n.P = n.L = t, e = n) : (t.P = t.N = null, this._ = t, e = null); + for (t.L = t.R = null, t.U = e, t.C = !0, n = t; e && e.C;) r = e.U, e === r.L ? (u = r.R, u && u.C ? (e.C = u.C = !1, r.C = !0, n = r) : (n === e.R && (eu(this, e), n = e, e = n.U), e.C = !1, r.C = !0, ru(this, r))) : (u = r.L, u && u.C ? (e.C = u.C = !1, r.C = !0, n = r) : (n === e.L && (ru(this, e), n = e, e = n.U), e.C = !1, r.C = !0, eu(this, r))), e = n.U; + this._.C = !1 + }, + remove: function(n) { + n.N && (n.N.P = n.P), n.P && (n.P.N = n.N), n.N = n.P = null; + var t, e, r, u = n.U, + i = n.L, + o = n.R; + if (e = i ? o ? uu(o) : i : o, u ? u.L === n ? u.L = e : u.R = e : this._ = e, i && o ? (r = e.C, e.C = n.C, e.L = i, i.U = e, e !== o ? (u = e.U, e.U = n.U, n = e.R, u.L = n, e.R = o, o.U = e) : (e.U = u, u = e, n = e.R)) : (r = n.C, n = e), n && (n.U = u), !r) { + if (n && n.C) return void(n.C = !1); + do { + if (n === this._) break; + if (n === u.L) { + if (t = u.R, t.C && (t.C = !1, u.C = !0, eu(this, u), t = u.R), t.L && t.L.C || t.R && t.R.C) { + t.R && t.R.C || (t.L.C = !1, t.C = !0, ru(this, t), t = u.R), t.C = u.C, u.C = t.R.C = !1, eu(this, u), n = this._; + break + } + } else if (t = u.L, t.C && (t.C = !1, u.C = !0, ru(this, u), t = u.L), t.L && t.L.C || t.R && t.R.C) { + t.L && t.L.C || (t.R.C = !1, t.C = !0, eu(this, t), t = u.L), t.C = u.C, u.C = t.L.C = !1, ru(this, u), n = this._; + break + } + t.C = !0, n = u, u = u.U + } while (!n.C); + n && (n.C = !1) + } + } + }, ta.geom.voronoi = function(n) { + function t(n) { + var t = new Array(n.length), + r = a[0][0], + u = a[0][1], + i = a[1][0], + o = a[1][1]; + return iu(e(n), a).cells.forEach(function(e, a) { + var c = e.edges, + l = e.site, + s = t[a] = c.length ? c.map(function(n) { + var t = n.start(); + return [t.x, t.y] + }) : l.x >= r && l.x <= i && l.y >= u && l.y <= o ? [ + [r, o], + [i, o], + [i, u], + [r, u] + ] : []; + s.point = n[a] + }), t + } + + function e(n) { + return n.map(function(n, t) { + return { + x: Math.round(i(n, t) / Ca) * Ca, + y: Math.round(o(n, t) / Ca) * Ca, + i: t + } + }) + } + var r = Ar, + u = Nr, + i = r, + o = u, + a = ul; + return n ? t(n) : (t.links = function(n) { + return iu(e(n)).edges.filter(function(n) { + return n.l && n.r + }).map(function(t) { + return { + source: n[t.l.i], + target: n[t.r.i] + } + }) + }, t.triangles = function(n) { + var t = []; + return iu(e(n)).cells.forEach(function(e, r) { + for (var u, i, o = e.site, a = e.edges.sort(Yr), c = -1, l = a.length, s = a[l - 1].edge, f = s.l === o ? s.r : s.l; ++c < l;) u = s, i = f, s = a[c].edge, f = s.l === o ? s.r : s.l, r < i.i && r < f.i && au(o, i, f) < 0 && t.push([n[r], n[i.i], n[f.i]]) + }), t + }, t.x = function(n) { + return arguments.length ? (i = Et(r = n), t) : r + }, t.y = function(n) { + return arguments.length ? (o = Et(u = n), t) : u + }, t.clipExtent = function(n) { + return arguments.length ? (a = null == n ? ul : n, t) : a === ul ? null : a + }, t.size = function(n) { + return arguments.length ? t.clipExtent(n && [ + [0, 0], n + ]) : a === ul ? null : a && a[1] + }, t) + }; + var ul = [ + [-1e6, -1e6], + [1e6, 1e6] + ]; + ta.geom.delaunay = function(n) { + return ta.geom.voronoi().triangles(n) + }, ta.geom.quadtree = function(n, t, e, r, u) { + function i(n) { + function i(n, t, e, r, u, i, o, a) { + if (!isNaN(e) && !isNaN(r)) + if (n.leaf) { + var c = n.x, + s = n.y; + if (null != c) + if (ga(c - e) + ga(s - r) < .01) l(n, t, e, r, u, i, o, a); + else { + var f = n.point; + n.x = n.y = n.point = null, l(n, f, c, s, u, i, o, a), l(n, t, e, r, u, i, o, a) + } + else n.x = e, n.y = r, n.point = t + } else l(n, t, e, r, u, i, o, a) + } + + function l(n, t, e, r, u, o, a, c) { + var l = .5 * (u + a), + s = .5 * (o + c), + f = e >= l, + h = r >= s, + g = h << 1 | f; + n.leaf = !1, n = n.nodes[g] || (n.nodes[g] = su()), f ? u = l : a = l, h ? o = s : c = s, i(n, t, e, r, u, o, a, c) + } + var s, f, h, g, p, v, d, m, y, M = Et(a), + x = Et(c); + if (null != t) v = t, d = e, m = r, y = u; + else if (m = y = -(v = d = 1 / 0), f = [], h = [], p = n.length, o) + for (g = 0; p > g; ++g) s = n[g], s.x < v && (v = s.x), s.y < d && (d = s.y), s.x > m && (m = s.x), s.y > y && (y = s.y), f.push(s.x), h.push(s.y); + else + for (g = 0; p > g; ++g) { + var b = +M(s = n[g], g), + _ = +x(s, g); + v > b && (v = b), d > _ && (d = _), b > m && (m = b), _ > y && (y = _), f.push(b), h.push(_) + } + var w = m - v, + S = y - d; + w > S ? y = d + w : m = v + S; + var k = su(); + if (k.add = function(n) { + i(k, n, +M(n, ++g), +x(n, g), v, d, m, y) + }, k.visit = function(n) { + fu(n, k, v, d, m, y) + }, k.find = function(n) { + return hu(k, n[0], n[1], v, d, m, y) + }, g = -1, null == t) { + for (; ++g < p;) i(k, n[g], f[g], h[g], v, d, m, y); + --g + } else n.forEach(k.add); + return f = h = n = s = null, k + } + var o, a = Ar, + c = Nr; + return (o = arguments.length) ? (a = cu, c = lu, 3 === o && (u = e, r = t, e = t = 0), i(n)) : (i.x = function(n) { + return arguments.length ? (a = n, i) : a + }, i.y = function(n) { + return arguments.length ? (c = n, i) : c + }, i.extent = function(n) { + return arguments.length ? (null == n ? t = e = r = u = null : (t = +n[0][0], e = +n[0][1], r = +n[1][0], u = +n[1][1]), i) : null == t ? null : [ + [t, e], + [r, u] + ] + }, i.size = function(n) { + return arguments.length ? (null == n ? t = e = r = u = null : (t = e = 0, r = +n[0], u = +n[1]), i) : null == t ? null : [r - t, u - e] + }, i) + }, ta.interpolateRgb = gu, ta.interpolateObject = pu, ta.interpolateNumber = vu, ta.interpolateString = du; + var il = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, + ol = new RegExp(il.source, "g"); + ta.interpolate = mu, ta.interpolators = [function(n, t) { + var e = typeof t; + return ("string" === e ? Ga.has(t) || /^(#|rgb\(|hsl\()/.test(t) ? gu : du : t instanceof ot ? gu : Array.isArray(t) ? yu : "object" === e && isNaN(t) ? pu : vu)(n, t) + }], ta.interpolateArray = yu; + var al = function() { + return y + }, + cl = ta.map({ + linear: al, + poly: ku, + quad: function() { + return _u + }, + cubic: function() { + return wu + }, + sin: function() { + return Eu + }, + exp: function() { + return Au + }, + circle: function() { + return Nu + }, + elastic: Cu, + back: zu, + bounce: function() { + return qu + } + }), + ll = ta.map({ + "in": y, + out: xu, + "in-out": bu, + "out-in": function(n) { + return bu(xu(n)) + } + }); + ta.ease = function(n) { + var t = n.indexOf("-"), + e = t >= 0 ? n.slice(0, t) : n, + r = t >= 0 ? n.slice(t + 1) : "in"; + return e = cl.get(e) || al, r = ll.get(r) || y, Mu(r(e.apply(null, ea.call(arguments, 1)))) + }, ta.interpolateHcl = Lu, ta.interpolateHsl = Tu, ta.interpolateLab = Ru, ta.interpolateRound = Du, ta.transform = function(n) { + var t = ua.createElementNS(ta.ns.prefix.svg, "g"); + return (ta.transform = function(n) { + if (null != n) { + t.setAttribute("transform", n); + var e = t.transform.baseVal.consolidate() + } + return new Pu(e ? e.matrix : sl) + })(n) + }, Pu.prototype.toString = function() { + return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")" + }; + var sl = { + a: 1, + b: 0, + c: 0, + d: 1, + e: 0, + f: 0 + }; + ta.interpolateTransform = Hu, ta.layout = {}, ta.layout.bundle = function() { + return function(n) { + for (var t = [], e = -1, r = n.length; ++e < r;) t.push(Yu(n[e])); + return t + } + }, ta.layout.chord = function() { + function n() { + var n, l, f, h, g, p = {}, + v = [], + d = ta.range(i), + m = []; + for (e = [], r = [], n = 0, h = -1; ++h < i;) { + for (l = 0, g = -1; ++g < i;) l += u[h][g]; + v.push(l), m.push(ta.range(i)), n += l + } + for (o && d.sort(function(n, t) { + return o(v[n], v[t]) + }), a && m.forEach(function(n, t) { + n.sort(function(n, e) { + return a(u[t][n], u[t][e]) + }) + }), n = (La - s * i) / n, l = 0, h = -1; ++h < i;) { + for (f = l, g = -1; ++g < i;) { + var y = d[h], + M = m[y][g], + x = u[y][M], + b = l, + _ = l += x * n; + p[y + "-" + M] = { + index: y, + subindex: M, + startAngle: b, + endAngle: _, + value: x + } + } + r[y] = { + index: y, + startAngle: f, + endAngle: l, + value: (l - f) / n + }, l += s + } + for (h = -1; ++h < i;) + for (g = h - 1; ++g < i;) { + var w = p[h + "-" + g], + S = p[g + "-" + h]; + (w.value || S.value) && e.push(w.value < S.value ? { + source: S, + target: w + } : { + source: w, + target: S + }) + } + c && t() + } + + function t() { + e.sort(function(n, t) { + return c((n.source.value + n.target.value) / 2, (t.source.value + t.target.value) / 2) + }) + } + var e, r, u, i, o, a, c, l = {}, + s = 0; + return l.matrix = function(n) { + return arguments.length ? (i = (u = n) && u.length, e = r = null, l) : u + }, l.padding = function(n) { + return arguments.length ? (s = n, e = r = null, l) : s + }, l.sortGroups = function(n) { + return arguments.length ? (o = n, e = r = null, l) : o + }, l.sortSubgroups = function(n) { + return arguments.length ? (a = n, e = null, l) : a + }, l.sortChords = function(n) { + return arguments.length ? (c = n, e && t(), l) : c + }, l.chords = function() { + return e || n(), e + }, l.groups = function() { + return r || n(), r + }, l + }, ta.layout.force = function() { + function n(n) { + return function(t, e, r, u) { + if (t.point !== n) { + var i = t.cx - n.x, + o = t.cy - n.y, + a = u - e, + c = i * i + o * o; + if (c > a * a / d) { + if (p > c) { + var l = t.charge / c; + n.px -= i * l, n.py -= o * l + } + return !0 + } + if (t.point && c && p > c) { + var l = t.pointCharge / c; + n.px -= i * l, n.py -= o * l + } + } + return !t.charge + } + } + + function t(n) { + n.px = ta.event.x, n.py = ta.event.y, a.resume() + } + var e, r, u, i, o, a = {}, + c = ta.dispatch("start", "tick", "end"), + l = [1, 1], + s = .9, + f = fl, + h = hl, + g = -30, + p = gl, + v = .1, + d = .64, + m = [], + M = []; + return a.tick = function() { + if ((r *= .99) < .005) return c.end({ + type: "end", + alpha: r = 0 + }), !0; + var t, e, a, f, h, p, d, y, x, b = m.length, + _ = M.length; + for (e = 0; _ > e; ++e) a = M[e], f = a.source, h = a.target, y = h.x - f.x, x = h.y - f.y, (p = y * y + x * x) && (p = r * i[e] * ((p = Math.sqrt(p)) - u[e]) / p, y *= p, x *= p, h.x -= y * (d = f.weight / (h.weight + f.weight)), h.y -= x * d, f.x += y * (d = 1 - d), f.y += x * d); + if ((d = r * v) && (y = l[0] / 2, x = l[1] / 2, e = -1, d)) + for (; ++e < b;) a = m[e], a.x += (y - a.x) * d, a.y += (x - a.y) * d; + if (g) + for (Ju(t = ta.geom.quadtree(m), r, o), e = -1; ++e < b;)(a = m[e]).fixed || t.visit(n(a)); + for (e = -1; ++e < b;) a = m[e], a.fixed ? (a.x = a.px, a.y = a.py) : (a.x -= (a.px - (a.px = a.x)) * s, a.y -= (a.py - (a.py = a.y)) * s); + c.tick({ + type: "tick", + alpha: r + }) + }, a.nodes = function(n) { + return arguments.length ? (m = n, a) : m + }, a.links = function(n) { + return arguments.length ? (M = n, a) : M + }, a.size = function(n) { + return arguments.length ? (l = n, a) : l + }, a.linkDistance = function(n) { + return arguments.length ? (f = "function" == typeof n ? n : +n, a) : f + }, a.distance = a.linkDistance, a.linkStrength = function(n) { + return arguments.length ? (h = "function" == typeof n ? n : +n, a) : h + }, a.friction = function(n) { + return arguments.length ? (s = +n, a) : s + }, a.charge = function(n) { + return arguments.length ? (g = "function" == typeof n ? n : +n, a) : g + }, a.chargeDistance = function(n) { + return arguments.length ? (p = n * n, a) : Math.sqrt(p) + }, a.gravity = function(n) { + return arguments.length ? (v = +n, a) : v + }, a.theta = function(n) { + return arguments.length ? (d = n * n, a) : Math.sqrt(d) + }, a.alpha = function(n) { + return arguments.length ? (n = +n, r ? r = n > 0 ? n : 0 : n > 0 && (c.start({ + type: "start", + alpha: r = n + }), ta.timer(a.tick)), a) : r + }, a.start = function() { + function n(n, r) { + if (!e) { + for (e = new Array(c), a = 0; c > a; ++a) e[a] = []; + for (a = 0; s > a; ++a) { + var u = M[a]; + e[u.source.index].push(u.target), e[u.target.index].push(u.source) + } + } + for (var i, o = e[t], a = -1, l = o.length; ++a < l;) + if (!isNaN(i = o[a][n])) return i; + return Math.random() * r + } + var t, e, r, c = m.length, + s = M.length, + p = l[0], + v = l[1]; + for (t = 0; c > t; ++t)(r = m[t]).index = t, r.weight = 0; + for (t = 0; s > t; ++t) r = M[t], "number" == typeof r.source && (r.source = m[r.source]), "number" == typeof r.target && (r.target = m[r.target]), ++r.source.weight, ++r.target.weight; + for (t = 0; c > t; ++t) r = m[t], isNaN(r.x) && (r.x = n("x", p)), isNaN(r.y) && (r.y = n("y", v)), isNaN(r.px) && (r.px = r.x), isNaN(r.py) && (r.py = r.y); + if (u = [], "function" == typeof f) + for (t = 0; s > t; ++t) u[t] = +f.call(this, M[t], t); + else + for (t = 0; s > t; ++t) u[t] = f; + if (i = [], "function" == typeof h) + for (t = 0; s > t; ++t) i[t] = +h.call(this, M[t], t); + else + for (t = 0; s > t; ++t) i[t] = h; + if (o = [], "function" == typeof g) + for (t = 0; c > t; ++t) o[t] = +g.call(this, m[t], t); + else + for (t = 0; c > t; ++t) o[t] = g; + return a.resume() + }, a.resume = function() { + return a.alpha(.1) + }, a.stop = function() { + return a.alpha(0) + }, a.drag = function() { + return e || (e = ta.behavior.drag().origin(y).on("dragstart.force", Xu).on("drag.force", t).on("dragend.force", $u)), arguments.length ? void this.on("mouseover.force", Bu).on("mouseout.force", Wu).call(e) : e + }, ta.rebind(a, c, "on") + }; + var fl = 20, + hl = 1, + gl = 1 / 0; + ta.layout.hierarchy = function() { + function n(u) { + var i, o = [u], + a = []; + for (u.depth = 0; null != (i = o.pop());) + if (a.push(i), (l = e.call(n, i, i.depth)) && (c = l.length)) { + for (var c, l, s; --c >= 0;) o.push(s = l[c]), s.parent = i, s.depth = i.depth + 1; + r && (i.value = 0), i.children = l + } else r && (i.value = +r.call(n, i, i.depth) || 0), delete i.children; + return Qu(u, function(n) { + var e, u; + t && (e = n.children) && e.sort(t), r && (u = n.parent) && (u.value += n.value) + }), a + } + var t = ei, + e = ni, + r = ti; + return n.sort = function(e) { + return arguments.length ? (t = e, n) : t + }, n.children = function(t) { + return arguments.length ? (e = t, n) : e + }, n.value = function(t) { + return arguments.length ? (r = t, n) : r + }, n.revalue = function(t) { + return r && (Ku(t, function(n) { + n.children && (n.value = 0) + }), Qu(t, function(t) { + var e; + t.children || (t.value = +r.call(n, t, t.depth) || 0), (e = t.parent) && (e.value += t.value) + })), t + }, n + }, ta.layout.partition = function() { + function n(t, e, r, u) { + var i = t.children; + if (t.x = e, t.y = t.depth * u, t.dx = r, t.dy = u, i && (o = i.length)) { + var o, a, c, l = -1; + for (r = t.value ? r / t.value : 0; ++l < o;) n(a = i[l], e, c = a.value * r, u), e += c + } + } + + function t(n) { + var e = n.children, + r = 0; + if (e && (u = e.length)) + for (var u, i = -1; ++i < u;) r = Math.max(r, t(e[i])); + return 1 + r + } + + function e(e, i) { + var o = r.call(this, e, i); + return n(o[0], 0, u[0], u[1] / t(o[0])), o + } + var r = ta.layout.hierarchy(), + u = [1, 1]; + return e.size = function(n) { + return arguments.length ? (u = n, e) : u + }, Gu(e, r) + }, ta.layout.pie = function() { + function n(o) { + var a, c = o.length, + l = o.map(function(e, r) { + return +t.call(n, e, r) + }), + s = +("function" == typeof r ? r.apply(this, arguments) : r), + f = ("function" == typeof u ? u.apply(this, arguments) : u) - s, + h = Math.min(Math.abs(f) / c, +("function" == typeof i ? i.apply(this, arguments) : i)), + g = h * (0 > f ? -1 : 1), + p = (f - c * g) / ta.sum(l), + v = ta.range(c), + d = []; + return null != e && v.sort(e === pl ? function(n, t) { + return l[t] - l[n] + } : function(n, t) { + return e(o[n], o[t]) + }), v.forEach(function(n) { + d[n] = { + data: o[n], + value: a = l[n], + startAngle: s, + endAngle: s += a * p + g, + padAngle: h + } + }), d + } + var t = Number, + e = pl, + r = 0, + u = La, + i = 0; + return n.value = function(e) { + return arguments.length ? (t = e, n) : t + }, n.sort = function(t) { + return arguments.length ? (e = t, n) : e + }, n.startAngle = function(t) { + return arguments.length ? (r = t, n) : r + }, n.endAngle = function(t) { + return arguments.length ? (u = t, n) : u + }, n.padAngle = function(t) { + return arguments.length ? (i = t, n) : i + }, n + }; + var pl = {}; + ta.layout.stack = function() { + function n(a, c) { + if (!(h = a.length)) return a; + var l = a.map(function(e, r) { + return t.call(n, e, r) + }), + s = l.map(function(t) { + return t.map(function(t, e) { + return [i.call(n, t, e), o.call(n, t, e)] + }) + }), + f = e.call(n, s, c); + l = ta.permute(l, f), s = ta.permute(s, f); + var h, g, p, v, d = r.call(n, s, c), + m = l[0].length; + for (p = 0; m > p; ++p) + for (u.call(n, l[0][p], v = d[p], s[0][p][1]), g = 1; h > g; ++g) u.call(n, l[g][p], v += s[g - 1][p][1], s[g][p][1]); + return a + } + var t = y, + e = ai, + r = ci, + u = oi, + i = ui, + o = ii; + return n.values = function(e) { + return arguments.length ? (t = e, n) : t + }, n.order = function(t) { + return arguments.length ? (e = "function" == typeof t ? t : vl.get(t) || ai, n) : e + }, n.offset = function(t) { + return arguments.length ? (r = "function" == typeof t ? t : dl.get(t) || ci, n) : r + }, n.x = function(t) { + return arguments.length ? (i = t, n) : i + }, n.y = function(t) { + return arguments.length ? (o = t, n) : o + }, n.out = function(t) { + return arguments.length ? (u = t, n) : u + }, n + }; + var vl = ta.map({ + "inside-out": function(n) { + var t, e, r = n.length, + u = n.map(li), + i = n.map(si), + o = ta.range(r).sort(function(n, t) { + return u[n] - u[t] + }), + a = 0, + c = 0, + l = [], + s = []; + for (t = 0; r > t; ++t) e = o[t], c > a ? (a += i[e], l.push(e)) : (c += i[e], s.push(e)); + return s.reverse().concat(l) + }, + reverse: function(n) { + return ta.range(n.length).reverse() + }, + "default": ai + }), + dl = ta.map({ + silhouette: function(n) { + var t, e, r, u = n.length, + i = n[0].length, + o = [], + a = 0, + c = []; + for (e = 0; i > e; ++e) { + for (t = 0, r = 0; u > t; t++) r += n[t][e][1]; + r > a && (a = r), o.push(r) + } + for (e = 0; i > e; ++e) c[e] = (a - o[e]) / 2; + return c + }, + wiggle: function(n) { + var t, e, r, u, i, o, a, c, l, s = n.length, + f = n[0], + h = f.length, + g = []; + for (g[0] = c = l = 0, e = 1; h > e; ++e) { + for (t = 0, u = 0; s > t; ++t) u += n[t][e][1]; + for (t = 0, i = 0, a = f[e][0] - f[e - 1][0]; s > t; ++t) { + for (r = 0, o = (n[t][e][1] - n[t][e - 1][1]) / (2 * a); t > r; ++r) o += (n[r][e][1] - n[r][e - 1][1]) / a; + i += o * n[t][e][1] + } + g[e] = c -= u ? i / u * a : 0, l > c && (l = c) + } + for (e = 0; h > e; ++e) g[e] -= l; + return g + }, + expand: function(n) { + var t, e, r, u = n.length, + i = n[0].length, + o = 1 / u, + a = []; + for (e = 0; i > e; ++e) { + for (t = 0, r = 0; u > t; t++) r += n[t][e][1]; + if (r) + for (t = 0; u > t; t++) n[t][e][1] /= r; + else + for (t = 0; u > t; t++) n[t][e][1] = o + } + for (e = 0; i > e; ++e) a[e] = 0; + return a + }, + zero: ci + }); + ta.layout.histogram = function() { + function n(n, i) { + for (var o, a, c = [], l = n.map(e, this), s = r.call(this, l, i), f = u.call(this, s, l, i), i = -1, h = l.length, g = f.length - 1, p = t ? 1 : 1 / h; ++i < g;) o = c[i] = [], o.dx = f[i + 1] - (o.x = f[i]), o.y = 0; + if (g > 0) + for (i = -1; ++i < h;) a = l[i], a >= s[0] && a <= s[1] && (o = c[ta.bisect(f, a, 1, g) - 1], o.y += p, o.push(n[i])); + return c + } + var t = !0, + e = Number, + r = pi, + u = hi; + return n.value = function(t) { + return arguments.length ? (e = t, n) : e + }, n.range = function(t) { + return arguments.length ? (r = Et(t), n) : r + }, n.bins = function(t) { + return arguments.length ? (u = "number" == typeof t ? function(n) { + return gi(n, t) + } : Et(t), n) : u + }, n.frequency = function(e) { + return arguments.length ? (t = !!e, n) : t + }, n + }, ta.layout.pack = function() { + function n(n, i) { + var o = e.call(this, n, i), + a = o[0], + c = u[0], + l = u[1], + s = null == t ? Math.sqrt : "function" == typeof t ? t : function() { + return t + }; + if (a.x = a.y = 0, Qu(a, function(n) { + n.r = +s(n.value) + }), Qu(a, Mi), r) { + var f = r * (t ? 1 : Math.max(2 * a.r / c, 2 * a.r / l)) / 2; + Qu(a, function(n) { + n.r += f + }), Qu(a, Mi), Qu(a, function(n) { + n.r -= f + }) + } + return _i(a, c / 2, l / 2, t ? 1 : 1 / Math.max(2 * a.r / c, 2 * a.r / l)), o + } + var t, e = ta.layout.hierarchy().sort(vi), + r = 0, + u = [1, 1]; + return n.size = function(t) { + return arguments.length ? (u = t, n) : u + }, n.radius = function(e) { + return arguments.length ? (t = null == e || "function" == typeof e ? e : +e, n) : t + }, n.padding = function(t) { + return arguments.length ? (r = +t, n) : r + }, Gu(n, e) + }, ta.layout.tree = function() { + function n(n, u) { + var s = o.call(this, n, u), + f = s[0], + h = t(f); + if (Qu(h, e), h.parent.m = -h.z, Ku(h, r), l) Ku(f, i); + else { + var g = f, + p = f, + v = f; + Ku(f, function(n) { + n.x < g.x && (g = n), n.x > p.x && (p = n), n.depth > v.depth && (v = n) + }); + var d = a(g, p) / 2 - g.x, + m = c[0] / (p.x + a(p, g) / 2 + d), + y = c[1] / (v.depth || 1); + Ku(f, function(n) { + n.x = (n.x + d) * m, n.y = n.depth * y + }) + } + return s + } + + function t(n) { + for (var t, e = { + A: null, + children: [n] + }, r = [e]; null != (t = r.pop());) + for (var u, i = t.children, o = 0, a = i.length; a > o; ++o) r.push((i[o] = u = { + _: i[o], + parent: t, + children: (u = i[o].children) && u.slice() || [], + A: null, + a: null, + z: 0, + m: 0, + c: 0, + s: 0, + t: null, + i: o + }).a = u); + return e.children[0] + } + + function e(n) { + var t = n.children, + e = n.parent.children, + r = n.i ? e[n.i - 1] : null; + if (t.length) { + Ni(n); + var i = (t[0].z + t[t.length - 1].z) / 2; + r ? (n.z = r.z + a(n._, r._), n.m = n.z - i) : n.z = i + } else r && (n.z = r.z + a(n._, r._)); + n.parent.A = u(n, r, n.parent.A || e[0]) + } + + function r(n) { + n._.x = n.z + n.parent.m, n.m += n.parent.m + } + + function u(n, t, e) { + if (t) { + for (var r, u = n, i = n, o = t, c = u.parent.children[0], l = u.m, s = i.m, f = o.m, h = c.m; o = Ei(o), u = ki(u), o && u;) c = ki(c), i = Ei(i), i.a = n, r = o.z + f - u.z - l + a(o._, u._), r > 0 && (Ai(Ci(o, n, e), n, r), l += r, s += r), f += o.m, l += u.m, h += c.m, s += i.m; + o && !Ei(i) && (i.t = o, i.m += f - s), u && !ki(c) && (c.t = u, c.m += l - h, e = n) + } + return e + } + + function i(n) { + n.x *= c[0], n.y = n.depth * c[1] + } + var o = ta.layout.hierarchy().sort(null).value(null), + a = Si, + c = [1, 1], + l = null; + return n.separation = function(t) { + return arguments.length ? (a = t, n) : a + }, n.size = function(t) { + return arguments.length ? (l = null == (c = t) ? i : null, n) : l ? null : c + }, n.nodeSize = function(t) { + return arguments.length ? (l = null == (c = t) ? null : i, n) : l ? c : null + }, Gu(n, o) + }, ta.layout.cluster = function() { + function n(n, i) { + var o, a = t.call(this, n, i), + c = a[0], + l = 0; + Qu(c, function(n) { + var t = n.children; + t && t.length ? (n.x = qi(t), n.y = zi(t)) : (n.x = o ? l += e(n, o) : 0, n.y = 0, o = n) + }); + var s = Li(c), + f = Ti(c), + h = s.x - e(s, f) / 2, + g = f.x + e(f, s) / 2; + return Qu(c, u ? function(n) { + n.x = (n.x - c.x) * r[0], n.y = (c.y - n.y) * r[1] + } : function(n) { + n.x = (n.x - h) / (g - h) * r[0], n.y = (1 - (c.y ? n.y / c.y : 1)) * r[1] + }), a + } + var t = ta.layout.hierarchy().sort(null).value(null), + e = Si, + r = [1, 1], + u = !1; + return n.separation = function(t) { + return arguments.length ? (e = t, n) : e + }, n.size = function(t) { + return arguments.length ? (u = null == (r = t), n) : u ? null : r + }, n.nodeSize = function(t) { + return arguments.length ? (u = null != (r = t), n) : u ? r : null + }, Gu(n, t) + }, ta.layout.treemap = function() { + function n(n, t) { + for (var e, r, u = -1, i = n.length; ++u < i;) r = (e = n[u]).value * (0 > t ? 0 : t), e.area = isNaN(r) || 0 >= r ? 0 : r + } + + function t(e) { + var i = e.children; + if (i && i.length) { + var o, a, c, l = f(e), + s = [], + h = i.slice(), + p = 1 / 0, + v = "slice" === g ? l.dx : "dice" === g ? l.dy : "slice-dice" === g ? 1 & e.depth ? l.dy : l.dx : Math.min(l.dx, l.dy); + for (n(h, l.dx * l.dy / e.value), s.area = 0; + (c = h.length) > 0;) s.push(o = h[c - 1]), s.area += o.area, "squarify" !== g || (a = r(s, v)) <= p ? (h.pop(), p = a) : (s.area -= s.pop().area, u(s, v, l, !1), v = Math.min(l.dx, l.dy), s.length = s.area = 0, p = 1 / 0); + s.length && (u(s, v, l, !0), s.length = s.area = 0), i.forEach(t) + } + } + + function e(t) { + var r = t.children; + if (r && r.length) { + var i, o = f(t), + a = r.slice(), + c = []; + for (n(a, o.dx * o.dy / t.value), c.area = 0; i = a.pop();) c.push(i), c.area += i.area, null != i.z && (u(c, i.z ? o.dx : o.dy, o, !a.length), c.length = c.area = 0); + r.forEach(e) + } + } + + function r(n, t) { + for (var e, r = n.area, u = 0, i = 1 / 0, o = -1, a = n.length; ++o < a;)(e = n[o].area) && (i > e && (i = e), e > u && (u = e)); + return r *= r, t *= t, r ? Math.max(t * u * p / r, r / (t * i * p)) : 1 / 0 + } + + function u(n, t, e, r) { + var u, i = -1, + o = n.length, + a = e.x, + l = e.y, + s = t ? c(n.area / t) : 0; + if (t == e.dx) { + for ((r || s > e.dy) && (s = e.dy); ++i < o;) u = n[i], u.x = a, u.y = l, u.dy = s, a += u.dx = Math.min(e.x + e.dx - a, s ? c(u.area / s) : 0); + u.z = !0, u.dx += e.x + e.dx - a, e.y += s, e.dy -= s + } else { + for ((r || s > e.dx) && (s = e.dx); ++i < o;) u = n[i], u.x = a, u.y = l, u.dx = s, l += u.dy = Math.min(e.y + e.dy - l, s ? c(u.area / s) : 0); + u.z = !1, u.dy += e.y + e.dy - l, e.x += s, e.dx -= s + } + } + + function i(r) { + var u = o || a(r), + i = u[0]; + return i.x = 0, i.y = 0, i.dx = l[0], i.dy = l[1], o && a.revalue(i), n([i], i.dx * i.dy / i.value), (o ? e : t)(i), h && (o = u), u + } + var o, a = ta.layout.hierarchy(), + c = Math.round, + l = [1, 1], + s = null, + f = Ri, + h = !1, + g = "squarify", + p = .5 * (1 + Math.sqrt(5)); + return i.size = function(n) { + return arguments.length ? (l = n, i) : l + }, i.padding = function(n) { + function t(t) { + var e = n.call(i, t, t.depth); + return null == e ? Ri(t) : Di(t, "number" == typeof e ? [e, e, e, e] : e) + } + + function e(t) { + return Di(t, n) + } + if (!arguments.length) return s; + var r; + return f = null == (s = n) ? Ri : "function" == (r = typeof n) ? t : "number" === r ? (n = [n, n, n, n], e) : e, i + }, i.round = function(n) { + return arguments.length ? (c = n ? Math.round : Number, i) : c != Number + }, i.sticky = function(n) { + return arguments.length ? (h = n, o = null, i) : h + }, i.ratio = function(n) { + return arguments.length ? (p = n, i) : p + }, i.mode = function(n) { + return arguments.length ? (g = n + "", i) : g + }, Gu(i, a) + }, ta.random = { + normal: function(n, t) { + var e = arguments.length; + return 2 > e && (t = 1), 1 > e && (n = 0), + function() { + var e, r, u; + do e = 2 * Math.random() - 1, r = 2 * Math.random() - 1, u = e * e + r * r; while (!u || u > 1); + return n + t * e * Math.sqrt(-2 * Math.log(u) / u) + } + }, + logNormal: function() { + var n = ta.random.normal.apply(ta, arguments); + return function() { + return Math.exp(n()) + } + }, + bates: function(n) { + var t = ta.random.irwinHall(n); + return function() { + return t() / n + } + }, + irwinHall: function(n) { + return function() { + for (var t = 0, e = 0; n > e; e++) t += Math.random(); + return t + } + } + }, ta.scale = {}; + var ml = { + floor: y, + ceil: y + }; + ta.scale.linear = function() { + return Ii([0, 1], [0, 1], mu, !1) + }; + var yl = { + s: 1, + g: 1, + p: 1, + r: 1, + e: 1 + }; + ta.scale.log = function() { + return Ji(ta.scale.linear().domain([0, 1]), 10, !0, [1, 10]) + }; + var Ml = ta.format(".0e"), + xl = { + floor: function(n) { + return -Math.ceil(-n) + }, + ceil: function(n) { + return -Math.floor(-n) + } + }; + ta.scale.pow = function() { + return Gi(ta.scale.linear(), 1, [0, 1]) + }, ta.scale.sqrt = function() { + return ta.scale.pow().exponent(.5) + }, ta.scale.ordinal = function() { + return Qi([], { + t: "range", + a: [ + [] + ] + }) + }, ta.scale.category10 = function() { + return ta.scale.ordinal().range(bl) + }, ta.scale.category20 = function() { + return ta.scale.ordinal().range(_l) + }, ta.scale.category20b = function() { + return ta.scale.ordinal().range(wl) + }, ta.scale.category20c = function() { + return ta.scale.ordinal().range(Sl) + }; + var bl = [2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175].map(Mt), + _l = [2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725].map(Mt), + wl = [3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654].map(Mt), + Sl = [3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081].map(Mt); + ta.scale.quantile = function() { + return no([], []) + }, ta.scale.quantize = function() { + return to(0, 1, [0, 1]) + }, ta.scale.threshold = function() { + return eo([.5], [0, 1]) + }, ta.scale.identity = function() { + return ro([0, 1]) + }, ta.svg = {}, ta.svg.arc = function() { + function n() { + var n = Math.max(0, +e.apply(this, arguments)), + l = Math.max(0, +r.apply(this, arguments)), + s = o.apply(this, arguments) - Ra, + f = a.apply(this, arguments) - Ra, + h = Math.abs(f - s), + g = s > f ? 0 : 1; + if (n > l && (p = l, l = n, n = p), h >= Ta) return t(l, g) + (n ? t(n, 1 - g) : "") + "Z"; + var p, v, d, m, y, M, x, b, _, w, S, k, E = 0, + A = 0, + N = []; + if ((m = (+c.apply(this, arguments) || 0) / 2) && (d = i === kl ? Math.sqrt(n * n + l * l) : +i.apply(this, arguments), g || (A *= -1), l && (A = tt(d / l * Math.sin(m))), n && (E = tt(d / n * Math.sin(m)))), l) { + y = l * Math.cos(s + A), M = l * Math.sin(s + A), x = l * Math.cos(f - A), b = l * Math.sin(f - A); + var C = Math.abs(f - s - 2 * A) <= qa ? 0 : 1; + if (A && so(y, M, x, b) === g ^ C) { + var z = (s + f) / 2; + y = l * Math.cos(z), M = l * Math.sin(z), x = b = null + } + } else y = M = 0; + if (n) { + _ = n * Math.cos(f - E), w = n * Math.sin(f - E), S = n * Math.cos(s + E), k = n * Math.sin(s + E); + var q = Math.abs(s - f + 2 * E) <= qa ? 0 : 1; + if (E && so(_, w, S, k) === 1 - g ^ q) { + var L = (s + f) / 2; + _ = n * Math.cos(L), w = n * Math.sin(L), S = k = null + } + } else _ = w = 0; + if ((p = Math.min(Math.abs(l - n) / 2, +u.apply(this, arguments))) > .001) { + v = l > n ^ g ? 0 : 1; + var T = null == S ? [_, w] : null == x ? [y, M] : Lr([y, M], [S, k], [x, b], [_, w]), + R = y - T[0], + D = M - T[1], + P = x - T[0], + U = b - T[1], + j = 1 / Math.sin(Math.acos((R * P + D * U) / (Math.sqrt(R * R + D * D) * Math.sqrt(P * P + U * U))) / 2), + F = Math.sqrt(T[0] * T[0] + T[1] * T[1]); + if (null != x) { + var H = Math.min(p, (l - F) / (j + 1)), + O = fo(null == S ? [_, w] : [S, k], [y, M], l, H, g), + I = fo([x, b], [_, w], l, H, g); + p === H ? N.push("M", O[0], "A", H, ",", H, " 0 0,", v, " ", O[1], "A", l, ",", l, " 0 ", 1 - g ^ so(O[1][0], O[1][1], I[1][0], I[1][1]), ",", g, " ", I[1], "A", H, ",", H, " 0 0,", v, " ", I[0]) : N.push("M", O[0], "A", H, ",", H, " 0 1,", v, " ", I[0]) + } else N.push("M", y, ",", M); + if (null != S) { + var Y = Math.min(p, (n - F) / (j - 1)), + Z = fo([y, M], [S, k], n, -Y, g), + V = fo([_, w], null == x ? [y, M] : [x, b], n, -Y, g); + p === Y ? N.push("L", V[0], "A", Y, ",", Y, " 0 0,", v, " ", V[1], "A", n, ",", n, " 0 ", g ^ so(V[1][0], V[1][1], Z[1][0], Z[1][1]), ",", 1 - g, " ", Z[1], "A", Y, ",", Y, " 0 0,", v, " ", Z[0]) : N.push("L", V[0], "A", Y, ",", Y, " 0 0,", v, " ", Z[0]) + } else N.push("L", _, ",", w) + } else N.push("M", y, ",", M), null != x && N.push("A", l, ",", l, " 0 ", C, ",", g, " ", x, ",", b), N.push("L", _, ",", w), null != S && N.push("A", n, ",", n, " 0 ", q, ",", 1 - g, " ", S, ",", k); + return N.push("Z"), N.join("") + } + + function t(n, t) { + return "M0," + n + "A" + n + "," + n + " 0 1," + t + " 0," + -n + "A" + n + "," + n + " 0 1," + t + " 0," + n + } + var e = io, + r = oo, + u = uo, + i = kl, + o = ao, + a = co, + c = lo; + return n.innerRadius = function(t) { + return arguments.length ? (e = Et(t), n) : e + }, n.outerRadius = function(t) { + return arguments.length ? (r = Et(t), n) : r + }, n.cornerRadius = function(t) { + return arguments.length ? (u = Et(t), n) : u + }, n.padRadius = function(t) { + return arguments.length ? (i = t == kl ? kl : Et(t), n) : i + }, n.startAngle = function(t) { + return arguments.length ? (o = Et(t), n) : o + }, n.endAngle = function(t) { + return arguments.length ? (a = Et(t), n) : a + }, n.padAngle = function(t) { + return arguments.length ? (c = Et(t), n) : c + }, n.centroid = function() { + var n = (+e.apply(this, arguments) + +r.apply(this, arguments)) / 2, + t = (+o.apply(this, arguments) + +a.apply(this, arguments)) / 2 - Ra; + return [Math.cos(t) * n, Math.sin(t) * n] + }, n + }; + var kl = "auto"; + ta.svg.line = function() { + return ho(y) + }; + var El = ta.map({ + linear: go, + "linear-closed": po, + step: vo, + "step-before": mo, + "step-after": yo, + basis: So, + "basis-open": ko, + "basis-closed": Eo, + bundle: Ao, + cardinal: bo, + "cardinal-open": Mo, + "cardinal-closed": xo, + monotone: To + }); + El.forEach(function(n, t) { + t.key = n, t.closed = /-closed$/.test(n) + }); + var Al = [0, 2 / 3, 1 / 3, 0], + Nl = [0, 1 / 3, 2 / 3, 0], + Cl = [0, 1 / 6, 2 / 3, 1 / 6]; + ta.svg.line.radial = function() { + var n = ho(Ro); + return n.radius = n.x, delete n.x, n.angle = n.y, delete n.y, n + }, mo.reverse = yo, yo.reverse = mo, ta.svg.area = function() { + return Do(y) + }, ta.svg.area.radial = function() { + var n = Do(Ro); + return n.radius = n.x, delete n.x, n.innerRadius = n.x0, delete n.x0, n.outerRadius = n.x1, delete n.x1, n.angle = n.y, delete n.y, n.startAngle = n.y0, delete n.y0, n.endAngle = n.y1, delete n.y1, n + }, ta.svg.chord = function() { + function n(n, a) { + var c = t(this, i, n, a), + l = t(this, o, n, a); + return "M" + c.p0 + r(c.r, c.p1, c.a1 - c.a0) + (e(c, l) ? u(c.r, c.p1, c.r, c.p0) : u(c.r, c.p1, l.r, l.p0) + r(l.r, l.p1, l.a1 - l.a0) + u(l.r, l.p1, c.r, c.p0)) + "Z" + } + + function t(n, t, e, r) { + var u = t.call(n, e, r), + i = a.call(n, u, r), + o = c.call(n, u, r) - Ra, + s = l.call(n, u, r) - Ra; + return { + r: i, + a0: o, + a1: s, + p0: [i * Math.cos(o), i * Math.sin(o)], + p1: [i * Math.cos(s), i * Math.sin(s)] + } + } + + function e(n, t) { + return n.a0 == t.a0 && n.a1 == t.a1 + } + + function r(n, t, e) { + return "A" + n + "," + n + " 0 " + +(e > qa) + ",1 " + t + } + + function u(n, t, e, r) { + return "Q 0,0 " + r + } + var i = mr, + o = yr, + a = Po, + c = ao, + l = co; + return n.radius = function(t) { + return arguments.length ? (a = Et(t), n) : a + }, n.source = function(t) { + return arguments.length ? (i = Et(t), n) : i + }, n.target = function(t) { + return arguments.length ? (o = Et(t), n) : o + }, n.startAngle = function(t) { + return arguments.length ? (c = Et(t), n) : c + }, n.endAngle = function(t) { + return arguments.length ? (l = Et(t), n) : l + }, n + }, ta.svg.diagonal = function() { + function n(n, u) { + var i = t.call(this, n, u), + o = e.call(this, n, u), + a = (i.y + o.y) / 2, + c = [i, { + x: i.x, + y: a + }, { + x: o.x, + y: a + }, o]; + return c = c.map(r), "M" + c[0] + "C" + c[1] + " " + c[2] + " " + c[3] + } + var t = mr, + e = yr, + r = Uo; + return n.source = function(e) { + return arguments.length ? (t = Et(e), n) : t + }, n.target = function(t) { + return arguments.length ? (e = Et(t), n) : e + }, n.projection = function(t) { + return arguments.length ? (r = t, n) : r + }, n + }, ta.svg.diagonal.radial = function() { + var n = ta.svg.diagonal(), + t = Uo, + e = n.projection; + return n.projection = function(n) { + return arguments.length ? e(jo(t = n)) : t + }, n + }, ta.svg.symbol = function() { + function n(n, r) { + return (zl.get(t.call(this, n, r)) || Oo)(e.call(this, n, r)) + } + var t = Ho, + e = Fo; + return n.type = function(e) { + return arguments.length ? (t = Et(e), n) : t + }, n.size = function(t) { + return arguments.length ? (e = Et(t), n) : e + }, n + }; + var zl = ta.map({ + circle: Oo, + cross: function(n) { + var t = Math.sqrt(n / 5) / 2; + return "M" + -3 * t + "," + -t + "H" + -t + "V" + -3 * t + "H" + t + "V" + -t + "H" + 3 * t + "V" + t + "H" + t + "V" + 3 * t + "H" + -t + "V" + t + "H" + -3 * t + "Z" + }, + diamond: function(n) { + var t = Math.sqrt(n / (2 * Ll)), + e = t * Ll; + return "M0," + -t + "L" + e + ",0 0," + t + " " + -e + ",0Z" + }, + square: function(n) { + var t = Math.sqrt(n) / 2; + return "M" + -t + "," + -t + "L" + t + "," + -t + " " + t + "," + t + " " + -t + "," + t + "Z" + }, + "triangle-down": function(n) { + var t = Math.sqrt(n / ql), + e = t * ql / 2; + return "M0," + e + "L" + t + "," + -e + " " + -t + "," + -e + "Z" + }, + "triangle-up": function(n) { + var t = Math.sqrt(n / ql), + e = t * ql / 2; + return "M0," + -e + "L" + t + "," + e + " " + -t + "," + e + "Z" + } + }); + ta.svg.symbolTypes = zl.keys(); + var ql = Math.sqrt(3), + Ll = Math.tan(30 * Da); + _a.transition = function(n) { + for (var t, e, r = Tl || ++Ul, u = Xo(n), i = [], o = Rl || { + time: Date.now(), + ease: Su, + delay: 0, + duration: 250 + }, a = -1, c = this.length; ++a < c;) { + i.push(t = []); + for (var l = this[a], s = -1, f = l.length; ++s < f;)(e = l[s]) && $o(e, s, u, r, o), t.push(e) + } + return Yo(i, u, r) + }, _a.interrupt = function(n) { + return this.each(null == n ? Dl : Io(Xo(n))) + }; + var Tl, Rl, Dl = Io(Xo()), + Pl = [], + Ul = 0; + Pl.call = _a.call, Pl.empty = _a.empty, Pl.node = _a.node, Pl.size = _a.size, ta.transition = function(n, t) { + return n && n.transition ? Tl ? n.transition(t) : n : ta.selection().transition(n) + }, ta.transition.prototype = Pl, Pl.select = function(n) { + var t, e, r, u = this.id, + i = this.namespace, + o = []; + n = N(n); + for (var a = -1, c = this.length; ++a < c;) { + o.push(t = []); + for (var l = this[a], s = -1, f = l.length; ++s < f;)(r = l[s]) && (e = n.call(r, r.__data__, s, a)) ? ("__data__" in r && (e.__data__ = r.__data__), $o(e, s, i, u, r[i][u]), t.push(e)) : t.push(null) + } + return Yo(o, i, u) + }, Pl.selectAll = function(n) { + var t, e, r, u, i, o = this.id, + a = this.namespace, + c = []; + n = C(n); + for (var l = -1, s = this.length; ++l < s;) + for (var f = this[l], h = -1, g = f.length; ++h < g;) + if (r = f[h]) { + i = r[a][o], e = n.call(r, r.__data__, h, l), c.push(t = []); + for (var p = -1, v = e.length; ++p < v;)(u = e[p]) && $o(u, p, a, o, i), t.push(u) + } return Yo(c, a, o) + }, Pl.filter = function(n) { + var t, e, r, u = []; + "function" != typeof n && (n = O(n)); + for (var i = 0, o = this.length; o > i; i++) { + u.push(t = []); + for (var e = this[i], a = 0, c = e.length; c > a; a++)(r = e[a]) && n.call(r, r.__data__, a, i) && t.push(r) + } + return Yo(u, this.namespace, this.id) + }, Pl.tween = function(n, t) { + var e = this.id, + r = this.namespace; + return arguments.length < 2 ? this.node()[r][e].tween.get(n) : Y(this, null == t ? function(t) { + t[r][e].tween.remove(n) + } : function(u) { + u[r][e].tween.set(n, t) + }) + }, Pl.attr = function(n, t) { + function e() { + this.removeAttribute(a) + } + + function r() { + this.removeAttributeNS(a.space, a.local) + } + + function u(n) { + return null == n ? e : (n += "", function() { + var t, e = this.getAttribute(a); + return e !== n && (t = o(e, n), function(n) { + this.setAttribute(a, t(n)) + }) + }) + } + + function i(n) { + return null == n ? r : (n += "", function() { + var t, e = this.getAttributeNS(a.space, a.local); + return e !== n && (t = o(e, n), function(n) { + this.setAttributeNS(a.space, a.local, t(n)) + }) + }) + } + if (arguments.length < 2) { + for (t in n) this.attr(t, n[t]); + return this + } + var o = "transform" == n ? Hu : mu, + a = ta.ns.qualify(n); + return Zo(this, "attr." + n, t, a.local ? i : u) + }, Pl.attrTween = function(n, t) { + function e(n, e) { + var r = t.call(this, n, e, this.getAttribute(u)); + return r && function(n) { + this.setAttribute(u, r(n)) + } + } + + function r(n, e) { + var r = t.call(this, n, e, this.getAttributeNS(u.space, u.local)); + return r && function(n) { + this.setAttributeNS(u.space, u.local, r(n)) + } + } + var u = ta.ns.qualify(n); + return this.tween("attr." + n, u.local ? r : e) + }, Pl.style = function(n, e, r) { + function u() { + this.style.removeProperty(n) + } + + function i(e) { + return null == e ? u : (e += "", function() { + var u, i = t(this).getComputedStyle(this, null).getPropertyValue(n); + return i !== e && (u = mu(i, e), function(t) { + this.style.setProperty(n, u(t), r) + }) + }) + } + var o = arguments.length; + if (3 > o) { + if ("string" != typeof n) { + 2 > o && (e = ""); + for (r in n) this.style(r, n[r], e); + return this + } + r = "" + } + return Zo(this, "style." + n, e, i) + }, Pl.styleTween = function(n, e, r) { + function u(u, i) { + var o = e.call(this, u, i, t(this).getComputedStyle(this, null).getPropertyValue(n)); + return o && function(t) { + this.style.setProperty(n, o(t), r) + } + } + return arguments.length < 3 && (r = ""), this.tween("style." + n, u) + }, Pl.text = function(n) { + return Zo(this, "text", n, Vo) + }, Pl.remove = function() { + var n = this.namespace; + return this.each("end.transition", function() { + var t; + this[n].count < 2 && (t = this.parentNode) && t.removeChild(this) + }) + }, Pl.ease = function(n) { + var t = this.id, + e = this.namespace; + return arguments.length < 1 ? this.node()[e][t].ease : ("function" != typeof n && (n = ta.ease.apply(ta, arguments)), Y(this, function(r) { + r[e][t].ease = n + })) + }, Pl.delay = function(n) { + var t = this.id, + e = this.namespace; + return arguments.length < 1 ? this.node()[e][t].delay : Y(this, "function" == typeof n ? function(r, u, i) { + r[e][t].delay = +n.call(r, r.__data__, u, i) + } : (n = +n, function(r) { + r[e][t].delay = n + })) + }, Pl.duration = function(n) { + var t = this.id, + e = this.namespace; + return arguments.length < 1 ? this.node()[e][t].duration : Y(this, "function" == typeof n ? function(r, u, i) { + r[e][t].duration = Math.max(1, n.call(r, r.__data__, u, i)) + } : (n = Math.max(1, n), function(r) { + r[e][t].duration = n + })) + }, Pl.each = function(n, t) { + var e = this.id, + r = this.namespace; + if (arguments.length < 2) { + var u = Rl, + i = Tl; + try { + Tl = e, Y(this, function(t, u, i) { + Rl = t[r][e], n.call(t, t.__data__, u, i) + }) + } finally { + Rl = u, Tl = i + } + } else Y(this, function(u) { + var i = u[r][e]; + (i.event || (i.event = ta.dispatch("start", "end", "interrupt"))).on(n, t) + }); + return this + }, Pl.transition = function() { + for (var n, t, e, r, u = this.id, i = ++Ul, o = this.namespace, a = [], c = 0, l = this.length; l > c; c++) { + a.push(n = []); + for (var t = this[c], s = 0, f = t.length; f > s; s++)(e = t[s]) && (r = e[o][u], $o(e, s, o, i, { + time: r.time, + ease: r.ease, + delay: r.delay + r.duration, + duration: r.duration + })), n.push(e) + } + return Yo(a, o, i) + }, ta.svg.axis = function() { + function n(n) { + n.each(function() { + var n, l = ta.select(this), + s = this.__chart__ || e, + f = this.__chart__ = e.copy(), + h = null == c ? f.ticks ? f.ticks.apply(f, a) : f.domain() : c, + g = null == t ? f.tickFormat ? f.tickFormat.apply(f, a) : y : t, + p = l.selectAll(".tick").data(h, f), + v = p.enter().insert("g", ".domain").attr("class", "tick").style("opacity", Ca), + d = ta.transition(p.exit()).style("opacity", Ca).remove(), + m = ta.transition(p.order()).style("opacity", 1), + M = Math.max(u, 0) + o, + x = Ui(f), + b = l.selectAll(".domain").data([0]), + _ = (b.enter().append("path").attr("class", "domain"), ta.transition(b)); + v.append("line"), v.append("text"); + var w, S, k, E, A = v.select("line"), + N = m.select("line"), + C = p.select("text").text(g), + z = v.select("text"), + q = m.select("text"), + L = "top" === r || "left" === r ? -1 : 1; + if ("bottom" === r || "top" === r ? (n = Bo, w = "x", k = "y", S = "x2", E = "y2", C.attr("dy", 0 > L ? "0em" : ".71em").style("text-anchor", "middle"), _.attr("d", "M" + x[0] + "," + L * i + "V0H" + x[1] + "V" + L * i)) : (n = Wo, w = "y", k = "x", S = "y2", E = "x2", C.attr("dy", ".32em").style("text-anchor", 0 > L ? "end" : "start"), _.attr("d", "M" + L * i + "," + x[0] + "H0V" + x[1] + "H" + L * i)), A.attr(E, L * u), z.attr(k, L * M), N.attr(S, 0).attr(E, L * u), q.attr(w, 0).attr(k, L * M), f.rangeBand) { + var T = f, + R = T.rangeBand() / 2; + s = f = function(n) { + return T(n) + R + } + } else s.rangeBand ? s = f : d.call(n, f, s); + v.call(n, s, f), m.call(n, f, f) + }) + } + var t, e = ta.scale.linear(), + r = jl, + u = 6, + i = 6, + o = 3, + a = [10], + c = null; + return n.scale = function(t) { + return arguments.length ? (e = t, n) : e + }, n.orient = function(t) { + return arguments.length ? (r = t in Fl ? t + "" : jl, n) : r + }, n.ticks = function() { + return arguments.length ? (a = arguments, n) : a + }, n.tickValues = function(t) { + return arguments.length ? (c = t, n) : c + }, n.tickFormat = function(e) { + return arguments.length ? (t = e, n) : t + }, n.tickSize = function(t) { + var e = arguments.length; + return e ? (u = +t, i = +arguments[e - 1], n) : u + }, n.innerTickSize = function(t) { + return arguments.length ? (u = +t, n) : u + }, n.outerTickSize = function(t) { + return arguments.length ? (i = +t, n) : i + }, n.tickPadding = function(t) { + return arguments.length ? (o = +t, n) : o + }, n.tickSubdivide = function() { + return arguments.length && n + }, n + }; + var jl = "bottom", + Fl = { + top: 1, + right: 1, + bottom: 1, + left: 1 + }; + ta.svg.brush = function() { + function n(t) { + t.each(function() { + var t = ta.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", i).on("touchstart.brush", i), + o = t.selectAll(".background").data([0]); + o.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair"), t.selectAll(".extent").data([0]).enter().append("rect").attr("class", "extent").style("cursor", "move"); + var a = t.selectAll(".resize").data(v, y); + a.exit().remove(), a.enter().append("g").attr("class", function(n) { + return "resize " + n + }).style("cursor", function(n) { + return Hl[n] + }).append("rect").attr("x", function(n) { + return /[ew]$/.test(n) ? -3 : null + }).attr("y", function(n) { + return /^[ns]/.test(n) ? -3 : null + }).attr("width", 6).attr("height", 6).style("visibility", "hidden"), a.style("display", n.empty() ? "none" : null); + var c, f = ta.transition(t), + h = ta.transition(o); + l && (c = Ui(l), h.attr("x", c[0]).attr("width", c[1] - c[0]), r(f)), s && (c = Ui(s), h.attr("y", c[0]).attr("height", c[1] - c[0]), u(f)), e(f) + }) + } + + function e(n) { + n.selectAll(".resize").attr("transform", function(n) { + return "translate(" + f[+/e$/.test(n)] + "," + h[+/^s/.test(n)] + ")" + }) + } + + function r(n) { + n.select(".extent").attr("x", f[0]), n.selectAll(".extent,.n>rect,.s>rect").attr("width", f[1] - f[0]) + } + + function u(n) { + n.select(".extent").attr("y", h[0]), n.selectAll(".extent,.e>rect,.w>rect").attr("height", h[1] - h[0]) + } + + function i() { + function i() { + 32 == ta.event.keyCode && (C || (M = null, q[0] -= f[1], q[1] -= h[1], C = 2), S()) + } + + function v() { + 32 == ta.event.keyCode && 2 == C && (q[0] += f[1], q[1] += h[1], C = 0, S()) + } + + function d() { + var n = ta.mouse(b), + t = !1; + x && (n[0] += x[0], n[1] += x[1]), C || (ta.event.altKey ? (M || (M = [(f[0] + f[1]) / 2, (h[0] + h[1]) / 2]), q[0] = f[+(n[0] < M[0])], q[1] = h[+(n[1] < M[1])]) : M = null), A && m(n, l, 0) && (r(k), t = !0), N && m(n, s, 1) && (u(k), t = !0), t && (e(k), w({ + type: "brush", + mode: C ? "move" : "resize" + })) + } + + function m(n, t, e) { + var r, u, i = Ui(t), + c = i[0], + l = i[1], + s = q[e], + v = e ? h : f, + d = v[1] - v[0]; + return C && (c -= s, l -= d + s), r = (e ? p : g) ? Math.max(c, Math.min(l, n[e])) : n[e], C ? u = (r += s) + d : (M && (s = Math.max(c, Math.min(l, 2 * M[e] - r))), r > s ? (u = r, r = s) : u = s), v[0] != r || v[1] != u ? (e ? a = null : o = null, v[0] = r, v[1] = u, !0) : void 0 + } + + function y() { + d(), k.style("pointer-events", "all").selectAll(".resize").style("display", n.empty() ? "none" : null), ta.select("body").style("cursor", null), L.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null), z(), w({ + type: "brushend" + }) + } + var M, x, b = this, + _ = ta.select(ta.event.target), + w = c.of(b, arguments), + k = ta.select(b), + E = _.datum(), + A = !/^(n|s)$/.test(E) && l, + N = !/^(e|w)$/.test(E) && s, + C = _.classed("extent"), + z = W(b), + q = ta.mouse(b), + L = ta.select(t(b)).on("keydown.brush", i).on("keyup.brush", v); + if (ta.event.changedTouches ? L.on("touchmove.brush", d).on("touchend.brush", y) : L.on("mousemove.brush", d).on("mouseup.brush", y), k.interrupt().selectAll("*").interrupt(), C) q[0] = f[0] - q[0], q[1] = h[0] - q[1]; + else if (E) { + var T = +/w$/.test(E), + R = +/^n/.test(E); + x = [f[1 - T] - q[0], h[1 - R] - q[1]], q[0] = f[T], q[1] = h[R] + } else ta.event.altKey && (M = q.slice()); + k.style("pointer-events", "none").selectAll(".resize").style("display", null), ta.select("body").style("cursor", _.style("cursor")), w({ + type: "brushstart" + }), d() + } + var o, a, c = E(n, "brushstart", "brush", "brushend"), + l = null, + s = null, + f = [0, 0], + h = [0, 0], + g = !0, + p = !0, + v = Ol[0]; + return n.event = function(n) { + n.each(function() { + var n = c.of(this, arguments), + t = { + x: f, + y: h, + i: o, + j: a + }, + e = this.__chart__ || t; + this.__chart__ = t, Tl ? ta.select(this).transition().each("start.brush", function() { + o = e.i, a = e.j, f = e.x, h = e.y, n({ + type: "brushstart" + }) + }).tween("brush:brush", function() { + var e = yu(f, t.x), + r = yu(h, t.y); + return o = a = null, + function(u) { + f = t.x = e(u), h = t.y = r(u), n({ + type: "brush", + mode: "resize" + }) + } + }).each("end.brush", function() { + o = t.i, a = t.j, n({ + type: "brush", + mode: "resize" + }), n({ + type: "brushend" + }) + }) : (n({ + type: "brushstart" + }), n({ + type: "brush", + mode: "resize" + }), n({ + type: "brushend" + })) + }) + }, n.x = function(t) { + return arguments.length ? (l = t, v = Ol[!l << 1 | !s], n) : l + }, n.y = function(t) { + return arguments.length ? (s = t, v = Ol[!l << 1 | !s], n) : s + }, n.clamp = function(t) { + return arguments.length ? (l && s ? (g = !!t[0], p = !!t[1]) : l ? g = !!t : s && (p = !!t), n) : l && s ? [g, p] : l ? g : s ? p : null + }, n.extent = function(t) { + var e, r, u, i, c; + return arguments.length ? (l && (e = t[0], r = t[1], s && (e = e[0], r = r[0]), o = [e, r], l.invert && (e = l(e), r = l(r)), e > r && (c = e, e = r, r = c), (e != f[0] || r != f[1]) && (f = [e, r])), s && (u = t[0], i = t[1], l && (u = u[1], i = i[1]), a = [u, i], s.invert && (u = s(u), i = s(i)), u > i && (c = u, u = i, i = c), (u != h[0] || i != h[1]) && (h = [u, i])), n) : (l && (o ? (e = o[0], r = o[1]) : (e = f[0], r = f[1], l.invert && (e = l.invert(e), r = l.invert(r)), e > r && (c = e, e = r, r = c))), s && (a ? (u = a[0], i = a[1]) : (u = h[0], i = h[1], s.invert && (u = s.invert(u), i = s.invert(i)), u > i && (c = u, u = i, i = c))), l && s ? [ + [e, u], + [r, i] + ] : l ? [e, r] : s && [u, i]) + }, n.clear = function() { + return n.empty() || (f = [0, 0], h = [0, 0], o = a = null), n + }, n.empty = function() { + return !!l && f[0] == f[1] || !!s && h[0] == h[1] + }, ta.rebind(n, c, "on") + }; + var Hl = { + n: "ns-resize", + e: "ew-resize", + s: "ns-resize", + w: "ew-resize", + nw: "nwse-resize", + ne: "nesw-resize", + se: "nwse-resize", + sw: "nesw-resize" + }, + Ol = [ + ["n", "e", "s", "w", "nw", "ne", "se", "sw"], + ["e", "w"], + ["n", "s"], + [] + ], + Il = ac.format = gc.timeFormat, + Yl = Il.utc, + Zl = Yl("%Y-%m-%dT%H:%M:%S.%LZ"); + Il.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? Jo : Zl, Jo.parse = function(n) { + var t = new Date(n); + return isNaN(t) ? null : t + }, Jo.toString = Zl.toString, ac.second = Ft(function(n) { + return new cc(1e3 * Math.floor(n / 1e3)) + }, function(n, t) { + n.setTime(n.getTime() + 1e3 * Math.floor(t)) + }, function(n) { + return n.getSeconds() + }), ac.seconds = ac.second.range, ac.seconds.utc = ac.second.utc.range, ac.minute = Ft(function(n) { + return new cc(6e4 * Math.floor(n / 6e4)) + }, function(n, t) { + n.setTime(n.getTime() + 6e4 * Math.floor(t)) + }, function(n) { + return n.getMinutes() + }), ac.minutes = ac.minute.range, ac.minutes.utc = ac.minute.utc.range, ac.hour = Ft(function(n) { + var t = n.getTimezoneOffset() / 60; + return new cc(36e5 * (Math.floor(n / 36e5 - t) + t)) + }, function(n, t) { + n.setTime(n.getTime() + 36e5 * Math.floor(t)) + }, function(n) { + return n.getHours() + }), ac.hours = ac.hour.range, ac.hours.utc = ac.hour.utc.range, ac.month = Ft(function(n) { + return n = ac.day(n), n.setDate(1), n + }, function(n, t) { + n.setMonth(n.getMonth() + t) + }, function(n) { + return n.getMonth() + }), ac.months = ac.month.range, ac.months.utc = ac.month.utc.range; + var Vl = [1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6], + Xl = [ + [ac.second, 1], + [ac.second, 5], + [ac.second, 15], + [ac.second, 30], + [ac.minute, 1], + [ac.minute, 5], + [ac.minute, 15], + [ac.minute, 30], + [ac.hour, 1], + [ac.hour, 3], + [ac.hour, 6], + [ac.hour, 12], + [ac.day, 1], + [ac.day, 2], + [ac.week, 1], + [ac.month, 1], + [ac.month, 3], + [ac.year, 1] + ], + $l = Il.multi([ + [".%L", function(n) { + return n.getMilliseconds() + }], + [":%S", function(n) { + return n.getSeconds() + }], + ["%I:%M", function(n) { + return n.getMinutes() + }], + ["%I %p", function(n) { + return n.getHours() + }], + ["%a %d", function(n) { + return n.getDay() && 1 != n.getDate() + }], + ["%b %d", function(n) { + return 1 != n.getDate() + }], + ["%B", function(n) { + return n.getMonth() + }], + ["%Y", Ne] + ]), + Bl = { + range: function(n, t, e) { + return ta.range(Math.ceil(n / e) * e, +t, e).map(Ko) + }, + floor: y, + ceil: y + }; + Xl.year = ac.year, ac.scale = function() { + return Go(ta.scale.linear(), Xl, $l) + }; + var Wl = Xl.map(function(n) { + return [n[0].utc, n[1]] + }), + Jl = Yl.multi([ + [".%L", function(n) { + return n.getUTCMilliseconds() + }], + [":%S", function(n) { + return n.getUTCSeconds() + }], + ["%I:%M", function(n) { + return n.getUTCMinutes() + }], + ["%I %p", function(n) { + return n.getUTCHours() + }], + ["%a %d", function(n) { + return n.getUTCDay() && 1 != n.getUTCDate() + }], + ["%b %d", function(n) { + return 1 != n.getUTCDate() + }], + ["%B", function(n) { + return n.getUTCMonth() + }], + ["%Y", Ne] + ]); + Wl.year = ac.year.utc, ac.scale.utc = function() { + return Go(ta.scale.linear(), Wl, Jl) + }, ta.text = At(function(n) { + return n.responseText + }), ta.json = function(n, t) { + return Nt(n, "application/json", Qo, t) + }, ta.html = function(n, t) { + return Nt(n, "text/html", na, t) + }, ta.xml = At(function(n) { + return n.responseXML + }), "function" == typeof define && define.amd ? define(ta) : "object" == typeof module && module.exports && (module.exports = ta), this.d3 = ta +}(); \ No newline at end of file diff --git a/activity_dashboard_mngmnt/static/src/js/lib/funnel.js b/activity_dashboard_mngmnt/static/src/js/lib/funnel.js new file mode 100644 index 000000000..ccfa100d2 --- /dev/null +++ b/activity_dashboard_mngmnt/static/src/js/lib/funnel.js @@ -0,0 +1,5 @@ +(function(b){typeof module==="object"&&module.exports?module.exports=b:b(Highcharts)})(function(b){var q=b.getOptions(),w=q.plotOptions,r=b.seriesTypes,G=b.merge,E=function(){},B=b.each,F=b.pick;w.funnel=G(w.pie,{animation:!1,center:["50%","50%"],width:"90%",neckWidth:"30%",height:"100%",neckHeight:"25%",reversed:!1,dataLabels:{connectorWidth:1,connectorColor:"#606060"},size:!0,states:{select:{color:"#C0C0C0",borderColor:"#000000",shadow:!1}}});r.funnel=b.extendClass(r.pie,{type:"funnel",animate:E, +translate:function(){var a=function(k,a){return/%$/.test(k)?a*parseInt(k,10)/100:parseInt(k,10)},b=0,f=this.chart,c=this.options,g=c.reversed,l=c.ignoreHiddenPoint,h=f.plotWidth,d=f.plotHeight,q=0,f=c.center,i=a(f[0],h),x=a(f[1],d),r=a(c.width,h),m,s,e=a(c.height,d),t=a(c.neckWidth,h),C=a(c.neckHeight,d),u=x-e/2+e-C,a=this.data,y,z,w=c.dataLabels.position==="left"?1:0,A,n,D,p,j,v,o;this.getWidthAt=s=function(k){var a=x-e/2;return k>u||e===C?t:t+(r-t)*(1-(k-a)/(e-C))};this.getX=function(k,a){return i+ +(a?-1:1)*(s(g?d-k:k)/2+c.dataLabels.distance)};this.center=[i,x,e];this.centerX=i;B(a,function(a){if(!l||a.visible!==!1)b+=a.y});B(a,function(a){o=null;z=b?a.y/b:0;n=x-e/2+q*e;j=n+z*e;m=s(n);A=i-m/2;D=A+m;m=s(j);p=i-m/2;v=p+m;n>u?(A=p=i-t/2,D=v=i+t/2):j>u&&(o=j,m=s(u),p=i-m/2,v=p+m,j=u);g&&(n=e-n,j=e-j,o=o?e-o:null);y=["M",A,n,"L",D,n,v,j];o&&y.push(v,o,p,o);y.push(p,j,"Z");a.shapeType="path";a.shapeArgs={d:y};a.percentage=z*100;a.plotX=i;a.plotY=(n+(o||j))/2;a.tooltipPos=[i,a.plotY];a.slice=E;a.half= +w;if(!l||a.visible!==!1)q+=z})},drawPoints:function(){var a=this,b=a.options,f=a.chart.renderer,c,g,l,h;B(a.data,function(d){c=d.options;h=d.graphic;l=d.shapeArgs;g={fill:d.color,stroke:F(c.borderColor,b.borderColor),"stroke-width":F(c.borderWidth,b.borderWidth)};h?h.attr(g).animate(l):d.graphic=f.path(l).attr(g).add(a.group)})},sortByAngle:function(a){a.sort(function(a,b){return a.plotY-b.plotY})},drawDataLabels:function(){var a=this.data,b=this.options.dataLabels.distance,f,c,g,l=a.length,h,d;for(this.center[2]-= +2*b;l--;)g=a[l],c=(f=g.half)?1:-1,d=g.plotY,h=this.getX(d,f),g.labelPos=[0,d,h+(b-5)*c,d,h+b*c,d,f?"right":"left",0];r.pie.prototype.drawDataLabels.call(this)}});q.plotOptions.pyramid=b.merge(q.plotOptions.funnel,{neckWidth:"0%",neckHeight:"0%",reversed:!0});b.seriesTypes.pyramid=b.extendClass(b.seriesTypes.funnel,{type:"pyramid"})}); diff --git a/activity_dashboard_mngmnt/static/src/js/lib/highcharts.js b/activity_dashboard_mngmnt/static/src/js/lib/highcharts.js new file mode 100644 index 000000000..fc26d2d83 --- /dev/null +++ b/activity_dashboard_mngmnt/static/src/js/lib/highcharts.js @@ -0,0 +1,333 @@ +(function(E,X){typeof module==="object"&&module.exports?module.exports=E.document?X(E):X:E.Highcharts=X(E)})(typeof window!=="undefined"?window:this,function(E){function X(a,b){var c="Highcharts error #"+a+": www.highcharts.com/errors/"+a;if(b)throw Error(c);E.console&&console.log(c)}function pb(a,b,c){this.options=b;this.elem=a;this.prop=c}function D(){var a,b=arguments,c,d={},e=function(a,b){var c,d;typeof a!=="object"&&(a={});for(d in b)b.hasOwnProperty(d)&&(c=b[d],a[d]=c&&typeof c==="object"&& +Object.prototype.toString.call(c)!=="[object Array]"&&d!=="renderTo"&&typeof c.nodeType!=="number"?e(a[d]||{},c):b[d]);return a};b[0]===!0&&(d=b[1],b=Array.prototype.slice.call(b,2));c=b.length;for(a=0;a-1?h.thousandsSep:""))):e=Qa(f,e)}k.push(e);a=a.slice(c+1);c=(d=!d)?"}":"{"}k.push(a);return k.join("")}function rb(a){return W.pow(10,T(W.log(a)/W.LN10))}function sb(a,b,c,d,e){var f,g=a,c=p(c,1);f=a/c;b||(b=[1,2,2.5,5,10],d===!1&&(c===1?b=[1,2,5,10]:c<=0.1&&(b=[1/c])));for(d=0;d=a||!e&&f<=(b[d]+(b[d+1]||b[d]))/ +2)break;g*=c;return g}function ib(a,b){var c=a.length,d,e;for(e=0;ec&&(c=a[b]);return c}function Sa(a,b){for(var c in a)a[c]&&a[c]!==b&&a[c].destroy&&a[c].destroy(),delete a[c]}function Ta(a){jb||(jb=Z(La));a&&jb.appendChild(a);jb.innerHTML=""}function fa(a,b){return parseFloat(a.toPrecision(b|| +14))}function Ua(a,b){b.renderer.globalAnimation=p(a,b.animation)}function Fb(){var a=N.global,b=a.useUTC,c=b?"getUTC":"get",d=b?"setUTC":"set";qa=a.Date||E.Date;qb=b&&a.timezoneOffset;Za=b&&a.getTimezoneOffset;kb=function(a,c,d,h,i,k){var j;b?(j=qa.UTC.apply(0,arguments),j+=Ya(j)):j=(new qa(a,c,p(d,1),p(h,0),p(i,0),p(k,0))).getTime();return j};tb=c+"Minutes";ub=c+"Hours";vb=c+"Day";$a=c+"Date";ab=c+"Month";bb=c+"FullYear";Gb=d+"Milliseconds";Hb=d+"Seconds";Ib=d+"Minutes";Jb=d+"Hours";wb=d+"Date"; +xb=d+"Month";yb=d+"FullYear"}function ia(a){if(!(this instanceof ia))return new ia(a);this.init(a)}function O(){}function Va(a,b,c,d){this.axis=a;this.pos=b;this.type=c||"";this.isNew=!0;!c&&!d&&this.addLabel()}function Kb(a,b,c,d,e){var f=a.chart.inverted;this.axis=a;this.isNegative=c;this.options=b;this.x=d;this.total=null;this.points={};this.stack=e;this.rightCliff=this.leftCliff=0;this.alignOptions={align:b.align||(f?c?"left":"right":"center"),verticalAlign:b.verticalAlign||(f?"middle":c?"bottom": +"top"),y:p(b.y,f?4:c?14:-6),x:p(b.x,f?c?-6:6:0)};this.textAlign=b.textAlign||(f?c?"right":"left":"center")}var z,y=E.document,W=Math,A=W.round,T=W.floor,ua=W.ceil,t=W.max,F=W.min,P=W.abs,U=W.cos,$=W.sin,ra=W.PI,ga=ra*2/360,za=E.navigator&&E.navigator.userAgent||"",Lb=E.opera,ya=/(msie|trident|edge)/i.test(za)&&!Lb,lb=y&&y.documentMode===8,mb=!ya&&/AppleWebKit/.test(za),Ma=/Firefox/.test(za),Mb=/(Mobile|Android|Windows Phone)/.test(za),Fa="http://www.w3.org/2000/svg",ca=y&&y.createElementNS&&!!y.createElementNS(Fa, +"svg").createSVGRect,Qb=Ma&&parseInt(za.split("Firefox/")[1],10)<4,ha=y&&!ca&&!ya&&!!y.createElement("canvas").getContext,cb,db,Nb={},zb=0,jb,N,Qa,G,Aa=function(){},S=[],eb=0,La="div",Rb=/^[0-9]+$/,nb=["plotTop","marginRight","marginBottom","plotLeft"],qa,kb,qb,Za,tb,ub,vb,$a,ab,bb,Gb,Hb,Ib,Jb,wb,xb,yb,I={},B;B=E.Highcharts?X(16,!0):{win:E};B.seriesTypes=I;var Ga=[],ja,sa,o,Na,Ab,Ba,M,V,H,Wa,Oa;pb.prototype={dSetter:function(){var a=this.paths[0],b=this.paths[1],c=[],d=this.now,e=a.length,f;if(d=== +1)c=this.toD;else if(e===b.length&&d<1)for(;e--;)f=parseFloat(a[e]),c[e]=isNaN(f)?a[e]:d*parseFloat(b[e]-f)+f;else c=b;this.elem.attr("d",c)},update:function(){var a=this.elem,b=this.prop,c=this.now,d=this.options.step;if(this[b+"Setter"])this[b+"Setter"]();else a.attr?a.element&&a.attr(b,c):a.style[b]=c+this.unit;d&&d.call(a,c,this)},run:function(a,b,c){var d=this,e=function(a){return e.stopped?!1:d.step(a)},f;this.startTime=+new qa;this.start=a;this.end=b;this.unit=c;this.now=this.start;this.pos= +0;e.elem=this.elem;if(e()&&Ga.push(e)===1)e.timerId=setInterval(function(){for(f=0;f=f+this.startTime){this.now=this.end;this.pos=1;this.update();a=g[this.prop]=!0;for(h in g)g[h]!==!0&&(a=!1);a&&e&&e.call(c);c=!1}else this.pos=d.easing((b-this.startTime)/f),this.now=this.start+ +(this.end-this.start)*this.pos,this.update(),c=!0;return c},initPath:function(a,b,c){var b=b||"",d=a.shift,e=b.indexOf("C")>-1,f=e?7:3,g,b=b.split(" "),c=[].concat(c),h=a.isArea,i=h?2:1,k=function(a){for(g=a.length;g--;)(a[g]==="M"||a[g]==="L")&&a.splice(g+1,0,a[g+1],a[g+2],a[g+1],a[g+2])};e&&(k(b),k(c));if(d<=c.length/f&&b.length===c.length)for(;d--;)c=c.slice(0,f).concat(c),h&&(c=c.concat(c.slice(c.length-f)));a.shift=0;if(b.length)for(a=c.length;b.length3?g.length%3:0;c=p(c,e.decimalPoint);d=p(d,e.thousandsSep);a=a<0?"-":"";a+=h?g.substr(0,h)+d:"";a+=g.substr(h).replace(/(\d{3})(?=\d)/g,"$1"+d);+b&&(d=Math.abs(i-g+Math.pow(10,-Math.max(b,f)-1)),a+=c+d.toFixed(b).slice(2)); +return a};Math.easeInOutSine=function(a){return-0.5*(Math.cos(Math.PI*a)-1)};ja=function(a,b){var c;if(b==="width")return Math.min(a.offsetWidth,a.scrollWidth)-ja(a,"padding-left")-ja(a,"padding-right");else if(b==="height")return Math.min(a.offsetHeight,a.scrollHeight)-ja(a,"padding-top")-ja(a,"padding-bottom");return(c=E.getComputedStyle(a,void 0))&&C(c.getPropertyValue(b))};sa=function(a,b){return b.indexOf?b.indexOf(a):[].indexOf.call(b,a)};Na=function(a,b){return[].filter.call(a,b)};Ba=function(a, +b){for(var c=[],d=0,e=a.length;d-1&&(f.splice(h,1),g[b]=f),d(b,c)):(e(),g[b]=[])):(e(),a.hcEvents={})};H=function(a,b,c,d){var e;e=a.hcEvents;var f,g,h,i,c=c||{};if(y.createEvent&&(a.dispatchEvent||a.fireEvent))e=y.createEvent("Events"),e.initEvent(b,!0,!0),e.target=a,u(e,c),a.dispatchEvent?a.dispatchEvent(e):a.fireEvent(b,e);else if(e){e=e[b]||[];f=e.length;h=function(){c.defaultPrevented=!0};for(g=0;g{point.key}
',pointFormat:'\u25cf {series.name}: {point.y}
',shadow:!0,snap:Mb?25:10,style:{color:"#333333",cursor:"default",fontSize:"12px",padding:"8px",pointerEvents:"none",whiteSpace:"nowrap"}},credits:{enabled:!0,text:"Highcharts.com", +href:"http://www.highcharts.com",position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer",color:"#909090",fontSize:"9px"}}};var aa=N.plotOptions,da=aa.line;Fb();ia.prototype={parsers:[{regex:/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/,parse:function(a){return[C(a[1]),C(a[2]),C(a[3]),parseFloat(a[4],10)]}},{regex:/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,parse:function(a){return[C(a[1],16),C(a[2],16),C(a[3],16), +1]}},{regex:/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,parse:function(a){return[C(a[1]),C(a[2]),C(a[3]),1]}}],init:function(a){var b,c,d,e;if((this.input=a)&&a.stops)this.stops=Ba(a.stops,function(a){return new ia(a[1])});else for(d=this.parsers.length;d--&&!c;)e=this.parsers[d],(b=e.regex.exec(a))&&(c=e.parse(b));this.rgba=c||[]},get:function(a){var b=this.input,c=this.rgba,d;this.stops?(d=D(b),d.stops=[].concat(d.stops),o(this.stops,function(b,c){d.stops[c]=[d.stops[c][0], +b.get(a)]})):d=c&&!isNaN(c[0])?a==="rgb"||!a&&c[3]===1?"rgb("+c[0]+","+c[1]+","+c[2]+")":a==="a"?c[3]:"rgba("+c.join(",")+")":b;return d},brighten:function(a){var b,c=this.rgba;if(this.stops)o(this.stops,function(b){b.brighten(a)});else if(ma(a)&&a!==0)for(b=0;b<3;b++)c[b]+=C(a*255),c[b]<0&&(c[b]=0),c[b]>255&&(c[b]=255);return this},setOpacity:function(a){this.rgba[3]=a;return this}};O.prototype={opacity:1,textProps:"direction,fontSize,fontWeight,fontFamily,fontStyle,color,lineHeight,width,textDecoration,textOverflow,textShadow".split(","), +init:function(a,b){this.element=b==="span"?Z(b):y.createElementNS(Fa,b);this.renderer=a},animate:function(a,b,c){b=p(b,this.renderer.globalAnimation,!0);Oa(this);if(b){b=D(b,{});if(c)b.complete=c;Wa(this,a,b)}else this.attr(a,null,c);return this},colorGradient:function(a,b,c){var d=this.renderer,e,f,g,h,i,k,j,l,m,n,r,s=[],p;a.linearGradient?f="linearGradient":a.radialGradient&&(f="radialGradient");if(f){g=a[f];i=d.gradients;j=a.stops;n=c.radialReference;Ia(g)&&(a[f]=g={x1:g[0],y1:g[1],x2:g[2],y2:g[3], +gradientUnits:"userSpaceOnUse"});f==="radialGradient"&&n&&!q(g.gradientUnits)&&(h=g,g=D(g,d.getRadialAttr(n,h),{gradientUnits:"userSpaceOnUse"}));for(r in g)r!=="id"&&s.push(r,g[r]);for(r in j)s.push(j[r]);s=s.join(",");i[s]?n=i[s].attr("id"):(g.id=n="highcharts-"+zb++,i[s]=k=d.createElement(f).attr(g).add(d.defs),k.radAttr=h,k.stops=[],o(j,function(a){a[1].indexOf("rgba")===0?(e=ia(a[1]),l=e.get("rgb"),m=e.get("a")):(l=a[1],m=1);a=d.createElement("stop").attr({offset:a[0],"stop-color":l,"stop-opacity":m}).add(k); +k.stops.push(a)}));p="url("+d.url+"#"+n+")";c.setAttribute(b,p);c.gradient=s;a.toString=function(){return p}}},applyTextShadow:function(a){var b=this.element,c,d=a.indexOf("contrast")!==-1,e={},f=this.renderer.forExport,g=f||b.style.textShadow!==z&&!ya;if(d)e.textShadow=a=a.replace(/contrast/g,this.renderer.getContrast(b.style.fill));if(mb||f)e.textRendering="geometricPrecision";g?this.css(e):(this.fakeTS=!0,this.ySetter=this.xSetter,c=[].slice.call(b.getElementsByTagName("tspan")),o(a.split(/\s?,\s?/g), +function(a){var d=b.firstChild,e,f,a=a.split(" ");e=a[a.length-1];(f=a[a.length-2])&&o(c,function(a,c){var g;c===0&&(a.setAttribute("x",b.getAttribute("x")),c=b.getAttribute("y"),a.setAttribute("y",c||0),c===null&&b.setAttribute("y",0));g=a.cloneNode(1);K(g,{"class":"highcharts-text-shadow",fill:e,stroke:e,"stroke-opacity":1/t(C(f),3),"stroke-width":f,"stroke-linejoin":"round"});b.insertBefore(g,d)})}))},attr:function(a,b,c){var d,e=this.element,f,g=this,h;typeof a==="string"&&b!==z&&(d=a,a={},a[d]= +b);if(typeof a==="string")g=(this[a+"Getter"]||this._defaultGetter).call(this,a,e);else{for(d in a){b=a[d];h=!1;this.symbolName&&/^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(d)&&(f||(this.symbolAttr(a),f=!0),h=!0);if(this.rotation&&(d==="x"||d==="y"))this.doTransform=!0;h||(h=this[d+"Setter"]||this._defaultSetter,h.call(this,b,d,e),this.shadows&&/^(width|height|visibility|x|y|d|transform|cx|cy|r)$/.test(d)&&this.updateShadows(d,b,h))}if(this.doTransform)this.updateTransform(),this.doTransform= +!1}c&&c();return g},updateShadows:function(a,b,c){for(var d=this.shadows,e=d.length;e--;)c.call(null,a==="height"?Math.max(b-(d[e].cutHeight||0),0):a==="d"?this.d:b,a,d[e])},addClass:function(a){var b=this.element,c=K(b,"class")||"";c.indexOf(a)===-1&&K(b,"class",c+" "+a);return this},symbolAttr:function(a){var b=this;o("x,y,r,start,end,width,height,innerR,anchorX,anchorY".split(","),function(c){b[c]=p(a[c],b[c])});b.attr({d:b.renderer.symbols[b.symbolName](b.x,b.y,b.width,b.height,b)})},clip:function(a){return this.attr("clip-path", +a?"url("+this.renderer.url+"#"+a.id+")":"none")},crisp:function(a){var b,c={},d,e=this.strokeWidth||0;d=A(e)%2/2;a.x=T(a.x||this.x||0)+d;a.y=T(a.y||this.y||0)+d;a.width=T((a.width||this.width||0)-2*d);a.height=T((a.height||this.height||0)-2*d);a.strokeWidth=e;for(b in a)this[b]!==a[b]&&(this[b]=c[b]=a[b]);return c},css:function(a){var b=this.styles,c={},d=this.element,e,f,g="";e=!b;if(a&&a.color)a.fill=a.color;if(b)for(f in a)a[f]!==b[f]&&(c[f]=a[f],e=!0);if(e){e=this.textWidth=a&&a.width&&d.nodeName.toLowerCase()=== +"text"&&C(a.width)||this.textWidth;b&&(a=u(b,c));this.styles=a;e&&(ha||!ca&&this.renderer.forExport)&&delete a.width;if(ya&&!ca)L(this.element,a);else{b=function(a,b){return"-"+b.toLowerCase()};for(f in a)g+=f.replace(/([A-Z])/g,b)+":"+a[f]+";";K(d,"style",g)}e&&this.added&&this.renderer.buildText(this)}return this},on:function(a,b){var c=this,d=c.element;db&&a==="click"?(d.ontouchstart=function(a){c.touchEventFired=qa.now();a.preventDefault();b.call(d,a)},d.onclick=function(a){(za.indexOf("Android")=== +-1||qa.now()-(c.touchEventFired||0)>1100)&&b.call(d,a)}):d["on"+a]=b;return this},setRadialReference:function(a){var b=this.renderer.gradients[this.element.gradient];this.element.radialReference=a;b&&b.radAttr&&b.animate(this.renderer.getRadialAttr(a,b.radAttr));return this},translate:function(a,b){return this.attr({translateX:a,translateY:b})},invert:function(){this.inverted=!0;this.updateTransform();return this},updateTransform:function(){var a=this.translateX||0,b=this.translateY||0,c=this.scaleX, +d=this.scaleY,e=this.inverted,f=this.rotation,g=this.element;e&&(a+=this.attr("width"),b+=this.attr("height"));a=["translate("+a+","+b+")"];e?a.push("rotate(90) scale(-1,1)"):f&&a.push("rotate("+f+" "+(g.getAttribute("x")||0)+" "+(g.getAttribute("y")||0)+")");(q(c)||q(d))&&a.push("scale("+p(c,1)+" "+p(d,1)+")");a.length&&g.setAttribute("transform",a.join(" "))},toFront:function(){var a=this.element;a.parentNode.appendChild(a);return this},align:function(a,b,c){var d,e,f,g,h={};e=this.renderer;f=e.alignedObjects; +if(a){if(this.alignOptions=a,this.alignByTranslate=b,!c||xa(c))this.alignTo=d=c||"renderer",oa(f,this),f.push(this),c=null}else a=this.alignOptions,b=this.alignByTranslate,d=this.alignTo;c=p(c,e[d],e);d=a.align;e=a.verticalAlign;f=(c.x||0)+(a.x||0);g=(c.y||0)+(a.y||0);if(d==="right"||d==="center")f+=(c.width-(a.width||0))/{right:1,center:2}[d];h[b?"translateX":"x"]=A(f);if(e==="bottom"||e==="middle")g+=(c.height-(a.height||0))/({bottom:1,middle:2}[e]||1);h[b?"translateY":"y"]=A(g);this[this.placed? +"animate":"attr"](h);this.placed=!0;this.alignAttr=h;return this},getBBox:function(a,b){var c,d=this.renderer,e,f,g,h=this.element,i=this.styles;e=this.textStr;var k,j=h.style,l,m=d.cache,n=d.cacheKeys,r;f=p(b,this.rotation);g=f*ga;e!==z&&(r=["",f||0,i&&i.fontSize,h.style.width].join(","),r=e===""||Rb.test(e)?"num:"+e.toString().length+r:e+r);r&&!a&&(c=m[r]);if(!c){if(h.namespaceURI===Fa||d.forExport){try{l=this.fakeTS&&function(a){o(h.querySelectorAll(".highcharts-text-shadow"),function(b){b.style.display= +a})},Ma&&j.textShadow?(k=j.textShadow,j.textShadow=""):l&&l("none"),c=h.getBBox?u({},h.getBBox()):{width:h.offsetWidth,height:h.offsetHeight},k?j.textShadow=k:l&&l("")}catch(s){}if(!c||c.width<0)c={width:0,height:0}}else c=this.htmlGetBBox();if(d.isSVG){d=c.width;e=c.height;if(ya&&i&&i.fontSize==="11px"&&e.toPrecision(3)==="16.9")c.height=e=14;if(f)c.width=P(e*$(g))+P(d*U(g)),c.height=P(e*U(g))+P(d*$(g))}if(r){for(;n.length>250;)delete m[n.shift()];m[r]||n.push(r);m[r]=c}}return c},show:function(a){return this.attr({visibility:a? +"inherit":"visible"})},hide:function(){return this.attr({visibility:"hidden"})},fadeOut:function(a){var b=this;b.animate({opacity:0},{duration:a||150,complete:function(){b.attr({y:-9999})}})},add:function(a){var b=this.renderer,c=this.element,d;if(a)this.parentGroup=a;this.parentInverted=a&&a.inverted;this.textStr!==void 0&&b.buildText(this);this.added=!0;if(!a||a.handleZ||this.zIndex)d=this.zIndexSetter();d||(a?a.element:b.box).appendChild(c);if(this.onAdd)this.onAdd();return this},safeRemoveChild:function(a){var b= +a.parentNode;b&&b.removeChild(a)},destroy:function(){var a=this,b=a.element||{},c=a.shadows,d=a.renderer.isSVG&&b.nodeName==="SPAN"&&a.parentGroup,e,f;b.onclick=b.onmouseout=b.onmouseover=b.onmousemove=b.point=null;Oa(a);if(a.clipPath)a.clipPath=a.clipPath.destroy();if(a.stops){for(f=0;f]*>/g,"")))},textSetter:function(a){if(a!==this.textStr)delete this.bBox,this.textStr=a,this.added&&this.renderer.buildText(this)},fillSetter:function(a,b,c){typeof a==="string"?c.setAttribute(b,a):a&&this.colorGradient(a,b,c)},visibilitySetter:function(a,b,c){a=== +"inherit"?c.removeAttribute(b):c.setAttribute(b,a)},zIndexSetter:function(a,b){var c=this.renderer,d=this.parentGroup,c=(d||c).element||c.box,e,f,g=this.element,h;e=this.added;var i;q(a)&&(g.setAttribute(b,a),a=+a,this[b]===a&&(e=!1),this[b]=a);if(e){if((a=this.zIndex)&&d)d.handleZ=!0;d=c.childNodes;for(i=0;ia||!q(a)&&q(f)))c.insertBefore(g,e),h=!0;h||c.appendChild(g)}return h},_defaultSetter:function(a,b,c){c.setAttribute(b,a)}};O.prototype.yGetter= +O.prototype.xGetter;O.prototype.translateXSetter=O.prototype.translateYSetter=O.prototype.rotationSetter=O.prototype.verticalAlignSetter=O.prototype.scaleXSetter=O.prototype.scaleYSetter=function(a,b){this[b]=a;this.doTransform=!0};O.prototype["stroke-widthSetter"]=O.prototype.strokeSetter=function(a,b,c){this[b]=a;if(this.stroke&&this["stroke-width"])this.strokeWidth=this["stroke-width"],O.prototype.fillSetter.call(this,this.stroke,"stroke",c),c.setAttribute("stroke-width",this["stroke-width"]), +this.hasStroke=!0;else if(b==="stroke-width"&&a===0&&this.hasStroke)c.removeAttribute("stroke"),this.hasStroke=!1};var Ca=function(){this.init.apply(this,arguments)};Ca.prototype={Element:O,init:function(a,b,c,d,e,f){var g,d=this.createElement("svg").attr({version:"1.1"}).css(this.getStyle(d));g=d.element;a.appendChild(g);a.innerHTML.indexOf("xmlns")===-1&&K(g,"xmlns",Fa);this.isSVG=!0;this.box=g;this.boxWrapper=d;this.alignedObjects=[];this.url=(Ma||mb)&&y.getElementsByTagName("base").length?E.location.href.replace(/#.*?$/, +"").replace(/([\('\)])/g,"\\$1").replace(/ /g,"%20"):"";this.createElement("desc").add().element.appendChild(y.createTextNode("Created with Highcharts 4.2.3"));this.defs=this.createElement("defs").add();this.allowHTML=f;this.forExport=e;this.gradients={};this.cache={};this.cacheKeys=[];this.imgCount=0;this.setSize(b,c,!1);var h;if(Ma&&a.getBoundingClientRect)this.subPixelFix=b=function(){L(a,{left:0,top:0});h=a.getBoundingClientRect();L(a,{left:ua(h.left)-h.left+"px",top:ua(h.top)-h.top+"px"})},b(), +M(E,"resize",b)},getStyle:function(a){return this.style=u({fontFamily:'"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif',fontSize:"12px"},a)},isHidden:function(){return!this.boxWrapper.getBBox().width},destroy:function(){var a=this.defs;this.box=null;this.boxWrapper=this.boxWrapper.destroy();Sa(this.gradients||{});this.gradients=null;if(a)this.defs=a.destroy();this.subPixelFix&&V(E,"resize",this.subPixelFix);return this.alignedObjects=null},createElement:function(a){var b=new this.Element; +b.init(this,a);return b},draw:function(){},getRadialAttr:function(a,b){return{cx:a[0]-a[2]/2+b.cx*a[2],cy:a[1]-a[2]/2+b.cy*a[2],r:b.r*a[2]}},buildText:function(a){for(var b=a.element,c=this,d=c.forExport,e=p(a.textStr,"").toString(),f=e.indexOf("<")!==-1,g=b.childNodes,h,i,k=K(b,"x"),j=a.styles,l=a.textWidth,m=j&&j.lineHeight,n=j&&j.textShadow,r=j&&j.textOverflow==="ellipsis",s=g.length,R=l&&!a.added&&this.box,v=function(a){return m?C(m):c.fontMetrics(/(px|em)$/.test(a&&a.style.fontSize)?a.style.fontSize: +j&&j.fontSize||c.style.fontSize||12,a).h},x=function(a){return a.replace(/</g,"<").replace(/>/g,">")};s--;)b.removeChild(g[s]);!f&&!n&&!r&&e.indexOf(" ")===-1?b.appendChild(y.createTextNode(x(e))):(h=/<.*style="([^"]+)".*>/,i=/<.*href="(http[^"]+)".*>/,R&&R.appendChild(b),e=f?e.replace(/<(b|strong)>/g,'').replace(/<(i|em)>/g,'').replace(//g,"").split(//g):[e],e[e.length-1]=== +""&&e.pop(),o(e,function(e,f){var g,m=0,e=e.replace(//g,"|||");g=e.split("|||");o(g,function(e){if(e!==""||g.length===1){var n={},s=y.createElementNS(Fa,"tspan"),p;h.test(e)&&(p=e.match(h)[1].replace(/(;| |^)color([ :])/,"$1fill$2"),K(s,"style",p));i.test(e)&&!d&&(K(s,"onclick",'location.href="'+e.match(i)[1]+'"'),L(s,{cursor:"pointer"}));e=x(e.replace(/<(.|\n)*?>/g,"")||" ");if(e!==" "){s.appendChild(y.createTextNode(e));if(m)n.dx=0;else if(f&&k!==null)n.x= +k;K(s,n);b.appendChild(s);!m&&f&&(!ca&&d&&L(s,{display:"block"}),K(s,"dy",v(s)));if(l){for(var n=e.replace(/([^\^])-/g,"$1- ").split(" "),R=g.length>1||f||n.length>1&&j.whiteSpace!=="nowrap",o,w,q,t=[],u=v(s),A=1,z=a.rotation,B=e,D=B.length;(R||r)&&(n.length||t.length);)a.rotation=0,o=a.getBBox(!0),q=o.width,!ca&&c.forExport&&(q=c.measureSpanWidth(s.firstChild.data,a.styles)),o=q>l,w===void 0&&(w=o),r&&w?(D/=2,B===""||!o&&D<0.5?n=[]:(o&&(w=!0),B=e.substring(0,B.length+(o?-1:1)*ua(D)),n=[B+(l>3?"\u2026": +"")],s.removeChild(s.firstChild))):!o||n.length===1?(n=t,t=[],n.length&&(A++,s=y.createElementNS(Fa,"tspan"),K(s,{dy:u,x:k}),p&&K(s,"style",p),b.appendChild(s)),q>l&&(l=q)):(s.removeChild(s.firstChild),t.unshift(n.pop())),n.length&&s.appendChild(y.createTextNode(n.join(" ").replace(/- /g,"-")));w&&a.attr("title",a.textStr);a.rotation=z}m++}}})}),R&&R.removeChild(b),n&&a.applyTextShadow&&a.applyTextShadow(n))},getContrast:function(a){a=ia(a).rgba;return a[0]+a[1]+a[2]>384?"#000000":"#FFFFFF"},button:function(a, +b,c,d,e,f,g,h,i){var k=this.label(a,b,c,i,null,null,null,null,"button"),j=0,l,m,n,r,s,p,a={x1:0,y1:0,x2:0,y2:1},e=D({"stroke-width":1,stroke:"#CCCCCC",fill:{linearGradient:a,stops:[[0,"#FEFEFE"],[1,"#F6F6F6"]]},r:2,padding:5,style:{color:"black"}},e);n=e.style;delete e.style;f=D(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,"#FFF"],[1,"#ACF"]]}},f);r=f.style;delete f.style;g=D(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,"#9BD"],[1,"#CDF"]]}},g);s=g.style;delete g.style;h=D(e,{style:{color:"#CCC"}}, +h);p=h.style;delete h.style;M(k.element,ya?"mouseover":"mouseenter",function(){j!==3&&k.attr(f).css(r)});M(k.element,ya?"mouseout":"mouseleave",function(){j!==3&&(l=[e,f,g][j],m=[n,r,s][j],k.attr(l).css(m))});k.setState=function(a){(k.state=j=a)?a===2?k.attr(g).css(s):a===3&&k.attr(h).css(p):k.attr(e).css(n)};return k.on("click",function(a){j!==3&&d.call(k,a)}).attr(e).css(u({cursor:"default"},n))},crispLine:function(a,b){a[1]===a[4]&&(a[1]=a[4]=A(a[1])-b%2/2);a[2]===a[5]&&(a[2]=a[5]=A(a[2])+b%2/ +2);return a},path:function(a){var b={fill:"none"};Ia(a)?b.d=a:Y(a)&&u(b,a);return this.createElement("path").attr(b)},circle:function(a,b,c){a=Y(a)?a:{x:a,y:b,r:c};b=this.createElement("circle");b.xSetter=b.ySetter=function(a,b,c){c.setAttribute("c"+b,a)};return b.attr(a)},arc:function(a,b,c,d,e,f){if(Y(a))b=a.y,c=a.r,d=a.innerR,e=a.start,f=a.end,a=a.x;a=this.symbol("arc",a||0,b||0,c||0,c||0,{innerR:d||0,start:e||0,end:f||0});a.r=c;return a},rect:function(a,b,c,d,e,f){var e=Y(a)?a.r:e,g=this.createElement("rect"), +a=Y(a)?a:a===z?{}:{x:a,y:b,width:t(c,0),height:t(d,0)};if(f!==z)g.strokeWidth=f,a=g.crisp(a);if(e)a.r=e;g.rSetter=function(a,b,c){K(c,{rx:a,ry:a})};return g.attr(a)},setSize:function(a,b,c){var d=this.alignedObjects,e=d.length;this.width=a;this.height=b;for(this.boxWrapper[p(c,!0)?"animate":"attr"]({width:a,height:b});e--;)d[e].align()},g:function(a){var b=this.createElement("g");return q(a)?b.attr({"class":"highcharts-"+a}):b},image:function(a,b,c,d,e){var f={preserveAspectRatio:"none"};arguments.length> +1&&u(f,{x:b,y:c,width:d,height:e});f=this.createElement("image").attr(f);f.element.setAttributeNS?f.element.setAttributeNS("http://www.w3.org/1999/xlink","href",a):f.element.setAttribute("hc-svg-href",a);return f},symbol:function(a,b,c,d,e,f){var g=this,h,i=this.symbols[a],i=i&&i(A(b),A(c),d,e,f),k=/^url\((.*?)\)$/,j,l;if(i)h=this.path(i),u(h,{symbolName:a,x:b,y:c,width:d,height:e}),f&&u(h,f);else if(k.test(a))l=function(a,b){a.element&&(a.attr({width:b[0],height:b[1]}),a.alignByTranslate||a.translate(A((d- +b[0])/2),A((e-b[1])/2)))},j=a.match(k)[1],a=Nb[j]||f&&f.width&&f.height&&[f.width,f.height],h=this.image(j).attr({x:b,y:c}),h.isImg=!0,a?l(h,a):(h.attr({width:0,height:0}),Z("img",{onload:function(){this.width===0&&(L(this,{position:"absolute",top:"-999em"}),y.body.appendChild(this));l(h,Nb[j]=[this.width,this.height]);this.parentNode&&this.parentNode.removeChild(this);g.imgCount--;if(!g.imgCount)S[g.chartIndex].onload()},src:j})),this.imgCount++;return h},symbols:{circle:function(a,b,c,d){var e= +0.166*c;return["M",a+c/2,b,"C",a+c+e,b,a+c+e,b+d,a+c/2,b+d,"C",a-e,b+d,a-e,b,a+c/2,b,"Z"]},square:function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c,b+d,a,b+d,"Z"]},triangle:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d,a,b+d,"Z"]},"triangle-down":function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c/2,b+d,"Z"]},diamond:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d/2,a+c/2,b+d,a,b+d/2,"Z"]},arc:function(a,b,c,d,e){var f=e.start,c=e.r||c||d,g=e.end-0.001,d=e.innerR,h=e.open,i=U(f),k=$(f),j=U(g),g=$(g), +e=e.end-fc&&e>b+g&&eb+g&&ed&&h>a+g&&ha+g&&hj&&/[ \-]/.test(b.textContent||b.innerText))L(b,{width:j+"px",display:"block",whiteSpace:l||"normal"}),this.hasTextWidth=!0;else if(this.hasTextWidth)L(b,{width:"",display:"",whiteSpace:l||"nowrap"}),this.hasTextWidth=!1;this.getSpanCorrection(this.hasTextWidth? +j:b.offsetWidth,k,h,i,g)}L(b,{left:e+(this.xCorr||0)+"px",top:f+(this.yCorr||0)+"px"});if(mb)k=b.offsetHeight;this.cTT=m}}else this.alignOnAdd=!0},setSpanRotation:function(a,b,c){var d={},e=ya?"-ms-transform":mb?"-webkit-transform":Ma?"MozTransform":Lb?"-o-transform":"";d[e]=d.transform="rotate("+a+"deg)";d[e+(Ma?"Origin":"-origin")]=d.transformOrigin=b*100+"% "+c+"px";L(this.element,d)},getSpanCorrection:function(a,b,c){this.xCorr=-a*c;this.yCorr=-b}});u(Ca.prototype,{html:function(a,b,c){var d= +this.createElement("span"),e=d.element,f=d.renderer,g=function(a,b){o(["opacity","visibility"],function(c){fb(a,c+"Setter",function(a,c,d,e){a.call(this,c,d,e);b[d]=c})})};d.textSetter=function(a){a!==e.innerHTML&&delete this.bBox;e.innerHTML=this.textStr=a;d.htmlUpdateTransform()};g(d,d.element.style);d.xSetter=d.ySetter=d.alignSetter=d.rotationSetter=function(a,b){b==="align"&&(b="textAlign");d[b]=a;d.htmlUpdateTransform()};d.attr({text:a,x:A(b),y:A(c)}).css({position:"absolute",fontFamily:this.style.fontFamily, +fontSize:this.style.fontSize});e.style.whiteSpace="nowrap";d.css=d.htmlCss;if(f.isSVG)d.add=function(a){var b,c=f.box.parentNode,j=[];if(this.parentGroup=a){if(b=a.div,!b){for(;a;)j.push(a),a=a.parentGroup;o(j.reverse(),function(a){var d,e=K(a.element,"class");e&&(e={className:e});b=a.div=a.div||Z(La,e,{position:"absolute",left:(a.translateX||0)+"px",top:(a.translateY||0)+"px"},b||c);d=b.style;u(a,{translateXSetter:function(b,c){d.left=b+"px";a[c]=b;a.doTransform=!0},translateYSetter:function(b,c){d.top= +b+"px";a[c]=b;a.doTransform=!0}});g(a,d)})}}else b=c;b.appendChild(e);d.added=!0;d.alignOnAdd&&d.htmlUpdateTransform();return d};return d}});var J;if(!ca&&!ha){J={init:function(a,b){var c=["<",b,' filled="f" stroked="f"'],d=["position: ","absolute",";"],e=b===La;(b==="shape"||e)&&d.push("left:0;top:0;width:1px;height:1px;");d.push("visibility: ",e?"hidden":"visible");c.push(' style="',d.join(""),'"/>');if(b)c=e||b==="span"||b==="img"?c.join(""):a.prepVML(c),this.element=Z(c);this.renderer=a},add:function(a){var b= +this.renderer,c=this.element,d=b.box,e=a&&a.inverted,d=a?a.element||a:d;if(a)this.parentGroup=a;e&&b.invertChild(c,d);d.appendChild(c);this.added=!0;this.alignOnAdd&&!this.deferUpdateTransform&&this.updateTransform();if(this.onAdd)this.onAdd();return this},updateTransform:O.prototype.htmlUpdateTransform,setSpanRotation:function(){var a=this.rotation,b=U(a*ga),c=$(a*ga);L(this.element,{filter:a?["progid:DXImageTransform.Microsoft.Matrix(M11=",b,", M12=",-c,", M21=",c,", M22=",b,", sizingMethod='auto expand')"].join(""): +"none"})},getSpanCorrection:function(a,b,c,d,e){var f=d?U(d*ga):1,g=d?$(d*ga):0,h=p(this.elemHeight,this.element.offsetHeight),i;this.xCorr=f<0&&-a;this.yCorr=g<0&&-h;i=f*g<0;this.xCorr+=g*b*(i?1-c:c);this.yCorr-=f*b*(d?i?c:1-c:1);e&&e!=="left"&&(this.xCorr-=a*c*(f<0?-1:1),d&&(this.yCorr-=h*c*(g<0?-1:1)),L(this.element,{textAlign:e}))},pathToVML:function(a){for(var b=a.length,c=[];b--;)if(ma(a[b]))c[b]=A(a[b]*10)-5;else if(a[b]==="Z")c[b]="x";else if(c[b]=a[b],a.isArc&&(a[b]==="wa"||a[b]==="at"))c[b+ +5]===c[b+7]&&(c[b+7]+=a[b+7]>a[b+5]?1:-1),c[b+6]===c[b+8]&&(c[b+8]+=a[b+8]>a[b+6]?1:-1);return c.join(" ")||"x"},clip:function(a){var b=this,c;a?(c=a.members,oa(c,b),c.push(b),b.destroyClip=function(){oa(c,b)},a=a.getCSS(b)):(b.destroyClip&&b.destroyClip(),a={clip:lb?"inherit":"rect(auto)"});return b.css(a)},css:O.prototype.htmlCss,safeRemoveChild:function(a){a.parentNode&&Ta(a)},destroy:function(){this.destroyClip&&this.destroyClip();return O.prototype.destroy.apply(this)},on:function(a,b){this.element["on"+ +a]=function(){var a=E.event;a.target=a.srcElement;b(a)};return this},cutOffPath:function(a,b){var c,a=a.split(/[ ,]/);c=a.length;if(c===9||c===11)a[c-4]=a[c-2]=C(a[c-2])-10*b;return a.join(" ")},shadow:function(a,b,c){var d=[],e,f=this.element,g=this.renderer,h,i=f.style,k,j=f.path,l,m,n,r;j&&typeof j.value!=="string"&&(j="x");m=j;if(a){n=p(a.width,3);r=(a.opacity||0.15)/n;for(e=1;e<=3;e++){l=n*2+1-2*e;c&&(m=this.cutOffPath(j.value,l+0.5));k=[''];h=Z(g.prepVML(k),null,{left:C(i.left)+p(a.offsetX,1),top:C(i.top)+p(a.offsetY,1)});if(c)h.cutOff=l+1;k=[''];Z(g.prepVML(k),null,null,h);b?b.element.appendChild(h):f.parentNode.insertBefore(h,f);d.push(h)}this.shadows=d}return this},updateShadows:Aa,setAttr:function(a,b){lb?this.element[a]=b:this.element.setAttribute(a,b)},classSetter:function(a){this.element.className=a},dashstyleSetter:function(a, +b,c){(c.getElementsByTagName("stroke")[0]||Z(this.renderer.prepVML([""]),null,null,c))[b]=a||"solid";this[b]=a},dSetter:function(a,b,c){var d=this.shadows,a=a||[];this.d=a.join&&a.join(" ");c.path=a=this.pathToVML(a);if(d)for(c=d.length;c--;)d[c].path=d[c].cutOff?this.cutOffPath(a,d[c].cutOff):a;this.setAttr(b,a)},fillSetter:function(a,b,c){var d=c.nodeName;if(d==="SPAN")c.style.color=a;else if(d!=="IMG")c.filled=a!=="none",this.setAttr("fillcolor",this.renderer.color(a,c,b,this))},"fill-opacitySetter":function(a, +b,c){Z(this.renderer.prepVML(["<",b.split("-")[0],' opacity="',a,'"/>']),null,null,c)},opacitySetter:Aa,rotationSetter:function(a,b,c){c=c.style;this[b]=c[b]=a;c.left=-A($(a*ga)+1)+"px";c.top=A(U(a*ga))+"px"},strokeSetter:function(a,b,c){this.setAttr("strokecolor",this.renderer.color(a,c,b,this))},"stroke-widthSetter":function(a,b,c){c.stroked=!!a;this[b]=a;ma(a)&&(a+="px");this.setAttr("strokeweight",a)},titleSetter:function(a,b){this.setAttr(b,a)},visibilitySetter:function(a,b,c){a==="inherit"&& +(a="visible");this.shadows&&o(this.shadows,function(c){c.style[b]=a});c.nodeName==="DIV"&&(a=a==="hidden"?"-999em":0,lb||(c.style[b]=a?"visible":"hidden"),b="top");c.style[b]=a},xSetter:function(a,b,c){this[b]=a;b==="x"?b="left":b==="y"&&(b="top");this.updateClipping?(this[b]=a,this.updateClipping()):c.style[b]=a},zIndexSetter:function(a,b,c){c.style[b]=a}};J["stroke-opacitySetter"]=J["fill-opacitySetter"];B.VMLElement=J=pa(O,J);J.prototype.ySetter=J.prototype.widthSetter=J.prototype.heightSetter= +J.prototype.xSetter;var Cb={Element:J,isIE8:za.indexOf("MSIE 8.0")>-1,init:function(a,b,c,d){var e;this.alignedObjects=[];d=this.createElement(La).css(u(this.getStyle(d),{position:"relative"}));e=d.element;a.appendChild(d.element);this.isVML=!0;this.box=e;this.boxWrapper=d;this.gradients={};this.cache={};this.cacheKeys=[];this.imgCount=0;this.setSize(b,c,!1);if(!y.namespaces.hcv){y.namespaces.add("hcv","urn:schemas-microsoft-com:vml");try{y.createStyleSheet().cssText="hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "}catch(f){y.styleSheets[0].cssText+= +"hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "}}},isHidden:function(){return!this.box.offsetWidth},clipRect:function(a,b,c,d){var e=this.createElement(),f=Y(a);return u(e,{members:[],count:0,left:(f?a.x:a)+1,top:(f?a.y:b)+1,width:(f?a.width:c)-1,height:(f?a.height:d)-1,getCSS:function(a){var b=a.element,c=b.nodeName,a=a.inverted,d=this.top-(c==="shape"?b.offsetTop:0),e=this.left,b=e+this.width,f=d+this.height,d={clip:"rect("+A(a?e:d)+"px,"+ +A(a?f:b)+"px,"+A(a?b:f)+"px,"+A(a?d:e)+"px)"};!a&&lb&&c==="DIV"&&u(d,{width:b+"px",height:f+"px"});return d},updateClipping:function(){o(e.members,function(a){a.element&&a.css(e.getCSS(a))})}})},color:function(a,b,c,d){var e=this,f,g=/^rgba/,h,i,k="none";a&&a.linearGradient?i="gradient":a&&a.radialGradient&&(i="pattern");if(i){var j,l,m=a.linearGradient||a.radialGradient,n,r,s,p,v,x="",a=a.stops,w,q=[],ba=function(){h=[''];Z(e.prepVML(h),null,null,b)};n=a[0];w=a[a.length-1];n[0]>0&&a.unshift([0,n[1]]);w[0]<1&&a.push([1,w[1]]);o(a,function(a,b){g.test(a[1])?(f=ia(a[1]),j=f.get("rgb"),l=f.get("a")):(j=a[1],l=1);q.push(a[0]*100+"% "+j);b?(s=l,p=j):(r=l,v=j)});if(c==="fill")if(i==="gradient")c=m.x1||m[0]||0,a=m.y1||m[1]||0,n=m.x2||m[2]||0,m=m.y2||m[3]||0,x='angle="'+(90-W.atan((m-a)/(n-c))*180/ra)+'"',ba();else{var k=m.r,t=k*2,u=k*2,A=m.cx,B=m.cy,z=b.radialReference,y,k=function(){z&&(y= +d.getBBox(),A+=(z[0]-y.x)/y.width-0.5,B+=(z[1]-y.y)/y.height-0.5,t*=z[2]/y.width,u*=z[2]/y.height);x='src="'+N.global.VMLRadialGradientURL+'" size="'+t+","+u+'" origin="0.5,0.5" position="'+A+","+B+'" color2="'+v+'" ';ba()};d.added?k():d.onAdd=k;k=p}else k=j}else if(g.test(a)&&b.tagName!=="IMG")f=ia(a),d[c+"-opacitySetter"](f.get("a"),c,b),k=f.get("rgb");else{k=b.getElementsByTagName(c);if(k.length)k[0].opacity=1,k[0].type="solid";k=a}return k},prepVML:function(a){var b=this.isIE8,a=a.join("");b? +(a=a.replace("/>",' xmlns="urn:schemas-microsoft-com:vml" />'),a=a.indexOf('style="')===-1?a.replace("/>",' style="display:inline-block;behavior:url(#default#VML);" />'):a.replace('style="','style="display:inline-block;behavior:url(#default#VML);')):a=a.replace("<","1&&f.attr({x:b,y:c,width:d,height:e});return f},createElement:function(a){return a==="rect"?this.symbol(a):Ca.prototype.createElement.call(this,a)},invertChild:function(a,b){var c=this,d=b.style,e=a.tagName==="IMG"&&a.style;L(a,{flip:"x",left:C(d.width)-(e?C(e.top): +1),top:C(d.height)-(e?C(e.left):1),rotation:-90});o(a.childNodes,function(b){c.invertChild(b,a)})},symbols:{arc:function(a,b,c,d,e){var f=e.start,g=e.end,h=e.r||c||d,c=e.innerR,d=U(f),i=$(f),k=U(g),j=$(g);if(g-f===0)return["x"];f=["wa",a-h,b-h,a+h,b+h,a+h*d,b+h*i,a+h*k,b+h*j];e.open&&!c&&f.push("e","M",a,b);f.push("at",a-c,b-c,a+c,b+c,a+c*k,b+c*j,a+c*d,b+c*i,"x","e");f.isArc=!0;return f},circle:function(a,b,c,d,e){e&&(c=d=2*e.r);e&&e.isCircle&&(a-=c/2,b-=d/2);return["wa",a,b,a+c,b+d,a+c,b+d/2,a+c, +b+d/2,"e"]},rect:function(a,b,c,d,e){return Ca.prototype.symbols[!q(e)||!e.r?"square":"callout"].call(0,a,b,c,d,e)}}};B.VMLRenderer=J=function(){this.init.apply(this,arguments)};J.prototype=D(Ca.prototype,Cb);cb=J}Ca.prototype.measureSpanWidth=function(a,b){var c=y.createElement("span"),d;d=y.createTextNode(a);c.appendChild(d);L(c,b);this.box.appendChild(c);d=c.offsetWidth;Ta(c);return d};var Ob;if(ha)B.CanVGRenderer=J=function(){Fa="http://www.w3.org/1999/xhtml"},J.prototype.symbols={},Ob=function(){function a(){var a= +b.length,d;for(d=0;d0&&c+i*k>e&&(m=A((d-c)/U(h*ga)));else if(d=c+(1-i)*k,c-i*ke&&(j=e-a.x+j*i,l=-1),j=F(b.slotWidth,j),jj||b.autoRotation&&g.styles.width)m=j;if(m){n.width= +m;if(!b.options.labels.style.textOverflow)n.textOverflow="ellipsis";g.css(n)}},getPosition:function(a,b,c,d){var e=this.axis,f=e.chart,g=d&&f.oldChartHeight||f.chartHeight;return{x:a?e.translate(b+c,null,null,d)+e.transB:e.left+e.offset+(e.opposite?(d&&f.oldChartWidth||f.chartWidth)-e.right-e.left:0),y:a?g-e.bottom+e.offset-(e.opposite?e.height:0):g-e.translate(b+c,null,null,d)-e.transB}},getLabelPosition:function(a,b,c,d,e,f,g,h){var i=this.axis,k=i.transA,j=i.reversed,l=i.staggerLines,m=i.tickRotCorr|| +{x:0,y:0},n=e.y;q(n)||(n=i.side===2?m.y+8:n=U(c.rotation*ga)*(m.y-c.getBBox(!1,0).height/2));a=a+e.x+m.x-(f&&d?f*k*(j?-1:1):0);b=b+n-(f&&!d?f*k*(j?1:-1):0);l&&(c=g/(h||1)%l,i.opposite&&(c=l-c-1),b+=c*(i.labelOffset/l));return{x:a,y:A(b)}},getMarkPath:function(a,b,c,d,e,f){return f.crispLine(["M",a,b,"L",a+(e?0:-c),b+(e?c:0)],d)},render:function(a,b,c){var d=this.axis,e=d.options,f=d.chart.renderer,g=d.horiz,h=this.type,i=this.label,k=this.pos,j=e.labels,l=this.gridLine,m=h?h+"Grid":"grid",n=h?h+"Tick": +"tick",r=e[m+"LineWidth"],s=e[m+"LineColor"],o=e[m+"LineDashStyle"],v=e[n+"Length"],m=p(e[n+"Width"],!h&&d.isXAxis?1:0),x=e[n+"Color"],w=e[n+"Position"],n=this.mark,q=j.step,ba=!0,t=d.tickmarkOffset,u=this.getPosition(g,k,t,b),A=u.x,u=u.y,B=g&&A===d.pos+d.len||!g&&u===d.pos?-1:1,c=p(c,1);this.isActive=!0;if(r){k=d.getPlotLinePath(k+t,r*B,b,!0);if(l===z){l={stroke:s,"stroke-width":r};if(o)l.dashstyle=o;if(!h)l.zIndex=1;if(b)l.opacity=0;this.gridLine=l=r?f.path(k).attr(l).add(d.gridGroup):null}if(!b&& +l&&k)l[this.isNew?"attr":"animate"]({d:k,opacity:c})}if(m&&v)w==="inside"&&(v=-v),d.opposite&&(v=-v),h=this.getMarkPath(A,u,v,m*B,g,f),n?n.animate({d:h,opacity:c}):this.mark=f.path(h).attr({stroke:x,"stroke-width":m,opacity:c}).add(d.axisGroup);if(i&&!isNaN(A))i.xy=u=this.getLabelPosition(A,u,i,g,j,t,a,q),this.isFirst&&!this.isLast&&!p(e.showFirstLabel,1)||this.isLast&&!this.isFirst&&!p(e.showLastLabel,1)?ba=!1:g&&!d.isRadial&&!j.step&&!j.rotation&&!b&&c!==0&&this.handleOverflow(u),q&&a%q&&(ba=!1), +ba&&!isNaN(u.y)?(u.opacity=c,i[this.isNew?"attr":"animate"](u),this.isNew=!1):i.attr("y",-9999)},destroy:function(){Sa(this,this.axis)}};B.PlotLineOrBand=function(a,b){this.axis=a;if(b)this.options=b,this.id=b.id};B.PlotLineOrBand.prototype={render:function(){var a=this,b=a.axis,c=b.horiz,d=a.options,e=d.label,f=a.label,g=d.width,h=d.to,i=d.from,k=q(i)&&q(h),j=d.value,l=d.dashStyle,m=a.svgElem,n=[],r,s=d.color,o=p(d.zIndex,0),v=d.events,x={},w=b.chart.renderer;b.isLog&&(i=Da(i),h=Da(h),j=Da(j));if(g){if(n= +b.getPlotLinePath(j,g),x={stroke:s,"stroke-width":g},l)x.dashstyle=l}else if(k){n=b.getPlotBandPath(i,h,d);if(s)x.fill=s;if(d.borderWidth)x.stroke=d.borderColor,x["stroke-width"]=d.borderWidth}else return;x.zIndex=o;if(m)if(n)m.show(),m.animate({d:n});else{if(m.hide(),f)a.label=f=f.destroy()}else if(n&&n.length&&(a.svgElem=m=w.path(n).attr(x).add(),v))for(r in d=function(b){m.on(b,function(c){v[b].apply(a,[c])})},v)d(r);e&&q(e.text)&&n&&n.length&&b.width>0&&b.height>0&&!n.flat?(e=D({align:c&&k&&"center", +x:c?!k&&4:10,verticalAlign:!c&&k&&"middle",y:c?k?16:10:k?6:-4,rotation:c&&!k&&90},e),this.renderLabel(e,n,k,o)):f&&f.hide();return a},renderLabel:function(a,b,c,d){var e=this.label,f=this.axis.chart.renderer;if(!e)e={align:a.textAlign||a.align,rotation:a.rotation},e.zIndex=d,this.label=e=f.text(a.text,0,0,a.useHTML).attr(e).css(a.style).add();d=[b[1],b[4],c?b[6]:b[1]];b=[b[2],b[5],c?b[7]:b[2]];c=Ra(d);f=Ra(b);e.align(a,!1,{x:c,y:f,width:Ea(d)-c,height:Ea(b)-f});e.show()},destroy:function(){oa(this.axis.plotLinesAndBands, +this);delete this.axis;Sa(this)}};var ka=B.Axis=function(){this.init.apply(this,arguments)};ka.prototype={defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S",minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,gridLineColor:"#D8D8D8",labels:{enabled:!0,style:{color:"#606060",cursor:"default",fontSize:"11px"},x:0,y:15},lineColor:"#C0D0E0",lineWidth:1,minPadding:0.01,maxPadding:0.01,minorGridLineColor:"#E0E0E0",minorGridLineWidth:1, +minorTickColor:"#A0A0A0",minorTickLength:2,minorTickPosition:"outside",startOfWeek:1,startOnTick:!1,tickColor:"#C0D0E0",tickLength:10,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",title:{align:"middle",style:{color:"#707070"}},type:"linear"},defaultYAxisOptions:{endOnTick:!0,gridLineWidth:1,tickPixelInterval:72,showLastLabel:!0,labels:{x:-8,y:3},lineWidth:0,maxPadding:0.05,minPadding:0.05,startOnTick:!0,title:{rotation:270,text:"Values"},stackLabels:{enabled:!1,formatter:function(){return B.numberFormat(this.total, +-1)},style:D(aa.line.dataLabels.style,{color:"#000000"})}},defaultLeftAxisOptions:{labels:{x:-15,y:null},title:{rotation:270}},defaultRightAxisOptions:{labels:{x:15,y:null},title:{rotation:90}},defaultBottomAxisOptions:{labels:{autoRotation:[-45],x:0,y:null},title:{rotation:0}},defaultTopAxisOptions:{labels:{autoRotation:[-45],x:0,y:-15},title:{rotation:0}},init:function(a,b){var c=b.isX;this.chart=a;this.horiz=a.inverted?!c:c;this.coll=(this.isXAxis=c)?"xAxis":"yAxis";this.opposite=b.opposite;this.side= +b.side||(this.horiz?this.opposite?0:2:this.opposite?1:3);this.setOptions(b);var d=this.options,e=d.type;this.labelFormatter=d.labels.formatter||this.defaultLabelFormatter;this.userOptions=b;this.minPixelPadding=0;this.reversed=d.reversed;this.visible=d.visible!==!1;this.zoomEnabled=d.zoomEnabled!==!1;this.categories=d.categories||e==="category";this.names=this.names||[];this.isLog=e==="logarithmic";this.isDatetimeAxis=e==="datetime";this.isLinked=q(d.linkedTo);this.ticks={};this.labelEdge=[];this.minorTicks= +{};this.plotLinesAndBands=[];this.alternateBands={};this.len=0;this.minRange=this.userMinRange=d.minRange||d.maxZoom;this.range=d.range;this.offset=d.offset||0;this.stacks={};this.oldStacks={};this.stacksTouched=0;this.min=this.max=null;this.crosshair=p(d.crosshair,ta(a.options.tooltip.crosshairs)[c?0:1],!1);var f,d=this.options.events;sa(this,a.axes)===-1&&(c&&!this.isColorAxis?a.axes.splice(a.xAxis.length,0,this):a.axes.push(this),a[this.coll].push(this));this.series=this.series||[];if(a.inverted&& +c&&this.reversed===z)this.reversed=!0;this.removePlotLine=this.removePlotBand=this.removePlotBandOrLine;for(f in d)M(this,f,d[f]);if(this.isLog)this.val2lin=Da,this.lin2val=na},setOptions:function(a){this.options=D(this.defaultOptions,this.isXAxis?{}:this.defaultYAxisOptions,[this.defaultTopAxisOptions,this.defaultRightAxisOptions,this.defaultBottomAxisOptions,this.defaultLeftAxisOptions][this.side],D(N[this.coll],a))},defaultLabelFormatter:function(){var a=this.axis,b=this.value,c=a.categories,d= +this.dateTimeLabelFormat,e=N.lang.numericSymbols,f=e&&e.length,g,h=a.options.labels.format,a=a.isLog?b:a.tickInterval;if(h)g=Ka(h,this);else if(c)g=b;else if(d)g=Qa(d,b);else if(f&&a>=1E3)for(;f--&&g===z;)c=Math.pow(1E3,f+1),a>=c&&b*10%c===0&&e[f]!==null&&(g=B.numberFormat(b/c,-1)+e[f]);g===z&&(g=P(b)>=1E4?B.numberFormat(b,-1):B.numberFormat(b,-1,z,""));return g},getSeriesExtremes:function(){var a=this,b=a.chart;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=a.threshold=null;a.softThreshold=!a.isXAxis; +a.buildStacks&&a.buildStacks();o(a.series,function(c){if(c.visible||!b.options.chart.ignoreHiddenSeries){var d=c.options,e=d.threshold,f;a.hasVisibleSeries=!0;a.isLog&&e<=0&&(e=null);if(a.isXAxis){if(d=c.xData,d.length)a.dataMin=F(p(a.dataMin,d[0]),Ra(d)),a.dataMax=t(p(a.dataMax,d[0]),Ea(d))}else{c.getExtremes();f=c.dataMax;c=c.dataMin;if(q(c)&&q(f))a.dataMin=F(p(a.dataMin,c),c),a.dataMax=t(p(a.dataMax,f),f);if(q(e))a.threshold=e;if(!d.softThreshold||a.isLog)a.softThreshold=!1}}})},translate:function(a, +b,c,d,e,f){var g=this.linkedParent||this,h=1,i=0,k=d?g.oldTransA:g.transA,d=d?g.oldMin:g.min,j=g.minPixelPadding,e=(g.isOrdinal||g.isBroken||g.isLog&&e)&&g.lin2val;if(!k)k=g.transA;if(c)h*=-1,i=g.len;g.reversed&&(h*=-1,i-=h*(g.sector||g.len));b?(a=a*h+i,a-=j,a=a/k+d,e&&(a=g.lin2val(a))):(e&&(a=g.val2lin(a)),f==="between"&&(f=0.5),a=h*(a-d)*k+i+h*j+(ma(f)?k*f*g.pointRange:0));return a},toPixels:function(a,b){return this.translate(a,!1,!this.horiz,null,!0)+(b?0:this.pos)},toValue:function(a,b){return this.translate(a- +(b?0:this.pos),!0,!this.horiz,null,!0)},getPlotLinePath:function(a,b,c,d,e){var f=this.chart,g=this.left,h=this.top,i,k,j=c&&f.oldChartHeight||f.chartHeight,l=c&&f.oldChartWidth||f.chartWidth,m;i=this.transB;var n=function(a,b,c){if(ac)d?a=F(t(b,a),c):m=!0;return a},e=p(e,this.translate(a,null,null,c)),a=c=A(e+i);i=k=A(j-e-i);isNaN(e)?m=!0:this.horiz?(i=h,k=j-this.bottom,a=c=n(a,g,g+this.width)):(a=g,c=l-this.right,i=k=n(i,h,h+this.height));return m&&!d?null:f.renderer.crispLine(["M",a,i,"L", +c,k],b||1)},getLinearTickPositions:function(a,b,c){var d,e=fa(T(b/a)*a),f=fa(ua(c/a)*a),g=[];if(b===c&&ma(b))return[b];for(b=e;b<=f;){g.push(b);b=fa(b+a);if(b===d)break;d=b}return g},getMinorTickPositions:function(){var a=this.options,b=this.tickPositions,c=this.minorTickInterval,d=[],e,f=this.pointRangePadding||0;e=this.min-f;var f=this.max+f,g=f-e;if(g&&g/c=this.minRange,f,g,h,i,k,j;if(this.isXAxis&&this.minRange===z&&!this.isLog)q(a.min)||q(a.max)?this.minRange=null:(o(this.series,function(a){i=a.xData;for(g=k=a.xIncrement?1:i.length-1;g>0;g--)if(h=i[g]- +i[g-1],f===z||h=n?(s=n,k=0):b.dataMax<=n&&(R=n,i=0)),b.min=p(v,s,b.dataMin),b.max=p(x,R,b.dataMax));if(e)!a&&F(b.min,p(b.dataMin,b.min))<=0&&X(10,1),b.min=fa(Da(b.min),15),b.max=fa(Da(b.max),15);if(b.range&&q(b.max))b.userMin=b.min=v=t(b.min,b.minFromRange()),b.userMax=x=b.max,b.range=null;b.beforePadding&&b.beforePadding();b.adjustForMinRange();if(!m&&!b.axisPointRange&&!b.usePercentage&&!h&&q(b.min)&&q(b.max)&&(c=b.max-b.min))!q(v)&&k&&(b.min-=c*k),!q(x)&& +i&&(b.max+=c*i);if(ma(d.floor))b.min=t(b.min,d.floor);if(ma(d.ceiling))b.max=F(b.max,d.ceiling);if(r&&q(b.dataMin))if(n=n||0,!q(v)&&b.min=n)b.min=n;else if(!q(x)&&b.max>n&&b.dataMax<=n)b.max=n;b.tickInterval=b.min===b.max||b.min===void 0||b.max===void 0?1:h&&!j&&l===b.linkedParent.options.tickPixelInterval?j=b.linkedParent.tickInterval:p(j,this.tickAmount?(b.max-b.min)/t(this.tickAmount-1,1):void 0,m?1:(b.max-b.min)*l/t(b.len,l));g&&!a&&o(b.series,function(a){a.processData(b.min!==b.oldMin|| +b.max!==b.oldMax)});b.setAxisTranslation(!0);b.beforeSetTickPositions&&b.beforeSetTickPositions();if(b.postProcessTickInterval)b.tickInterval=b.postProcessTickInterval(b.tickInterval);if(b.pointRange&&!j)b.tickInterval=t(b.pointRange,b.tickInterval);a=p(d.minTickInterval,b.isDatetimeAxis&&b.closestPointRange);if(!j&&b.tickInterval0.5&&b.tickInterval<5&&b.max>1E3&&b.max<9999)), +!!this.tickAmount);if(!this.tickAmount&&this.len)b.tickInterval=b.unsquish();this.setTickPositions()},setTickPositions:function(){var a=this.options,b,c=a.tickPositions,d=a.tickPositioner,e=a.startOnTick,f=a.endOnTick,g;this.tickmarkOffset=this.categories&&a.tickmarkPlacement==="between"&&this.tickInterval===1?0.5:0;this.minorTickInterval=a.minorTickInterval==="auto"&&this.tickInterval?this.tickInterval/5:a.minorTickInterval;this.tickPositions=b=c&&c.slice();if(!b&&(b=this.isDatetimeAxis?this.getTimeTicks(this.normalizeTimeTickInterval(this.tickInterval, +a.units),this.min,this.max,a.startOfWeek,this.ordinalPositions,this.closestPointRange,!0):this.isLog?this.getLogTickPositions(this.tickInterval,this.min,this.max):this.getLinearTickPositions(this.tickInterval,this.min,this.max),b.length>this.len&&(b=[b[0],b.pop()]),this.tickPositions=b,d&&(d=d.apply(this,[this.min,this.max]))))this.tickPositions=b=d;if(!this.isLinked)this.trimTicks(b,e,f),this.min===this.max&&q(this.min)&&!this.tickAmount&&(g=!0,this.min-=0.5,this.max+=0.5),this.single=g,!c&&!d&& +this.adjustTickAmount()},trimTicks:function(a,b,c){var d=a[0],e=a[a.length-1],f=this.minPointOffset||0;if(b)this.min=d;else for(;this.min-f>a[0];)a.shift();if(c)this.max=e;else for(;this.max+fc&&(this.tickInterval*=2,this.setTickPositions());if(q(d)){for(a=c=b.length;a--;)(d===3&&a%2===1||d<=2&&a>0&&a=e&&(b=e));this.displayBtn=a!==z||b!==z;this.setExtremes(a,b,!1,z,{trigger:"zoom"});return!0},setAxisSize:function(){var a=this.chart,b=this.options,c=b.offsetLeft||0,d=this.horiz,e=p(b.width,a.plotWidth-c+(b.offsetRight||0)),f=p(b.height,a.plotHeight),g=p(b.top,a.plotTop),b=p(b.left,a.plotLeft+c),c=/%$/;c.test(f)&&(f=Math.round(parseFloat(f)/ +100*a.plotHeight));c.test(g)&&(g=Math.round(parseFloat(g)/100*a.plotHeight+a.plotTop));this.left=b;this.top=g;this.width=e;this.height=f;this.bottom=a.chartHeight-f-g;this.right=a.chartWidth-e-b;this.len=t(d?e:f,0);this.pos=d?b:g},getExtremes:function(){var a=this.isLog;return{min:a?fa(na(this.min)):this.min,max:a?fa(na(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin,userMax:this.userMax}},getThreshold:function(a){var b=this.isLog,c=b?na(this.min):this.min,b=b?na(this.max): +this.max;a===null?a=b<0?b:c:c>a?a=c:b15&&a<165?"right":a>195&&a<345?"left":"center"},unsquish:function(){var a=this.ticks,b=this.options.labels,c=this.horiz,d=this.tickInterval,e=d,f=this.len/(((this.categories?1:0)+this.max-this.min)/d),g,h=b.rotation,i=this.chart.renderer.fontMetrics(b.style.fontSize,a[0]&&a[0].label),k,j=Number.MAX_VALUE,l,m=function(a){a/=f||1;a=a>1?ua(a):1;return a* +d};c?(l=!b.staggerLines&&!b.step&&(q(h)?[h]:f=-90&&a<=90)k=m(P(i.h/$(ga*a))),b=k+P(a/360),bm)m=a.labelLength}),m>i&&m>h.h?k.rotation=this.labelRotation:this.labelRotation=0;else if(g&&(l={width:i+"px"},!j)){l.textOverflow="clip";for(n=c.length;!f&&n--;)if(r=c[n],i=d[r].label)if(i.styles.textOverflow=== +"ellipsis"&&i.css({textOverflow:"clip"}),i.getBBox().height>this.len/c.length-(h.h-h.f)||d[r].labelLength>g)i.specCss={textOverflow:"ellipsis"}}if(k.rotation&&(l={width:(m>a.chartHeight*0.5?a.chartHeight*0.33:a.chartHeight)+"px"},!j))l.textOverflow="ellipsis";if(this.labelAlign=e.align||this.autoLabelAlign(this.labelRotation))k.align=this.labelAlign;o(c,function(a){var b=(a=d[a])&&a.label;if(b)b.attr(k),l&&b.css(D(l,b.specCss)),delete b.specCss,a.rotation=k.rotation});this.tickRotCorr=b.rotCorr(h.b, +this.labelRotation||0,this.side!==0)},hasData:function(){return this.hasVisibleSeries||q(this.min)&&q(this.max)&&!!this.tickPositions},getOffset:function(){var a=this,b=a.chart,c=b.renderer,d=a.options,e=a.tickPositions,f=a.ticks,g=a.horiz,h=a.side,i=b.inverted?[1,0,3,2][h]:h,k,j,l=0,m,n=0,r=d.title,s=d.labels,R=0,v=a.opposite,x=b.axisOffset,b=b.clipOffset,w=[-1,1,1,-1][h],u,ba=a.axisParent;k=a.hasData();a.showAxis=j=k||p(d.showEmpty,!0);a.staggerLines=a.horiz&&s.staggerLines;if(!a.axisGroup)a.gridGroup= +c.g("grid").attr({zIndex:d.gridZIndex||1}).add(ba),a.axisGroup=c.g("axis").attr({zIndex:d.zIndex||2}).add(ba),a.labelGroup=c.g("axis-labels").attr({zIndex:s.zIndex||7}).addClass("highcharts-"+a.coll.toLowerCase()+"-labels").add(ba);if(k||a.isLinked){if(o(e,function(b){f[b]?f[b].addLabel():f[b]=new Va(a,b)}),a.renderUnsquish(),s.reserveSpace!==!1&&(h===0||h===2||{1:"left",3:"right"}[h]===a.labelAlign||a.labelAlign==="center")&&o(e,function(a){R=t(f[a].getLabelSize(),R)}),a.staggerLines)R*=a.staggerLines, +a.labelOffset=R*(a.opposite?-1:1)}else for(u in f)f[u].destroy(),delete f[u];if(r&&r.text&&r.enabled!==!1){if(!a.axisTitle)a.axisTitle=c.text(r.text,0,0,r.useHTML).attr({zIndex:7,rotation:r.rotation||0,align:r.textAlign||{low:v?"right":"left",middle:"center",high:v?"left":"right"}[r.align]}).addClass("highcharts-"+this.coll.toLowerCase()+"-title").css(r.style).add(a.axisGroup),a.axisTitle.isNew=!0;if(j)l=a.axisTitle.getBBox()[g?"height":"width"],m=r.offset,n=q(m)?0:p(r.margin,g?5:10);a.axisTitle[j? +"show":"hide"](!0)}a.offset=w*p(d.offset,x[h]);a.tickRotCorr=a.tickRotCorr||{x:0,y:0};c=h===2?a.tickRotCorr.y:0;g=Math.abs(R)+n+(R&&w*(g?p(s.y,a.tickRotCorr.y+8):s.x)-c);a.axisTitleMargin=p(m,g);x[h]=t(x[h],a.axisTitleMargin+l+w*a.offset,g);d=d.offset?0:T(d.lineWidth/2)*2;b[i]=t(b[i],d)},getLinePath:function(a){var b=this.chart,c=this.opposite,d=this.offset,e=this.horiz,f=this.left+(c?this.width:0)+d,d=b.chartHeight-this.bottom-(c?this.height:0)+d;c&&(a*=-1);return b.renderer.crispLine(["M",e?this.left: +f,e?d:this.top,"L",e?b.chartWidth-this.right:f,e?d:b.chartHeight-this.bottom],a)},getTitlePosition:function(){var a=this.horiz,b=this.left,c=this.top,d=this.len,e=this.options.title,f=a?b:c,g=this.opposite,h=this.offset,i=e.x||0,k=e.y||0,j=C(e.style.fontSize||12),d={low:f+(a?0:d),middle:f+d/2,high:f+(a?d:0)}[e.align],b=(a?c+this.height:b)+(a?1:-1)*(g?-1:1)*this.axisTitleMargin+(this.side===2?j:0);return{x:a?d+i:b+(g?this.width:0)+h+i,y:a?b+k-(g?this.height:0)+h:d+k}},render:function(){var a=this, +b=a.chart,c=b.renderer,d=a.options,e=a.isLog,f=a.isLinked,g=a.tickPositions,h=a.axisTitle,i=a.ticks,k=a.minorTicks,j=a.alternateBands,l=d.stackLabels,m=d.alternateGridColor,n=a.tickmarkOffset,r=d.lineWidth,s,p=b.hasRendered&&q(a.oldMin)&&!isNaN(a.oldMin),v=a.showAxis,x=c.globalAnimation,w,t;a.labelEdge.length=0;a.overlap=!1;o([i,k,j],function(a){for(var b in a)a[b].isActive=!1});if(a.hasData()||f){a.minorTickInterval&&!a.categories&&o(a.getMinorTickPositions(),function(b){k[b]||(k[b]=new Va(a,b,"minor")); +p&&k[b].isNew&&k[b].render(null,!0);k[b].render(null,!1,1)});if(g.length&&(o(g,function(b,c){if(!f||b>=a.min&&b<=a.max)i[b]||(i[b]=new Va(a,b)),p&&i[b].isNew&&i[b].render(c,!0,0.1),i[b].render(c)}),n&&(a.min===0||a.single)))i[-1]||(i[-1]=new Va(a,-1,null,!0)),i[-1].render(-1);m&&o(g,function(c,d){t=g[d+1]!==z?g[d+1]+n:a.max-n;if(d%2===0&&c=G.second?0:j*T(i.getMilliseconds()/j));if(k>=G.second)i[Hb](k>=G.minute?0:j*T(i.getSeconds()/j));if(k>=G.minute)i[Ib](k>=G.hour?0:j*T(i[tb]()/j));if(k>=G.hour)i[Jb](k>=G.day?0:j*T(i[ub]()/j));if(k>=G.day)i[wb](k>=G.month? +1:j*T(i[$a]()/j));k>=G.month&&(i[xb](k>=G.year?0:j*T(i[ab]()/j)),h=i[bb]());k>=G.year&&(h-=h%j,i[yb](h));if(k===G.week)i[wb](i[$a]()-i[vb]()+p(d,1));b=1;if(qb||Za)i=i.getTime(),i=new qa(i+Ya(i));h=i[bb]();for(var d=i.getTime(),l=i[ab](),m=i[$a](),n=!g||!!Za,r=(G.day+(g?Ya(i):i.getTimezoneOffset()*6E4))%G.day;d=0.5)a=A(a),g=this.getLinearTickPositions(a,b,c);else if(a>=0.08)for(var f=T(b),h,i,k,j,l,e=a>0.3?[1,2,4]:a>0.15?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9];fb&&(!d||j<=c)&&j!==z&&g.push(j),j>c&&(l=!0),j=k}else if(b=na(b),c=na(c),a=e[d? +"minorTickInterval":"tickInterval"],a=p(a==="auto"?null:a,this._minorAutoInterval,(c-b)*(e.tickPixelInterval/(d?5:1))/((d?f/this.tickPositions.length:f)||1)),a=sb(a,null,rb(a)),g=Ba(this.getLinearTickPositions(a,b,c),Da),!d)this._minorAutoInterval=a/5;if(!d)this.tickInterval=a;return g};var Pb=B.Tooltip=function(){this.init.apply(this,arguments)};Pb.prototype={init:function(a,b){var c=b.borderWidth,d=b.style,e=C(d.padding);this.chart=a;this.options=b;this.crosshairs=[];this.now={x:0,y:0};this.isHidden= +!0;this.label=a.renderer.label("",0,0,b.shape||"callout",null,null,b.useHTML,null,"tooltip").attr({padding:e,fill:b.backgroundColor,"stroke-width":c,r:b.borderRadius,zIndex:8}).css(d).css({padding:0}).add().attr({y:-9999});ha||this.label.shadow(b.shadow);this.shared=b.shared},destroy:function(){if(this.label)this.label=this.label.destroy();clearTimeout(this.hideTimer);clearTimeout(this.tooltipTimeout)},move:function(a,b,c,d){var e=this,f=e.now,g=e.options.animation!==!1&&!e.isHidden&&(P(a-f.x)>1|| +P(b-f.y)>1),h=e.followPointer||e.len>1;u(f,{x:g?(2*f.x+a)/3:a,y:g?(f.y+b)/2:b,anchorX:h?z:g?(2*f.anchorX+c)/3:c,anchorY:h?z:g?(f.anchorY+d)/2:d});e.label.attr(f);if(g)clearTimeout(this.tooltipTimeout),this.tooltipTimeout=setTimeout(function(){e&&e.move(a,b,c,d)},32)},hide:function(a){var b=this;clearTimeout(this.hideTimer);a=p(a,this.options.hideDelay,500);if(!this.isHidden)this.hideTimer=Pa(function(){b.label[a?"fadeOut":"hide"]();b.isHidden=!0},a)},getAnchor:function(a,b){var c,d=this.chart,e=d.inverted, +f=d.plotTop,g=d.plotLeft,h=0,i=0,k,j,a=ta(a);c=a[0].tooltipPos;this.followPointer&&b&&(b.chartX===z&&(b=d.pointer.normalize(b)),c=[b.chartX-d.plotLeft,b.chartY-f]);c||(o(a,function(a){k=a.series.yAxis;j=a.series.xAxis;h+=a.plotX+(!e&&j?j.left-g:0);i+=(a.plotLow?(a.plotLow+a.plotHigh)/2:a.plotY)+(!e&&k?k.top-f:0)}),h/=a.length,i/=a.length,c=[e?d.plotWidth-i:h,this.shared&&!e&&a.length>1&&b?b.chartY-f:e?d.plotHeight-h:i]);return Ba(c,A)},getPosition:function(a,b,c){var d=this.chart,e=this.distance, +f={},g=c.h||0,h,i=["y",d.chartHeight,b,c.plotY+d.plotTop,d.plotTop,d.plotTop+d.plotHeight],k=["x",d.chartWidth,a,c.plotX+d.plotLeft,d.plotLeft,d.plotLeft+d.plotWidth],j=p(c.ttBelow,d.inverted&&!c.negative||!d.inverted&&c.negative),l=function(a,b,c,d,h,i){var k=cb?d:d+g);else return!1},m=function(a,b,c,d){var g;db-e?g=!1:f[a]=db-c/2?b-c-2:d-c/2;return g},n=function(a){var b= +i;i=k;k=b;h=a},r=function(){l.apply(0,i)!==!1?m.apply(0,k)===!1&&!h&&(n(!0),r()):h?f.x=f.y=0:(n(!0),r())};(d.inverted||this.len>1)&&n();r();return f},defaultFormatter:function(a){var b=this.points||ta(this),c;c=[a.tooltipFooterHeaderFormatter(b[0])];c=c.concat(a.bodyFormatter(b));c.push(a.tooltipFooterHeaderFormatter(b[0],!0));return c.join("")},refresh:function(a,b){var c=this.chart,d=this.label,e=this.options,f,g,h,i={},k,j=[];k=e.formatter||this.defaultFormatter;var i=c.hoverPoints,l,m=this.shared; +clearTimeout(this.hideTimer);this.followPointer=ta(a)[0].series.tooltipOptions.followPointer;h=this.getAnchor(a,b);f=h[0];g=h[1];m&&(!a.series||!a.series.noSharedTooltip)?(c.hoverPoints=a,i&&o(i,function(a){a.setState()}),o(a,function(a){a.setState("hover");j.push(a.getLabelConfig())}),i={x:a[0].category,y:a[0].y},i.points=j,this.len=j.length,a=a[0]):i=a.getLabelConfig();k=k.call(i,this);i=a.series;this.distance=p(i.tooltipOptions.distance,16);k===!1?this.hide():(this.isHidden&&(Oa(d),d.attr("opacity", +1).show()),d.attr({text:k}),l=e.borderColor||a.color||i.color||"#606060",d.attr({stroke:l}),this.updatePosition({plotX:f,plotY:g,negative:a.negative,ttBelow:a.ttBelow,h:h[2]||0}),this.isHidden=!1);H(c,"tooltipRefresh",{text:k,x:f+c.plotLeft,y:g+c.plotTop,borderColor:l})},updatePosition:function(a){var b=this.chart,c=this.label,c=(this.options.positioner||this.getPosition).call(this,c.width,c.height,a);this.move(A(c.x),A(c.y||0),a.plotX+b.plotLeft,a.plotY+b.plotTop)},getXDateFormat:function(a,b,c){var d, +b=b.dateTimeLabelFormats,e=c&&c.closestPointRange,f,g={millisecond:15,second:12,minute:9,hour:6,day:3},h,i="millisecond";if(e){h=Qa("%m-%d %H:%M:%S.%L",a.x);for(f in G){if(e===G.week&&+Qa("%w",a.x)===c.options.startOfWeek&&h.substr(6)==="00:00:00.000"){f="week";break}if(G[f]>e){f=i;break}if(g[f]&&h.substr(g[f])!=="01-01 00:00:00.000".substr(g[f]))break;f!=="week"&&(i=f)}f&&(d=b[f])}else d=b.day;return d||b.year},tooltipFooterHeaderFormatter:function(a,b){var c=b?"footer":"header",d=a.series,e=d.tooltipOptions, +f=e.xDateFormat,g=d.xAxis,h=g&&g.options.type==="datetime"&&ma(a.key),c=e[c+"Format"];h&&!f&&(f=this.getXDateFormat(a,e,g));h&&f&&(c=c.replace("{point.key}","{point.key:"+f+"}"));return Ka(c,{point:a,series:d})},bodyFormatter:function(a){return Ba(a,function(a){var c=a.series.tooltipOptions;return(c.pointFormatter||a.point.tooltipFormatter).call(a.point,c.pointFormat)})}};var ea;db=y&&y.documentElement.ontouchstart!==z;var Xa=B.Pointer=function(a,b){this.init(a,b)};Xa.prototype={init:function(a,b){var c= +b.chart,d=c.events,e=ha?"":c.zoomType,c=a.inverted,f;this.options=b;this.chart=a;this.zoomX=f=/x/.test(e);this.zoomY=e=/y/.test(e);this.zoomHor=f&&!c||e&&c;this.zoomVert=e&&!c||f&&c;this.hasZoom=f||e;this.runChartClick=d&&!!d.click;this.pinchDown=[];this.lastValidTouch={};if(B.Tooltip&&b.tooltip.enabled)a.tooltip=new Pb(a,b.tooltip),this.followTouchMove=p(b.tooltip.followTouchMove,!0);this.setDOMEvents()},normalize:function(a,b){var c,d,a=a||E.event;if(!a.target)a.target=a.srcElement;d=a.touches? +a.touches.length?a.touches.item(0):a.changedTouches[0]:a;if(!b)this.chartPosition=b=Ab(this.chart.container);d.pageX===z?(c=t(a.x,a.clientX-b.left),d=a.y):(c=d.pageX-b.left,d=d.pageY-b.top);return u(a,{chartX:A(c),chartY:A(d)})},getCoordinates:function(a){var b={xAxis:[],yAxis:[]};o(this.chart.axes,function(c){b[c.isXAxis?"xAxis":"yAxis"].push({axis:c,value:c.toValue(a[c.horiz?"chartX":"chartY"])})});return b},runPointActions:function(a){var b=this.chart,c=b.series,d=b.tooltip,e=d?d.shared:!1,f=b.hoverPoint, +g=b.hoverSeries,h=[Number.MAX_VALUE,Number.MAX_VALUE],i,k,j=[],l=[],m;if(!e&&!g)for(b=0;bh+k&&(d=h+k),ei+j&&(e=i+j),this.hasDragged=Math.sqrt(Math.pow(n-d,2)+Math.pow(r-e,2)),this.hasDragged>10){l=b.isInsidePlot(n-h,r-i);if(b.hasCartesianSeries&&(this.zoomX||this.zoomY)&&l&&!s&&!m)this.selectionMarker=m=b.renderer.rect(h,i,f?1:k,g?1:j,0).attr({fill:c.selectionMarkerFill||"rgba(69,114,167,0.25)",zIndex:7}).add();m&&f&&(d-=n,m.attr({width:P(d),x:(d>0?0:d)+n})); +m&&g&&(d=e-r,m.attr({height:P(d),y:(d>0?0:d)+r}));l&&!m&&c.panning&&b.pan(a,c.panning)}},drop:function(a){var b=this,c=this.chart,d=this.hasPinched;if(this.selectionMarker){var e={originalEvent:a,xAxis:[],yAxis:[]},f=this.selectionMarker,g=f.attr?f.attr("x"):f.x,h=f.attr?f.attr("y"):f.y,i=f.attr?f.attr("width"):f.width,k=f.attr?f.attr("height"):f.height,j;if(this.hasDragged||d)o(c.axes,function(c){if(c.zoomEnabled&&q(c.min)&&(d||b[{xAxis:"zoomX",yAxis:"zoomY"}[c.coll]])){var f=c.horiz,n=a.type=== +"touchend"?c.minPixelPadding:0,r=c.toValue((f?g:h)+n),f=c.toValue((f?g+i:h+k)-n);e[c.coll].push({axis:c,min:F(r,f),max:t(r,f)});j=!0}}),j&&H(c,"selection",e,function(a){c.zoom(u(a,d?{animation:!1}:null))});this.selectionMarker=this.selectionMarker.destroy();d&&this.scaleGroups()}if(c)L(c.container,{cursor:c._cursor}),c.cancelClick=this.hasDragged>10,c.mouseIsDown=this.hasDragged=this.hasPinched=!1,this.pinchDown=[]},onContainerMouseDown:function(a){a=this.normalize(a);a.preventDefault&&a.preventDefault(); +this.dragStart(a)},onDocumentMouseUp:function(a){S[ea]&&S[ea].pointer.drop(a)},onDocumentMouseMove:function(a){var b=this.chart,c=this.chartPosition,a=this.normalize(a,c);c&&!this.inClass(a.target,"highcharts-tracker")&&!b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)&&this.reset()},onContainerMouseLeave:function(a){var b=S[ea];if(b&&(a.relatedTarget||a.toElement))b.pointer.reset(),b.pointer.chartPosition=null},onContainerMouseMove:function(a){var b=this.chart;if(!q(ea)||!S[ea]||!S[ea].mouseIsDown)ea= +b.index;a=this.normalize(a);a.returnValue=!1;b.mouseIsDown==="mousedown"&&this.drag(a);(this.inClass(a.target,"highcharts-tracker")||b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop))&&!b.openMenu&&this.runPointActions(a)},inClass:function(a,b){for(var c;a;){if(c=K(a,"class")){if(c.indexOf(b)!==-1)return!0;if(c.indexOf("highcharts-container")!==-1)return!1}a=a.parentNode}},onTrackerMouseOut:function(a){var b=this.chart.hoverSeries,a=a.relatedTarget||a.toElement;if(b&&a&&!b.options.stickyTracking&& +!this.inClass(a,"highcharts-tooltip")&&!this.inClass(a,"highcharts-series-"+b.index))b.onMouseOut()},onContainerClick:function(a){var b=this.chart,c=b.hoverPoint,d=b.plotLeft,e=b.plotTop,a=this.normalize(a);b.cancelClick||(c&&this.inClass(a.target,"highcharts-tracker")?(H(c.series,"click",u(a,{point:c})),b.hoverPoint&&c.firePointEvent("click",a)):(u(a,this.getCoordinates(a)),b.isInsidePlot(a.chartX-d,a.chartY-e)&&H(b,"click",a)))},setDOMEvents:function(){var a=this,b=a.chart.container;b.onmousedown= +function(b){a.onContainerMouseDown(b)};b.onmousemove=function(b){a.onContainerMouseMove(b)};b.onclick=function(b){a.onContainerClick(b)};M(b,"mouseleave",a.onContainerMouseLeave);eb===1&&M(y,"mouseup",a.onDocumentMouseUp);if(db)b.ontouchstart=function(b){a.onContainerTouchStart(b)},b.ontouchmove=function(b){a.onContainerTouchMove(b)},eb===1&&M(y,"touchend",a.onDocumentTouchEnd)},destroy:function(){var a;V(this.chart.container,"mouseleave",this.onContainerMouseLeave);eb||(V(y,"mouseup",this.onDocumentMouseUp), +V(y,"touchend",this.onDocumentTouchEnd));clearInterval(this.tooltipTimeout);for(a in this)this[a]=null}};u(B.Pointer.prototype,{pinchTranslate:function(a,b,c,d,e,f){(this.zoomHor||this.pinchHor)&&this.pinchTranslateDirection(!0,a,b,c,d,e,f);(this.zoomVert||this.pinchVert)&&this.pinchTranslateDirection(!1,a,b,c,d,e,f)},pinchTranslateDirection:function(a,b,c,d,e,f,g,h){var i=this.chart,k=a?"x":"y",j=a?"X":"Y",l="chart"+j,m=a?"width":"height",n=i["plot"+(a?"Left":"Top")],r,s,p=h||1,o=i.inverted,x=i.bounds[a? +"h":"v"],w=b.length===1,q=b[0][l],t=c[0][l],u=!w&&b[1][l],A=!w&&c[1][l],B,c=function(){!w&&P(q-u)>20&&(p=h||P(t-A)/P(q-u));s=(n-t)/p+q;r=i["plot"+(a?"Width":"Height")]/p};c();b=s;bx.max&&(b=x.max-r,B=!0);B?(t-=0.8*(t-g[k][0]),w||(A-=0.8*(A-g[k][1])),c()):g[k]=[t,A];o||(f[k]=s-n,f[m]=r);f=o?1/p:p;e[m]=r;e[k]=b;d[o?a?"scaleY":"scaleX":"scale"+j]=p;d["translate"+j]=f*n+(t-f*q)},pinch:function(a){var b=this,c=b.chart,d=b.pinchDown,e=a.touches,f=e.length,g=b.lastValidTouch,h= +b.hasZoom,i=b.selectionMarker,k={},j=f===1&&(b.inClass(a.target,"highcharts-tracker")&&c.runTrackerClick||b.runChartClick),l={};if(f>1)b.initiated=!0;h&&b.initiated&&!j&&a.preventDefault();Ba(e,function(a){return b.normalize(a)});if(a.type==="touchstart")o(e,function(a,b){d[b]={chartX:a.chartX,chartY:a.chartY}}),g.x=[d[0].chartX,d[1]&&d[1].chartX],g.y=[d[0].chartY,d[1]&&d[1].chartY],o(c.axes,function(a){if(a.zoomEnabled){var b=c.bounds[a.horiz?"h":"v"],d=a.minPixelPadding,e=a.toPixels(p(a.options.min, +a.dataMin)),f=a.toPixels(p(a.options.max,a.dataMax)),g=F(e,f),e=t(e,f);b.min=F(a.pos,g-d);b.max=t(a.pos+a.len,e+d)}}),b.res=!0;else if(d.length){if(!i)b.selectionMarker=i=u({destroy:Aa,touch:!0},c.plotBox);b.pinchTranslate(d,e,k,i,l,g);b.hasPinched=h;b.scaleGroups(k,l);if(!h&&b.followTouchMove&&f===1)this.runPointActions(b.normalize(a));else if(b.res)b.res=!1,this.reset(!1,0)}},touch:function(a,b){var c=this.chart;ea=c.index;a.touches.length===1?(a=this.normalize(a),c.isInsidePlot(a.chartX-c.plotLeft, +a.chartY-c.plotTop)&&!c.openMenu?(b&&this.runPointActions(a),this.pinch(a)):b&&this.reset()):a.touches.length===2&&this.pinch(a)},onContainerTouchStart:function(a){this.touch(a,!0)},onContainerTouchMove:function(a){this.touch(a)},onDocumentTouchEnd:function(a){S[ea]&&S[ea].pointer.drop(a)}});if(E.PointerEvent||E.MSPointerEvent){var va={},Db=!!E.PointerEvent,Sb=function(){var a,b=[];b.item=function(a){return this[a]};for(a in va)va.hasOwnProperty(a)&&b.push({pageX:va[a].pageX,pageY:va[a].pageY,target:va[a].target}); +return b},Eb=function(a,b,c,d){if((a.pointerType==="touch"||a.pointerType===a.MSPOINTER_TYPE_TOUCH)&&S[ea])d(a),d=S[ea].pointer,d[b]({type:c,target:a.currentTarget,preventDefault:Aa,touches:Sb()})};u(Xa.prototype,{onContainerPointerDown:function(a){Eb(a,"onContainerTouchStart","touchstart",function(a){va[a.pointerId]={pageX:a.pageX,pageY:a.pageY,target:a.currentTarget}})},onContainerPointerMove:function(a){Eb(a,"onContainerTouchMove","touchmove",function(a){va[a.pointerId]={pageX:a.pageX,pageY:a.pageY}; +if(!va[a.pointerId].target)va[a.pointerId].target=a.currentTarget})},onDocumentPointerUp:function(a){Eb(a,"onDocumentTouchEnd","touchend",function(a){delete va[a.pointerId]})},batchMSEvents:function(a){a(this.chart.container,Db?"pointerdown":"MSPointerDown",this.onContainerPointerDown);a(this.chart.container,Db?"pointermove":"MSPointerMove",this.onContainerPointerMove);a(y,Db?"pointerup":"MSPointerUp",this.onDocumentPointerUp)}});fb(Xa.prototype,"init",function(a,b,c){a.call(this,b,c);this.hasZoom&& +L(b.container,{"-ms-touch-action":"none","touch-action":"none"})});fb(Xa.prototype,"setDOMEvents",function(a){a.apply(this);(this.hasZoom||this.followTouchMove)&&this.batchMSEvents(M)});fb(Xa.prototype,"destroy",function(a){this.batchMSEvents(V);a.call(this)})}var ob=B.Legend=function(a,b){this.init(a,b)};ob.prototype={init:function(a,b){var c=this,d=b.itemStyle,e=b.itemMarginTop||0;this.options=b;if(b.enabled)c.itemStyle=d,c.itemHiddenStyle=D(d,b.itemHiddenStyle),c.itemMarginTop=e,c.padding=d=p(b.padding, +8),c.initialItemX=d,c.initialItemY=d-5,c.maxItemWidth=0,c.chart=a,c.itemHeight=0,c.symbolWidth=p(b.symbolWidth,16),c.pages=[],c.render(),M(c.chart,"endResize",function(){c.positionCheckboxes()})},colorizeItem:function(a,b){var c=this.options,d=a.legendItem,e=a.legendLine,f=a.legendSymbol,g=this.itemHiddenStyle.color,c=b?c.itemStyle.color:g,h=b?a.legendColor||a.color||"#CCC":g,g=a.options&&a.options.marker,i={fill:h},k;d&&d.css({fill:c,color:c});e&&e.attr({stroke:h});if(f){if(g&&f.isMarker)for(k in i.stroke= +h,g=a.convertAttribs(g),g)d=g[k],d!==z&&(i[k]=d);f.attr(i)}},positionItem:function(a){var b=this.options,c=b.symbolPadding,b=!b.rtl,d=a._legendItemPos,e=d[0],d=d[1],f=a.checkbox;(a=a.legendGroup)&&a.element&&a.translate(b?e:this.legendWidth-e-2*c-4,d);if(f)f.x=e,f.y=d},destroyItem:function(a){var b=a.checkbox;o(["legendItem","legendLine","legendSymbol","legendGroup"],function(b){a[b]&&(a[b]=a[b].destroy())});b&&Ta(a.checkbox)},destroy:function(){var a=this.group,b=this.box;if(b)this.box=b.destroy(); +if(a)this.group=a.destroy()},positionCheckboxes:function(a){var b=this.group.alignAttr,c,d=this.clipHeight||this.legendHeight,e=this.titleHeight;if(b)c=b.translateY,o(this.allItems,function(f){var g=f.checkbox,h;g&&(h=c+e+g.y+(a||0)+3,L(g,{left:b.translateX+f.checkboxOffset+g.x-20+"px",top:h+"px",display:h>c-6&&h(m||b.chartWidth-2*k-s-d.x))this.itemX=s,this.itemY+=r+this.lastLineHeight+n,this.lastLineHeight=0;this.maxItemWidth=t(this.maxItemWidth, +f);this.lastItemY=r+this.itemY+n;this.lastLineHeight=t(g,this.lastLineHeight);a._legendItemPos=[this.itemX,this.itemY];e?this.itemX+=f:(this.itemY+=r+g+n,this.lastLineHeight=g);this.offsetWidth=m||t((e?this.itemX-s-j:f)+k,this.offsetWidth)},getAllItems:function(){var a=[];o(this.chart.series,function(b){var c=b.options;if(p(c.showInLegend,!q(c.linkedTo)?z:!1,!0))a=a.concat(b.legendItems||(c.legendType==="point"?b.data:b))});return a},adjustMargins:function(a,b){var c=this.chart,d=this.options,e=d.align.charAt(0)+ +d.verticalAlign.charAt(0)+d.layout.charAt(0);this.display&&!d.floating&&o([/(lth|ct|rth)/,/(rtv|rm|rbv)/,/(rbh|cb|lbh)/,/(lbv|lm|ltv)/],function(f,g){f.test(e)&&!q(a[g])&&(c[nb[g]]=t(c[nb[g]],c.legend[(g+1)%2?"legendHeight":"legendWidth"]+[1,-1,-1,1][g]*d[g%2?"x":"y"]+p(d.margin,12)+b[g]))})},render:function(){var a=this,b=a.chart,c=b.renderer,d=a.group,e,f,g,h,i=a.box,k=a.options,j=a.padding,l=k.borderWidth,m=k.backgroundColor;a.itemX=a.initialItemX;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY= +0;if(!d)a.group=d=c.g("legend").attr({zIndex:7}).add(),a.contentGroup=c.g().attr({zIndex:1}).add(d),a.scrollGroup=c.g().add(a.contentGroup);a.renderTitle();e=a.getAllItems();ib(e,function(a,b){return(a.options&&a.options.legendIndex||0)-(b.options&&b.options.legendIndex||0)});k.reversed&&e.reverse();a.allItems=e;a.display=f=!!e.length;a.lastLineHeight=0;o(e,function(b){a.renderItem(b)});g=(k.width||a.offsetWidth)+j;h=a.lastItemY+a.lastLineHeight+a.titleHeight;h=a.handleOverflow(h);h+=j;if(l||m){if(i){if(g> +0&&h>0)i[i.isNew?"attr":"animate"](i.crisp({width:g,height:h})),i.isNew=!1}else a.box=i=c.rect(0,0,g,h,k.borderRadius,l||0).attr({stroke:k.borderColor,"stroke-width":l||0,fill:m||"none"}).add(d).shadow(k.shadow),i.isNew=!0;i[f?"show":"hide"]()}a.legendWidth=g;a.legendHeight=h;o(e,function(b){a.positionItem(b)});f&&d.align(u({width:g,height:h},k),!0,"spacingBox");b.isResizing||this.positionCheckboxes()},handleOverflow:function(a){var b=this,c=this.chart,d=c.renderer,e=this.options,f=e.y,f=c.spacingBox.height+ +(e.verticalAlign==="top"?-f:f)-this.padding,g=e.maxHeight,h,i=this.clipRect,k=e.navigation,j=p(k.animation,!0),l=k.arrowSize||12,m=this.nav,n=this.pages,r=this.padding,s,R=this.allItems,v=function(a){i.attr({height:a});if(b.contentGroup.div)b.contentGroup.div.style.clip="rect("+r+"px,9999px,"+(r+a)+"px,0)"};e.layout==="horizontal"&&(f/=2);g&&(f=F(f,g));n.length=0;if(a>f){this.clipHeight=h=t(f-20-this.titleHeight-r,0);this.currentPage=p(this.currentPage,1);this.fullHeight=a;o(R,function(a,b){var c= +a._legendItemPos[1],d=A(a.legendItem.getBBox().height),e=n.length;if(!e||c-n[e-1]>h&&(s||c)!==n[e-1])n.push(s||c),e++;b===R.length-1&&c+d-n[e-1]>h&&n.push(c);c!==s&&(s=c)});if(!i)i=b.clipRect=d.clipRect(0,r,9999,0),b.contentGroup.clip(i);v(h);if(!m)this.nav=m=d.g().attr({zIndex:1}).add(this.group),this.up=d.symbol("triangle",0,0,l,l).on("click",function(){b.scroll(-1,j)}).add(m),this.pager=d.text("",15,10).css(k.style).add(m),this.down=d.symbol("triangle-down",0,0,l,l).on("click",function(){b.scroll(1, +j)}).add(m);b.scroll(0);a=f}else if(m)v(c.chartHeight),m.hide(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0;return a},scroll:function(a,b){var c=this.pages,d=c.length,e=this.currentPage+a,f=this.clipHeight,g=this.options.navigation,h=g.activeColor,g=g.inactiveColor,i=this.pager,k=this.padding;e>d&&(e=d);if(e>0)b!==z&&Ua(b,this.chart),this.nav.attr({translateX:k,translateY:f+this.padding+7+this.titleHeight,visibility:"visible"}),this.up.attr({fill:e===1?g:h}).css({cursor:e===1?"default": +"pointer"}),i.attr({text:e+"/"+d}),this.down.attr({x:18+this.pager.getBBox().width,fill:e===d?g:h}).css({cursor:e===d?"default":"pointer"}),c=-c[e-1]+this.initialItemY,this.scrollGroup.animate({translateY:c}),this.currentPage=e,this.positionCheckboxes(c)}};J=B.LegendSymbolMixin={drawRectangle:function(a,b){var c=a.options.symbolHeight||a.fontMetrics.f;b.legendSymbol=this.chart.renderer.rect(0,a.baseline-c+1,a.symbolWidth,c,a.options.symbolRadius||0).attr({zIndex:3}).add(b.legendGroup)},drawLineMarker:function(a){var b= +this.options,c=b.marker,d=a.symbolWidth,e=this.chart.renderer,f=this.legendGroup,a=a.baseline-A(a.fontMetrics.b*0.3),g;if(b.lineWidth){g={"stroke-width":b.lineWidth};if(b.dashStyle)g.dashstyle=b.dashStyle;this.legendLine=e.path(["M",0,a,"L",d,a]).attr(g).add(f)}if(c&&c.enabled!==!1)b=c.radius,this.legendSymbol=c=e.symbol(this.symbol,d/2-b,a-b,2*b,2*b,c).add(f),c.isMarker=!0}};(/Trident\/7\.0/.test(za)||Ma)&&fb(ob.prototype,"positionItem",function(a,b){var c=this,d=function(){b._legendItemPos&&a.call(c, +b)};d();setTimeout(d)});var hb=B.Chart=function(){this.getArgs.apply(this,arguments)};B.chart=function(a,b,c){return new hb(a,b,c)};hb.prototype={callbacks:[],getArgs:function(){var a=[].slice.call(arguments);if(xa(a[0])||a[0].nodeName)this.renderTo=a.shift();this.init(a[0],a[1])},init:function(a,b){var c,d=a.series;a.series=null;c=D(N,a);c.series=a.series=d;this.userOptions=a;d=c.chart;this.margin=this.splashArray("margin",d);this.spacing=this.splashArray("spacing",d);var e=d.events;this.bounds= +{h:{},v:{}};this.callback=b;this.isResizing=0;this.options=c;this.axes=[];this.series=[];this.hasCartesianSeries=d.showAxes;var f=this,g;f.index=S.length;S.push(f);eb++;d.reflow!==!1&&M(f,"load",function(){f.initReflow()});if(e)for(g in e)M(f,g,e[g]);f.xAxis=[];f.yAxis=[];f.animation=ha?!1:p(d.animation,!0);f.pointCount=f.colorCounter=f.symbolCounter=0;f.firstRender()},initSeries:function(a){var b=this.options.chart;(b=I[a.type||b.type||b.defaultSeriesType])||X(17,!0);b=new b;b.init(this,a);return b}, +isInsidePlot:function(a,b,c){var d=c?b:a,a=c?a:b;return d>=0&&d<=this.plotWidth&&a>=0&&a<=this.plotHeight},redraw:function(a){var b=this.axes,c=this.series,d=this.pointer,e=this.legend,f=this.isDirtyLegend,g,h,i=this.hasCartesianSeries,k=this.isDirtyBox,j=c.length,l=j,m=this.renderer,n=m.isHidden(),r=[];Ua(a,this);n&&this.cloneRenderTo();for(this.layOutTitles();l--;)if(a=c[l],a.options.stacking&&(g=!0,a.isDirty)){h=!0;break}if(h)for(l=j;l--;)if(a=c[l],a.options.stacking)a.isDirty=!0;o(c,function(a){a.isDirty&& +a.options.legendType==="point"&&(a.updateTotals&&a.updateTotals(),f=!0)});if(f&&e.options.enabled)e.render(),this.isDirtyLegend=!1;g&&this.getStacks();if(i&&!this.isResizing)this.maxTicks=null,o(b,function(a){a.setScale()});this.getMargins();i&&(o(b,function(a){a.isDirty&&(k=!0)}),o(b,function(a){var b=a.min+","+a.max;if(a.extKey!==b)a.extKey=b,r.push(function(){H(a,"afterSetExtremes",u(a.eventArgs,a.getExtremes()));delete a.eventArgs});(k||g)&&a.redraw()}));k&&this.drawChartBox();o(c,function(a){a.isDirty&& +a.visible&&(!a.isCartesian||a.xAxis)&&a.redraw()});d&&d.reset(!0);m.draw();H(this,"redraw");n&&this.cloneRenderTo(!0);o(r,function(a){a.call()})},get:function(a){var b=this.axes,c=this.series,d,e;for(d=0;d19?this.containerHeight:600))},cloneRenderTo:function(a){var b=this.renderToClone,c=this.container;a?b&&(this.renderTo.appendChild(c),Ta(b),delete this.renderToClone):(c&&c.parentNode===this.renderTo&&this.renderTo.removeChild(c),this.renderToClone=b=this.renderTo.cloneNode(0),L(b,{position:"absolute",top:"-9999px",display:"block"}),b.style.setProperty&&b.style.setProperty("display","block", +"important"),y.body.appendChild(b),c&&b.appendChild(c))},getContainer:function(){var a,b=this.options,c=b.chart,d,e;a=this.renderTo;var f="highcharts-"+zb++;if(!a)this.renderTo=a=c.renderTo;if(xa(a))this.renderTo=a=y.getElementById(a);a||X(13,!0);d=C(K(a,"data-highcharts-chart"));!isNaN(d)&&S[d]&&S[d].hasRendered&&S[d].destroy();K(a,"data-highcharts-chart",this.index);a.innerHTML="";!c.skipClone&&!a.offsetWidth&&this.cloneRenderTo();this.getChartSize();d=this.chartWidth;e=this.chartHeight;this.container= +a=Z(La,{className:"highcharts-container"+(c.className?" "+c.className:""),id:f},u({position:"relative",overflow:"hidden",width:d+"px",height:e+"px",textAlign:"left",lineHeight:"normal",zIndex:0,"-webkit-tap-highlight-color":"rgba(0,0,0,0)"},c.style),this.renderToClone||a);this._cursor=a.style.cursor;this.renderer=new (B[c.renderer]||cb)(a,d,e,c.style,c.forExport,b.exporting&&b.exporting.allowHTML);ha&&this.renderer.create(this,a,d,e);this.renderer.chartIndex=this.index},getMargins:function(a){var b= +this.spacing,c=this.margin,d=this.titleOffset;this.resetMargins();if(d&&!q(c[0]))this.plotTop=t(this.plotTop,d+this.options.title.margin+b[0]);this.legend.adjustMargins(c,b);this.extraBottomMargin&&(this.marginBottom+=this.extraBottomMargin);this.extraTopMargin&&(this.plotTop+=this.extraTopMargin);a||this.getAxisMargins()},getAxisMargins:function(){var a=this,b=a.axisOffset=[0,0,0,0],c=a.margin;a.hasCartesianSeries&&o(a.axes,function(a){a.visible&&a.getOffset()});o(nb,function(d,e){q(c[e])||(a[d]+= +b[e])});a.setChartSize()},reflow:function(a){var b=this,c=b.options.chart,d=b.renderTo,e=c.width||ja(d,"width"),f=c.height||ja(d,"height"),c=a?a.target:E;if(!b.hasUserSize&&!b.isPrinting&&e&&f&&(c===E||c===y)){if(e!==b.containerWidth||f!==b.containerHeight)clearTimeout(b.reflowTimeout),b.reflowTimeout=Pa(function(){if(b.container)b.setSize(e,f,!1),b.hasUserSize=null},a?100:0);b.containerWidth=e;b.containerHeight=f}},initReflow:function(){var a=this,b=function(b){a.reflow(b)};M(E,"resize",b);M(a,"destroy", +function(){V(E,"resize",b)})},setSize:function(a,b,c){var d=this,e,f,g=d.renderer;d.isResizing+=1;Ua(c,d);d.oldChartHeight=d.chartHeight;d.oldChartWidth=d.chartWidth;if(q(a))d.chartWidth=e=t(0,A(a)),d.hasUserSize=!!e;if(q(b))d.chartHeight=f=t(0,A(b));a=g.globalAnimation;(a?Wa:L)(d.container,{width:e+"px",height:f+"px"},a);d.setChartSize(!0);g.setSize(e,f,c);d.maxTicks=null;o(d.axes,function(a){a.isDirty=!0;a.setScale()});o(d.series,function(a){a.isDirty=!0});d.isDirtyLegend=!0;d.isDirtyBox=!0;d.layOutTitles(); +d.getMargins();d.redraw(c);d.oldChartHeight=null;H(d,"resize");a=g.globalAnimation;Pa(function(){d&&H(d,"endResize",null,function(){d.isResizing-=1})},a===!1?0:a&&a.duration||500)},setChartSize:function(a){var b=this.inverted,c=this.renderer,d=this.chartWidth,e=this.chartHeight,f=this.options.chart,g=this.spacing,h=this.clipOffset,i,k,j,l;this.plotLeft=i=A(this.plotLeft);this.plotTop=k=A(this.plotTop);this.plotWidth=j=t(0,A(d-i-this.marginRight));this.plotHeight=l=t(0,A(e-k-this.marginBottom));this.plotSizeX= +b?l:j;this.plotSizeY=b?j:l;this.plotBorderWidth=f.plotBorderWidth||0;this.spacingBox=c.spacingBox={x:g[3],y:g[0],width:d-g[3]-g[1],height:e-g[0]-g[2]};this.plotBox=c.plotBox={x:i,y:k,width:j,height:l};d=2*T(this.plotBorderWidth/2);b=ua(t(d,h[3])/2);c=ua(t(d,h[0])/2);this.clipBox={x:b,y:c,width:T(this.plotSizeX-t(d,h[1])/2-b),height:t(0,T(this.plotSizeY-t(d,h[2])/2-c))};a||o(this.axes,function(a){a.setAxisSize();a.setAxisTranslation()})},resetMargins:function(){var a=this;o(nb,function(b,c){a[b]=p(a.margin[c], +a.spacing[c])});a.axisOffset=[0,0,0,0];a.clipOffset=[0,0,0,0]},drawChartBox:function(){var a=this.options.chart,b=this.renderer,c=this.chartWidth,d=this.chartHeight,e=this.chartBackground,f=this.plotBackground,g=this.plotBorder,h=this.plotBGImage,i=a.borderWidth||0,k=a.backgroundColor,j=a.plotBackgroundColor,l=a.plotBackgroundImage,m=a.plotBorderWidth||0,n,r=this.plotLeft,p=this.plotTop,o=this.plotWidth,v=this.plotHeight,x=this.plotBox,w=this.clipRect,t=this.clipBox;n=i+(a.shadow?8:0);if(i||k)if(e)e.animate(e.crisp({width:c- +n,height:d-n}));else{e={fill:k||"none"};if(i)e.stroke=a.borderColor,e["stroke-width"]=i;this.chartBackground=b.rect(n/2,n/2,c-n,d-n,a.borderRadius,i).attr(e).addClass("highcharts-background").add().shadow(a.shadow)}if(j)f?f.animate(x):this.plotBackground=b.rect(r,p,o,v,0).attr({fill:j}).add().shadow(a.plotShadow);if(l)h?h.animate(x):this.plotBGImage=b.image(l,r,p,o,v).add();w?w.animate({width:t.width,height:t.height}):this.clipRect=b.clipRect(t);if(m)g?(g.strokeWidth=-m,g.animate(g.crisp({x:r,y:p, +width:o,height:v}))):this.plotBorder=b.rect(r,p,o,v,0,-m).attr({stroke:a.plotBorderColor,"stroke-width":m,fill:"none",zIndex:1}).add();this.isDirtyBox=!1},propFromSeries:function(){var a=this,b=a.options.chart,c,d=a.options.series,e,f;o(["inverted","angular","polar"],function(g){c=I[b.type||b.defaultSeriesType];f=a[g]||b[g]||c&&c.prototype[g];for(e=d&&d.length;!f&&e--;)(c=I[d[e].type])&&c.prototype[g]&&(f=!0);a[g]=f})},linkSeries:function(){var a=this,b=a.series;o(b,function(a){a.linkedSeries.length= +0});o(b,function(b){var d=b.options.linkedTo;if(xa(d)&&(d=d===":previous"?a.series[b.index-1]:a.get(d)))d.linkedSeries.push(b),b.linkedParent=d,b.visible=p(b.options.visible,d.options.visible,b.visible)})},renderSeries:function(){o(this.series,function(a){a.translate();a.render()})},renderLabels:function(){var a=this,b=a.options.labels;b.items&&o(b.items,function(c){var d=u(b.style,c.style),e=C(d.left)+a.plotLeft,f=C(d.top)+a.plotTop+12;delete d.left;delete d.top;a.renderer.text(c.html,e,f).attr({zIndex:2}).css(d).add()})}, +render:function(){var a=this.axes,b=this.renderer,c=this.options,d,e,f,g;this.setTitle();this.legend=new ob(this,c.legend);this.getStacks&&this.getStacks();this.getMargins(!0);this.setChartSize();d=this.plotWidth;e=this.plotHeight-=21;o(a,function(a){a.setScale()});this.getAxisMargins();f=d/this.plotWidth>1.1;g=e/this.plotHeight>1.05;if(f||g)this.maxTicks=null,o(a,function(a){(a.horiz&&f||!a.horiz&&g)&&a.setTickInterval(!0)}),this.getMargins();this.drawChartBox();this.hasCartesianSeries&&o(a,function(a){a.visible&& +a.render()});if(!this.seriesGroup)this.seriesGroup=b.g("series-group").attr({zIndex:3}).add();this.renderSeries();this.renderLabels();this.showCredits(c.credits);this.hasRendered=!0},showCredits:function(a){if(a.enabled&&!this.credits)this.credits=this.renderer.text(a.text,0,0).on("click",function(){if(a.href)E.location.href=a.href}).attr({align:a.position.align,zIndex:8}).css(a.style).add().align(a.position)},destroy:function(){var a=this,b=a.axes,c=a.series,d=a.container,e,f=d&&d.parentNode;H(a, +"destroy");S[a.index]=z;eb--;a.renderTo.removeAttribute("data-highcharts-chart");V(a);for(e=b.length;e--;)b[e]=b[e].destroy();for(e=c.length;e--;)c[e]=c[e].destroy();o("title,subtitle,chartBackground,plotBackground,plotBGImage,plotBorder,seriesGroup,clipRect,credits,pointer,scroller,rangeSelector,legend,resetZoomButton,tooltip,renderer".split(","),function(b){var c=a[b];c&&c.destroy&&(a[b]=c.destroy())});if(d)d.innerHTML="",V(d),f&&Ta(d);for(e in a)delete a[e]},isReadyToRender:function(){var a=this; +return!ca&&E==E.top&&y.readyState!=="complete"||ha&&!E.canvg?(ha?Ob.push(function(){a.firstRender()},a.options.global.canvasToolsURL):y.attachEvent("onreadystatechange",function(){y.detachEvent("onreadystatechange",a.firstRender);y.readyState==="complete"&&a.firstRender()}),!1):!0},firstRender:function(){var a=this,b=a.options;if(a.isReadyToRender()){a.getContainer();H(a,"init");a.resetMargins();a.setChartSize();a.propFromSeries();a.getAxes();o(b.series||[],function(b){a.initSeries(b)});a.linkSeries(); +H(a,"beforeRender");if(B.Pointer)a.pointer=new Xa(a,b);a.render();a.renderer.draw();if(!a.renderer.imgCount)a.onload();a.cloneRenderTo(!0)}},onload:function(){var a=this;o([this.callback].concat(this.callbacks),function(b){b&&a.index!==void 0&&b.apply(a,[a])});a.renderer.imgCount||H(a,"load")},splashArray:function(a,b){var c=b[a],c=Y(c)?c:[c,c,c,c];return[p(b[a+"Top"],c[0]),p(b[a+"Right"],c[1]),p(b[a+"Bottom"],c[2]),p(b[a+"Left"],c[3])]}};var Cb=B.CenteredSeriesMixin={getCenter:function(){var a=this.options, +b=this.chart,c=2*(a.slicedOffset||0),d=b.plotWidth-2*c,b=b.plotHeight-2*c,e=a.center,e=[p(e[0],"50%"),p(e[1],"50%"),a.size||"100%",a.innerSize||0],f=F(d,b),g,h;for(g=0;g<4;++g)h=e[g],a=g<2||g===2&&/%$/.test(h),e[g]=(/%$/.test(h)?[d,b,f,e[2]][g]*parseFloat(h)/100:parseFloat(h))+(a?c:0);e[3]>e[2]&&(e[3]=e[2]);return e}},Ha=function(){};Ha.prototype={init:function(a,b,c){this.series=a;this.color=a.color;this.applyOptions(b,c);this.pointAttr={};if(a.options.colorByPoint&&(b=a.options.colors||a.chart.options.colors, +this.color=this.color||b[a.colorCounter++],a.colorCounter===b.length))a.colorCounter=0;a.chart.pointCount++;return this},applyOptions:function(a,b){var c=this.series,d=c.options.pointValKey||c.pointValKey,a=Ha.prototype.optionsToObject.call(this,a);u(this,a);this.options=this.options?u(this.options,a):a;if(d)this.y=this[d];this.isNull=this.y===null;if(typeof this.x!=="number"&&c)this.x=b===void 0?c.autoIncrement():b;return this},optionsToObject:function(a){var b={},c=this.series,d=c.options.keys, +e=d||c.pointArrayMap||["y"],f=e.length,g=0,h=0;if(typeof a==="number"||a===null)b[e[0]]=a;else if(Ia(a)){if(!d&&a.length>f){c=typeof a[0];if(c==="string")b.name=a[0];else if(c==="number")b.x=a[0];g++}for(;hn){for(c=0;j===null&&ci||this.forceCrop))if(b[d-1]p)b=[],c=[];else if(b[0]p)e=this.cropData(this.xData,this.yData,n,p),b=e.xData,c=e.yData,e=e.start,f=!0;for(i=b.length||1;--i;)d=m?k(b[i])-k(b[i-1]):b[i]-b[i-1],d>0&&(g===z||d=c){f=t(0,i-h);break}for(c=i;cd){g=c+h;break}return{xData:a.slice(f,g),yData:b.slice(f,g),start:f,end:g}},generatePoints:function(){var a=this.options.data,b=this.data,c,d=this.processedXData,e=this.processedYData,f=this.pointClass,g=d.length,h=this.cropStart||0,i,k=this.hasGroupedData,j,l=[],m;if(!b&&!k)b=[],b.length=a.length,b=this.data=b;for(m=0;m0),k=this.getExtremesFromAll||this.options.getExtremesFromAll||this.cropped|| +(c[l+1]||k)>=g&&(c[l-1]||k)<=h,i&&k)if(i=j.length)for(;i--;)j[i]!==null&&(e[f++]=j[i]);else e[f++]=j;this.dataMin=Ra(e);this.dataMax=Ea(e)},translate:function(){this.processedXData||this.processData();this.generatePoints();for(var a=this.options,b=a.stacking,c=this.xAxis,d=c.categories,e=this.yAxis,f=this.points,g=f.length,h=!!this.modifyValue,i=a.pointPlacement,k=i==="between"||ma(i),j=a.threshold,l=a.startFromThreshold?j:0,m,n,r,o,R=Number.MAX_VALUE,a=0;a=0&&n<=e.len&&m>=0&&m<=c.len;v.clientX=k?c.translate(x,0,0,0,1):m;v.negative=v.y<(j||0);v.category=d&&d[v.x]!==z?d[v.x]:v.x;a&&(R=F(R,P(m-r)));r=m}this.closestPointRangePx=R},getValidPoints:function(a){return Na(a||this.points,function(a){return!a.isNull})},setClip:function(a){var b=this.chart,c=this.options,d=b.renderer,e=b.inverted,f=this.clipBox,g=f||b.clipBox, +h=this.sharedClipKey||["_sharedClip",a&&a.duration,a&&a.easing,g.height,c.xAxis,c.yAxis].join(","),i=b[h],k=b[h+"m"];if(!i){if(a)g.width=0,b[h+"m"]=k=d.clipRect(-99,e?-b.plotLeft:-b.plotTop,99,e?b.chartWidth:b.chartHeight);b[h]=i=d.clipRect(g)}a&&(i.count+=1);if(c.clip!==!1)this.group.clip(a||f?i:b.clipRect),this.markerGroup.clip(k),this.sharedClipKey=h;a||(i.count-=1,i.count<=0&&h&&b[h]&&(f||(b[h]=b[h].destroy()),b[h+"m"]&&(b[h+"m"]=b[h+"m"].destroy())))},animate:function(a){var b=this.chart,c=this.options.animation, +d;if(c&&!Y(c))c=aa[this.type].animation;a?this.setClip(c):(d=this.sharedClipKey,(a=b[d])&&a.animate({width:b.plotSizeX},c),b[d+"m"]&&b[d+"m"].animate({width:b.plotSizeX+99},c),this.animate=null)},afterAnimate:function(){this.setClip();H(this,"afterAnimate")},drawPoints:function(){var a,b=this.points,c=this.chart,d,e,f,g,h,i,k,j,l=this.options.marker,m=this.pointAttr[""],n,r,o,t=this.markerGroup,v=p(l.enabled,this.xAxis.isRadial,this.closestPointRangePx>2*l.radius);if(l.enabled!==!1||this._hasPointMarkers)for(f= +b.length;f--;)if(g=b[f],d=T(g.plotX),e=g.plotY,j=g.graphic,n=g.marker||{},r=!!g.marker,a=v&&n.enabled===z||n.enabled,o=g.isInside,a&&e!==z&&!isNaN(e)&&g.y!==null)if(a=g.pointAttr[g.selected?"select":""]||m,h=a.r,i=p(n.symbol,this.symbol),k=i.indexOf("url")===0,j)j[o?"show":"hide"](!0).attr(a).animate(u({x:d-h,y:e-h},j.symbolName?{width:2*h,height:2*h}:{}));else{if(o&&(h>0||k))g.graphic=c.renderer.symbol(i,d-h,e-h,2*h,2*h,r?n:l).attr(a).add(t)}else if(j)g.graphic=j.destroy()},convertAttribs:function(a, +b,c,d){var e=this.pointAttrToOptions,f,g,h={},a=a||{},b=b||{},c=c||{},d=d||{};for(f in e)g=e[f],h[f]=p(a[g],b[f],c[f],d[f]);return h},getAttribs:function(){var a=this,b=a.options,c=aa[a.type].marker?b.marker:b,d=c.states,e=d.hover,f,g=a.color,h=a.options.negativeColor;f={stroke:g,fill:g};var i=a.points||[],k,j,l=[],m=a.pointAttrToOptions;k=a.hasPointSpecificOptions;var n=c.lineColor,r=c.fillColor;j=b.turboThreshold;var s=a.zones,t=a.zoneAxis||"y",v;b.marker?(e.radius=e.radius||c.radius+e.radiusPlus, +e.lineWidth=e.lineWidth||c.lineWidth+e.lineWidthPlus):(e.color=e.color||ia(e.color||g).brighten(e.brightness).get(),e.negativeColor=e.negativeColor||ia(e.negativeColor||h).brighten(e.brightness).get());l[""]=a.convertAttribs(c,f);o(["hover","select"],function(b){l[b]=a.convertAttribs(d[b],l[""])});a.pointAttr=l;g=i.length;if(!j||g=f.value;)f=s[++k];j.color=j.fillColor= +p(f.color,a.color)}k=b.colorByPoint||j.color;if(j.options)for(v in m)q(c[m[v]])&&(k=!0);if(k){c=c||{};k=[];d=c.states||{};f=d.hover=d.hover||{};if(!b.marker||j.negative&&!f.fillColor&&!e.fillColor)f[a.pointAttrToOptions.fill]=f.color||!j.options.color&&e[j.negative&&h?"negativeColor":"color"]||ia(j.color).brighten(f.brightness||e.brightness).get();f={color:j.color};if(!r)f.fillColor=j.color;if(!n)f.lineColor=j.color;c.hasOwnProperty("color")&&!c.color&&delete c.color;k[""]=a.convertAttribs(u(f,c), +l[""]);k.hover=a.convertAttribs(d.hover,l.hover,k[""]);k.select=a.convertAttribs(d.select,l.select,k[""])}else k=l;j.pointAttr=k}},destroy:function(){var a=this,b=a.chart,c=/AppleWebKit\/533/.test(za),d,e=a.data||[],f,g,h;H(a,"destroy");V(a);o(a.axisTypes||[],function(b){if(h=a[b])oa(h.series,a),h.isDirty=h.forceRedraw=!0});a.legendItem&&a.chart.legend.destroyItem(a);for(d=e.length;d--;)(f=e[d])&&f.destroy&&f.destroy();a.points=null;clearTimeout(a.animationTimeout);for(g in a)a[g]instanceof O&&!a[g].survive&& +(d=c&&g==="group"?"hide":"destroy",a[g][d]());if(b.hoverSeries===a)b.hoverSeries=null;oa(b.series,a);for(g in a)delete a[g]},getGraphPath:function(a,b,c){var d=this,e=d.options,f=e.step,g,h=[],i,a=a||d.points;(g=a.reversed)&&a.reverse();(f={right:1,center:2}[f]||f&&3)&&g&&(f=4-f);e.connectNulls&&!b&&!c&&(a=this.getValidPoints(a));o(a,function(g,j){var l=g.plotX,m=g.plotY,n=a[j-1];if((g.leftCliff||n&&n.rightCliff)&&!c)i=!0;g.isNull&&!q(b)&&j>0?i=!e.connectNulls:g.isNull&&!b?i=!0:(j===0||i?n=["M",g.plotX, +g.plotY]:d.getPointSpline?n=d.getPointSpline(a,g,j):f?(n=f===1?["L",n.plotX,m]:f===2?["L",(n.plotX+l)/2,n.plotY,"L",(n.plotX+l)/2,m]:["L",l,n.plotY],n.push("L",l,m)):n=["L",l,m],h.push.apply(h,n),i=!1)});return d.graphPath=h},drawGraph:function(){var a=this,b=this.options,c=[["graph",b.lineColor||this.color,b.dashStyle]],d=b.lineWidth,e=b.linecap!=="square",f=(this.gappedPath||this.getGraphPath).call(this),g=this.fillGraph&&this.color||"none";o(this.zones,function(d,e){c.push(["zoneGraph"+e,d.color|| +a.color,d.dashStyle||b.dashStyle])});o(c,function(c,i){var k=c[0],j=a[k];if(j)j.animate({d:f});else if((d||g)&&f.length)j={stroke:c[1],"stroke-width":d,fill:g,zIndex:1},c[2]?j.dashstyle=c[2]:e&&(j["stroke-linecap"]=j["stroke-linejoin"]="round"),a[k]=a.chart.renderer.path(f).attr(j).add(a.group).shadow(i<2&&b.shadow)})},applyZones:function(){var a=this,b=this.chart,c=b.renderer,d=this.zones,e,f,g=this.clips||[],h,i=this.graph,k=this.area,j=t(b.chartWidth,b.chartHeight),l=this[(this.zoneAxis||"y")+ +"Axis"],m,n=l.reversed,r=b.inverted,s=l.horiz,q,v,x,w=!1;if(d.length&&(i||k)&&l.min!==z)i&&i.hide(),k&&k.hide(),m=l.getExtremes(),o(d,function(d,o){e=n?s?b.plotWidth:0:s?0:l.toPixels(m.min);e=F(t(p(f,e),0),j);f=F(t(A(l.toPixels(p(d.value,m.max),!0)),0),j);w&&(e=f=l.toPixels(m.max));q=Math.abs(e-f);v=F(e,f);x=t(e,f);if(l.isXAxis){if(h={x:r?x:v,y:0,width:q,height:j},!s)h.x=b.plotHeight-h.x}else if(h={x:0,y:r?x:v,width:j,height:q},s)h.y=b.plotWidth-h.y;b.inverted&&c.isVML&&(h=l.isXAxis?{x:0,y:n?v:x, +height:h.width,width:b.chartWidth}:{x:h.y-b.plotLeft-b.spacingBox.x,y:0,width:h.height,height:b.chartHeight});g[o]?g[o].animate(h):(g[o]=c.clipRect(h),i&&a["zoneGraph"+o].clip(g[o]),k&&a["zoneArea"+o].clip(g[o]));w=d.value>m.max}),this.clips=g},invertGroups:function(){function a(){var a={width:b.yAxis.len,height:b.xAxis.len};o(["group","markerGroup"],function(c){b[c]&&b[c].attr(a).invert()})}var b=this,c=b.chart;if(b.xAxis)M(c,"resize",a),M(b,"destroy",function(){V(c,"resize",a)}),a(),b.invertGroups= +a},plotGroup:function(a,b,c,d,e){var f=this[a],g=!f;g&&(this[a]=f=this.chart.renderer.g(b).attr({zIndex:d||0.1}).add(e),f.addClass("highcharts-series-"+this.index));f.attr({visibility:c})[g?"attr":"animate"](this.getPlotBox());return f},getPlotBox:function(){var a=this.chart,b=this.xAxis,c=this.yAxis;if(a.inverted)b=c,c=this.xAxis;return{translateX:b?b.left:a.plotLeft,translateY:c?c.top:a.plotTop,scaleX:1,scaleY:1}},render:function(){var a=this,b=a.chart,c,d=a.options,e=(c=d.animation)&&!!a.animate&& +b.renderer.isSVG&&p(c.duration,500)||0,f=a.visible?"inherit":"hidden",g=d.zIndex,h=a.hasRendered,i=b.seriesGroup;c=a.plotGroup("group","series",f,g,i);a.markerGroup=a.plotGroup("markerGroup","markers",f,g,i);e&&a.animate(!0);a.getAttribs();c.inverted=a.isCartesian?b.inverted:!1;a.drawGraph&&(a.drawGraph(),a.applyZones());o(a.points,function(a){a.redraw&&a.redraw()});a.drawDataLabels&&a.drawDataLabels();a.visible&&a.drawPoints();a.drawTracker&&a.options.enableMouseTracking!==!1&&a.drawTracker();b.inverted&& +a.invertGroups();d.clip!==!1&&!a.sharedClipKey&&!h&&c.clip(b.clipRect);e&&a.animate();if(!h)a.animationTimeout=Pa(function(){a.afterAnimate()},e);a.isDirty=a.isDirtyData=!1;a.hasRendered=!0},redraw:function(){var a=this.chart,b=this.isDirtyData,c=this.isDirty,d=this.group,e=this.xAxis,f=this.yAxis;d&&(a.inverted&&d.attr({width:a.plotWidth,height:a.plotHeight}),d.animate({translateX:p(e&&e.left,a.plotLeft),translateY:p(f&&f.top,a.plotTop)}));this.translate();this.render();b&&H(this,"updatedData"); +(c||b)&&delete this.kdTree},kdDimensions:1,kdAxisArray:["clientX","plotY"],searchPoint:function(a,b){var c=this.xAxis,d=this.yAxis,e=this.chart.inverted;return this.searchKDTree({clientX:e?c.len-a.chartY+c.pos:a.chartX-c.pos,plotY:e?d.len-a.chartX+d.pos:a.chartY-d.pos},b)},buildKDTree:function(){function a(c,e,f){var g,h;if(h=c&&c.length)return g=b.kdAxisArray[e%f],c.sort(function(a,b){return a[g]-b[g]}),h=Math.floor(h/2),{point:c[h],left:a(c.slice(0,h),e+1,f),right:a(c.slice(h+1),e+1,f)}}var b=this, +c=b.kdDimensions;delete b.kdTree;Pa(function(){b.kdTree=a(b.getValidPoints(),c,c)},b.options.kdNow?0:1)},searchKDTree:function(a,b){function c(a,b,k,j){var l=b.point,m=d.kdAxisArray[k%j],n,p,o=l;p=q(a[e])&&q(l[e])?Math.pow(a[e]-l[e],2):null;n=q(a[f])&&q(l[f])?Math.pow(a[f]-l[f],2):null;n=(p||0)+(n||0);l.dist=q(n)?Math.sqrt(n):Number.MAX_VALUE;l.distX=q(p)?Math.sqrt(p):Number.MAX_VALUE;m=a[m]-l[m];n=m<0?"left":"right";p=m<0?"right":"left";b[n]&&(n=c(a,b[n],k+1,j),o=n[g]0&&this.singleStacks===!1&&(q.points[v][0]=q.points[this.index+","+w+",0"][0]); +e==="percent"?(s=s?i:k,j&&m[s]&&m[s][w]?(s=m[s][w],q.total=s.total=t(s.total,q.total)+P(u)||0):q.total=fa(q.total+(P(u)||0))):q.total=fa(q.total+(u||0));q.cum=p(q.cum,g)+(u||0);u!==null&&q.points[v].push(q.cum);c[x]=q.cum}if(e==="percent")l.usePercentage=!0;this.stackedYData=c;l.oldStacks={}}};Q.prototype.setPercentStacks=function(){var a=this,b=a.stackKey,c=a.yAxis.stacks,d=a.processedXData,e;o([b,"-"+b],function(b){var f;for(var g=d.length,h,i;g--;)if(h=d[g],e=a.getStackIndicator(e,h,a.index),f= +(i=c[b]&&c[b][h])&&i.points[e.key],h=f)i=i.total?100/i.total:0,h[0]=fa(h[0]*i),h[1]=fa(h[1]*i),a.stackedYData[g]=h[1]})};Q.prototype.getStackIndicator=function(a,b,c){!q(a)||a.x!==b?a={x:b,index:0}:a.index++;a.key=[c,b,a.index].join(",");return a};u(hb.prototype,{addSeries:function(a,b,c){var d,e=this;a&&(b=p(b,!0),H(e,"addSeries",{options:a},function(){d=e.initSeries(a);e.isDirtyLegend=!0;e.linkSeries();b&&e.redraw(c)}));return d},addAxis:function(a,b,c,d){var e=b?"xAxis":"yAxis",f=this.options; +new ka(this,D(a,{index:this[e].length,isX:b}));f[e]=ta(f[e]||{});f[e].push(a);p(c,!0)&&this.redraw(d)},showLoading:function(a){var b=this,c=b.options,d=b.loadingDiv,e=c.loading,f=function(){d&&L(d,{left:b.plotLeft+"px",top:b.plotTop+"px",width:b.plotWidth+"px",height:b.plotHeight+"px"})};if(!d)b.loadingDiv=d=Z(La,{className:"highcharts-loading"},u(e.style,{zIndex:10,display:"none"}),b.container),b.loadingSpan=Z("span",null,e.labelStyle,d),M(b,"redraw",f);b.loadingSpan.innerHTML=a||c.lang.loading; +if(!b.loadingShown)L(d,{opacity:0,display:""}),Wa(d,{opacity:e.style.opacity},{duration:e.showDuration||0}),b.loadingShown=!0;f()},hideLoading:function(){var a=this.options,b=this.loadingDiv;b&&Wa(b,{opacity:0},{duration:a.loading.hideDuration||100,complete:function(){L(b,{display:"none"})}});this.loadingShown=!1}});u(Ha.prototype,{update:function(a,b,c,d){function e(){f.applyOptions(a);if(f.y===null&&h)f.graphic=h.destroy();if(Y(a)&&!Ia(a))f.redraw=function(){if(h&&h.element&&a&&a.marker&&a.marker.symbol)f.graphic= +h.destroy();if(a&&a.dataLabels&&f.dataLabel)f.dataLabel=f.dataLabel.destroy();f.redraw=null};i=f.index;g.updateParallelArrays(f,i);if(l&&f.name)l[f.x]=f.name;j.data[i]=Y(j.data[i])?f.options:a;g.isDirty=g.isDirtyData=!0;if(!g.fixedBox&&g.hasCartesianSeries)k.isDirtyBox=!0;if(j.legendType==="point")k.isDirtyLegend=!0;b&&k.redraw(c)}var f=this,g=f.series,h=f.graphic,i,k=g.chart,j=g.options,l=g.xAxis&&g.xAxis.names,b=p(b,!0);d===!1?e():f.firePointEvent("update",{options:a},e)},remove:function(a,b){this.series.removePoint(sa(this, +this.series.data),a,b)}});u(Q.prototype,{addPoint:function(a,b,c,d){var e=this,f=e.options,g=e.data,h=e.graph,i=e.area,k=e.chart,j=e.xAxis&&e.xAxis.names,l=h&&h.shift||0,m=["graph","area"],h=f.data,n,r=e.xData;Ua(d,k);if(c){for(d=e.zones.length;d--;)m.push("zoneGraph"+d,"zoneArea"+d);o(m,function(a){if(e[a])e[a].shift=l+(f.step?2:1)})}if(i)i.isArea=!0;b=p(b,!0);i={series:e};e.pointClass.prototype.applyOptions.apply(i,[a]);m=i.x;d=r.length;if(e.requireSorting&&mm;)d--;e.updateParallelArrays(i, +"splice",d,0,0);e.updateParallelArrays(i,d);if(j&&i.name)j[m]=i.name;h.splice(d,0,a);n&&(e.data.splice(d,0,null),e.processData());f.legendType==="point"&&e.generatePoints();c&&(g[0]&&g[0].remove?g[0].remove(!1):(g.shift(),e.updateParallelArrays(i,"shift"),h.shift()));e.isDirty=!0;e.isDirtyData=!0;b&&(e.getAttribs(),k.redraw())},removePoint:function(a,b,c){var d=this,e=d.data,f=e[a],g=d.points,h=d.chart,i=function(){g&&g.length===e.length&&g.splice(a,1);e.splice(a,1);d.options.data.splice(a,1);d.updateParallelArrays(f|| +{series:d},"splice",a,1);f&&f.destroy();d.isDirty=!0;d.isDirtyData=!0;b&&h.redraw()};Ua(c,h);b=p(b,!0);f?f.firePointEvent("remove",null,i):i()},remove:function(a,b){var c=this,d=c.chart;H(c,"remove",null,function(){c.destroy();d.isDirtyLegend=d.isDirtyBox=!0;d.linkSeries();p(a,!0)&&d.redraw(b)})},update:function(a,b){var c=this,d=this.chart,e=this.userOptions,f=this.type,g=I[f].prototype,h=["group","markerGroup","dataLabelsGroup"],i;if(a.type&&a.type!==f||a.zIndex!==void 0)h.length=0;o(h,function(a){h[a]= +c[a];delete c[a]});a=D(e,{animation:!1,index:this.index,pointStart:this.xData[0]},{data:this.options.data},a);this.remove(!1);for(i in g)this[i]=z;u(this,I[a.type||f].prototype);o(h,function(a){c[a]=h[a]});this.init(d,a);d.linkSeries();p(b,!0)&&d.redraw(!1)}});u(ka.prototype,{update:function(a,b){var c=this.chart,a=c.options[this.coll][this.options.index]=D(this.userOptions,a);this.destroy(!0);this._addedPlotLB=this.chart._labelPanes=z;this.init(c,u(a,{events:z}));c.isDirtyBox=!0;p(b,!0)&&c.redraw()}, +remove:function(a){for(var b=this.chart,c=this.coll,d=this.series,e=d.length;e--;)d[e]&&d[e].remove(!1);oa(b.axes,this);oa(b[c],this);b.options[c].splice(this.options.index,1);o(b[c],function(a,b){a.options.index=b});this.destroy();b.isDirtyBox=!0;p(a,!0)&&b.redraw()},setTitle:function(a,b){this.update({title:a},b)},setCategories:function(a,b){this.update({categories:a},b)}});var wa=pa(Q);I.line=wa;aa.area=D(da,{softThreshold:!1,threshold:0});var la=pa(Q,{type:"area",singleStacks:!1,getStackPoints:function(){var a= +[],b=[],c=this.xAxis,d=this.yAxis,e=d.stacks[this.stackKey],f={},g=this.points,h=this.index,i=d.series,k=i.length,j,l=p(d.options.reversedStacks,!0)?1:-1,m,n;if(this.options.stacking){for(m=0;m=0&&m=0&&ma&&h>e?(h=t(a,e),k=2*e-h):hc&&k>e?(k=t(c,e), +h=2*e-k):k0.5;b=Math.round(b)+f;d-= +b;g&&(b-=1,d+=1);return{x:a,y:b,width:c,height:d}},translate:function(){var a=this,b=a.chart,c=a.options,d=a.borderWidth=p(c.borderWidth,a.closestPointRange*a.xAxis.transA<2?0:1),e=a.yAxis,f=a.translatedThreshold=e.getThreshold(c.threshold),g=p(c.minPointLength,5),h=a.getColumnMetrics(),i=h.width,k=a.barW=t(i,1+2*d),j=a.pointXOffset=h.offset;b.inverted&&(f-=0.5);c.pointPadding&&(k=ua(k));Q.prototype.translate.apply(a);o(a.points,function(c){var d=F(p(c.yBottom,f),9E4),h=999+P(d),h=F(t(-h,c.plotY), +e.len+h),o=c.plotX+j,s=k,q=F(h,d),v,u=t(h,d)-q;P(u)g?d-g:f-(v?g:0));c.barX=o;c.pointWidth=i;c.tooltipPos=b.inverted?[e.len+e.pos-b.plotLeft-h,a.xAxis.len-o-s/2,u]:[o+s/2,h+e.pos-b.plotTop,u];c.shapeType="rect";c.shapeArgs=a.crispCol(o,q,s,u)})},getSymbol:Aa,drawLegendSymbol:J.drawRectangle,drawGraph:Aa,drawPoints:function(){var a=this,b=this.chart,c=a.options,d=b.renderer,e=c.animationLimit||250,f,g;o(a.points,function(h){var i= +h.plotY,k=h.graphic;if(i!==z&&!isNaN(i)&&h.y!==null)f=h.shapeArgs,i=q(a.borderWidth)?{"stroke-width":a.borderWidth}:{},g=h.pointAttr[h.selected?"select":""]||a.pointAttr[""],k?(Oa(k),k.attr(i).attr(g)[b.pointCount\u25cf {series.name}
', +pointFormat:"x: {point.x}
y: {point.y}
"}});la=pa(Q,{type:"scatter",sorted:!1,requireSorting:!1,noSharedTooltip:!0,trackerGroups:["group","markerGroup","dataLabelsGroup"],takeOrdinalPosition:!1,kdDimensions:2,drawGraph:function(){this.options.lineWidth&&Q.prototype.drawGraph.call(this)}});I.scatter=la;aa.pie=D(da,{borderColor:"#FFFFFF",borderWidth:1,center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{distance:30,enabled:!0,formatter:function(){return this.y===null?void 0: +this.point.name},x:0},ignoreHiddenPoint:!0,legendType:"point",marker:null,size:null,showInLegend:!1,slicedOffset:10,states:{hover:{brightness:0.1,shadow:!1}},stickyTracking:!1,tooltip:{followPointer:!0}});da={type:"pie",isCartesian:!1,pointClass:pa(Ha,{init:function(){Ha.prototype.init.apply(this,arguments);var a=this,b;a.name=p(a.name,"Slice");b=function(b){a.slice(b.type==="select")};M(a,"select",b);M(a,"unselect",b);return a},setVisible:function(a,b){var c=this,d=c.series,e=d.chart,f=d.options.ignoreHiddenPoint, +b=p(b,f);if(a!==c.visible){c.visible=c.options.visible=a=a===z?!c.visible:a;d.options.data[sa(c,d.data)]=c.options;o(["graphic","dataLabel","connector","shadowGroup"],function(b){if(c[b])c[b][a?"show":"hide"](!0)});c.legendItem&&e.legend.colorizeItem(c,a);!a&&c.state==="hover"&&c.setState("");if(f)d.isDirty=!0;b&&e.redraw()}},slice:function(a,b,c){var d=this.series;Ua(c,d.chart);p(b,!0);this.sliced=this.options.sliced=a=q(a)?a:!this.sliced;d.options.data[sa(this,d.data)]=this.options;a=a?this.slicedTranslation: +{translateX:0,translateY:0};this.graphic.animate(a);this.shadowGroup&&this.shadowGroup.animate(a)},haloPath:function(a){var b=this.shapeArgs,c=this.series.chart;return this.sliced||!this.visible?[]:this.series.chart.renderer.symbols.arc(c.plotLeft+b.x,c.plotTop+b.y,b.r+a,b.r+a,{innerR:this.shapeArgs.r,start:b.start,end:b.end})}}),requireSorting:!1,directTouch:!0,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],axisTypes:[],pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth", +fill:"color"},animate:function(a){var b=this,c=b.points,d=b.startAngleRad;if(!a)o(c,function(a){var c=a.graphic,g=a.shapeArgs;c&&(c.attr({r:a.startR||b.center[3]/2,start:d,end:d}),c.animate({r:g.r,start:g.start,end:g.end},b.options.animation))}),b.animate=null},updateTotals:function(){var a,b=0,c=this.points,d=c.length,e,f=this.options.ignoreHiddenPoint;for(a=0;a0&&(e.visible||!f)?e.y/b*100:0,e.total=b},generatePoints:function(){Q.prototype.generatePoints.call(this); +this.updateTotals()},translate:function(a){this.generatePoints();var b=0,c=this.options,d=c.slicedOffset,e=d+c.borderWidth,f,g,h,i=c.startAngle||0,k=this.startAngleRad=ra/180*(i-90),i=(this.endAngleRad=ra/180*(p(c.endAngle,i+360)-90))-k,j=this.points,l=c.dataLabels.distance,c=c.ignoreHiddenPoint,m,n=j.length,o;if(!a)this.center=a=this.getCenter();this.getX=function(b,c){h=W.asin(F((b-a[1])/(a[2]/2+l),1));return a[0]+(c?-1:1)*U(h)*(a[2]/2+l)};for(m=0;m1.5*ra?h-=2*ra:h<-ra/2&&(h+=2*ra);o.slicedTranslation={translateX:A(U(h)*d),translateY:A($(h)*d)};f=U(h)*a[2]/2;g=$(h)*a[2]/2;o.tooltipPos=[a[0]+f*0.7,a[1]+g*0.7];o.half=h<-ra/2||h>ra/2?1:0;o.angle=h;e=F(e,l/2);o.labelPos=[a[0]+f+U(h)*l,a[1]+g+$(h)*l,a[0]+f+U(h)*e,a[1]+g+$(h)*e,a[0]+f,a[1]+g,l<0?"center":o.half?"right":"left",h]}},drawGraph:null,drawPoints:function(){var a= +this,b=a.chart.renderer,c,d,e=a.options.shadow,f,g,h,i;if(e&&!a.shadowGroup)a.shadowGroup=b.g("shadow").add(a.group);o(a.points,function(k){if(k.y!==null){d=k.graphic;h=k.shapeArgs;f=k.shadowGroup;g=k.pointAttr[k.selected?"select":""];if(!g.stroke)g.stroke=g.fill;if(e&&!f)f=k.shadowGroup=b.g("shadow").add(a.shadowGroup);c=k.sliced?k.slicedTranslation:{translateX:0,translateY:0};f&&f.attr(c);if(d)d.setRadialReference(a.center).attr(g).animate(u(h,c));else{i={"stroke-linejoin":"round"};if(!k.visible)i.visibility= +"hidden";k.graphic=d=b[k.shapeType](h).setRadialReference(a.center).attr(g).attr(i).attr(c).add(a.group).shadow(e,f)}}})},searchPoint:Aa,sortByAngle:function(a,b){a.sort(function(a,d){return a.angle!==void 0&&(d.angle-a.angle)*b})},drawLegendSymbol:J.drawRectangle,getCenter:Cb.getCenter,getSymbol:Aa};da=pa(Q,da);I.pie=da;Q.prototype.drawDataLabels=function(){var a=this,b=a.options,c=b.cursor,d=b.dataLabels,e=a.points,f,g,h=a.hasRendered||0,i,k,j=a.chart.renderer;if(d.enabled||a._hasPointLabels)a.dlProcessOptions&& +a.dlProcessOptions(d),k=a.plotGroup("dataLabelsGroup","data-labels",d.defer?"hidden":"visible",d.zIndex||6),p(d.defer,!0)&&(k.attr({opacity:+h}),h||M(a,"afterAnimate",function(){a.visible&&k.show();k[b.animation?"animate":"attr"]({opacity:1},{duration:200})})),g=d,o(e,function(e){var h,n=e.dataLabel,o,s,t=e.connector,v=!0,x,w={};f=e.dlOptions||e.options&&e.options.dataLabels;h=p(f&&f.enabled,g.enabled)&&e.y!==null;if(n&&!h)e.dataLabel=n.destroy();else if(h){d=D(g,f);x=d.style;h=d.rotation;o=e.getLabelConfig(); +i=d.format?Ka(d.format,o):d.formatter.call(o,d);x.color=p(d.color,x.color,a.color,"black");if(n)if(q(i))n.attr({text:i}),v=!1;else{if(e.dataLabel=n=n.destroy(),t)e.connector=t.destroy()}else if(q(i)){n={fill:d.backgroundColor,stroke:d.borderColor,"stroke-width":d.borderWidth,r:d.borderRadius||0,rotation:h,padding:d.padding,zIndex:1};if(x.color==="contrast")w.color=d.inside||d.distance<0||b.stacking?j.getContrast(e.color||a.color):"#000000";if(c)w.cursor=c;for(s in n)n[s]===z&&delete n[s];n=e.dataLabel= +j[h?"text":"label"](i,0,-9999,d.shape,null,null,d.useHTML).attr(n).css(u(x,w)).add(k).shadow(d.shadow)}n&&a.alignDataLabel(e,n,d,null,v)}})};Q.prototype.alignDataLabel=function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=p(a.plotX,-9999),i=p(a.plotY,-9999),k=b.getBBox(),j=f.renderer.fontMetrics(c.style.fontSize).b,l=c.rotation,m=c.align,n=this.visible&&(a.series.forceDL||f.isInsidePlot(h,A(i),g)||d&&f.isInsidePlot(h,g?d.x+1:d.y+d.height-1,g)),o=p(c.overflow,"justify")==="justify";if(n)d=u({x:g?f.plotWidth- +i:h,y:A(g?f.plotHeight-h:i),width:0,height:0},d),u(c,{width:k.width,height:k.height}),l?(o=!1,g=f.renderer.rotCorr(j,l),g={x:d.x+c.x+d.width/2+g.x,y:d.y+c.y+d.height/2},b[e?"attr":"animate"](g).attr({align:c.align}),h=(l+720)%360,h=h>180&&h<360,m==="left"?g.y-=h?k.height:0:m==="center"?(g.x-=k.width/2,g.y-=k.height/2):m==="right"&&(g.x-=k.width,g.y-=h?0:k.height)):(b.align(c,null,d),g=b.alignAttr),o?this.justifyDataLabel(b,c,g,k,d,e):p(c.crop,!0)&&(n=f.isInsidePlot(g.x,g.y)&&f.isInsidePlot(g.x+k.width, +g.y+k.height)),c.shape&&!l&&b.attr({anchorX:a.plotX,anchorY:a.plotY});if(!n)Oa(b),b.attr({y:-9999}),b.placed=!1};Q.prototype.justifyDataLabel=function(a,b,c,d,e,f){var g=this.chart,h=b.align,i=b.verticalAlign,k,j,l=a.box?0:a.padding||0;k=c.x+l;if(k<0)h==="right"?b.align="left":b.x=-k,j=!0;k=c.x+d.width-l;if(k>g.plotWidth)h==="left"?b.align="right":b.x=g.plotWidth-k,j=!0;k=c.y+l;if(k<0)i==="bottom"?b.verticalAlign="top":b.y=-k,j=!0;k=c.y+d.height-l;if(k>g.plotHeight)i==="top"?b.verticalAlign="bottom": +b.y=g.plotHeight-k,j=!0;if(j)a.placed=!f,a.align(b,null,e)};if(I.pie)I.pie.prototype.drawDataLabels=function(){var a=this,b=a.data,c,d=a.chart,e=a.options.dataLabels,f=p(e.connectorPadding,10),g=p(e.connectorWidth,1),h=d.plotWidth,i=d.plotHeight,k,j,l=p(e.softConnector,!0),m=e.distance,n=a.center,r=n[2]/2,q=n[1],u=m>0,v,x,w,B=[[],[]],z,y,E,D,C,G=[0,0,0,0],L=function(a,b){return b.y-a.y};if(a.visible&&(e.enabled||a._hasPointLabels)){Q.prototype.drawDataLabels.apply(a);o(b,function(a){if(a.dataLabel&& +a.visible)B[a.half].push(a),a.dataLabel._pos=null});for(D=2;D--;){var H=[],M=[],I=B[D],K=I.length,J;if(K){a.sortByAngle(I,D-0.5);for(C=b=0;!b&&I[C];)b=I[C]&&I[C].dataLabel&&(I[C].dataLabel.getBBox().height||21),C++;if(m>0){x=F(q+r+m,d.plotHeight);for(C=t(0,q-r-m);C<=x;C+=b)H.push(C);x=H.length;if(K>x){c=[].concat(I);c.sort(L);for(C=K;C--;)c[C].rank=C;for(C=K;C--;)I[C].rank>=x&&I.splice(C,1);K=I.length}for(C=0;C0){if(x=M.pop(),J=x.i,y=x.y,c>y&&H[J+1]!==null||ch-f&&(G[1]=t(A(z+x-h+f),G[1])),y-b/2<0?G[0]=t(A(-y+b/2),G[0]):y+b/2>i&&(G[2]=t(A(y+b/2-i),G[2]))}}}if(Ea(G)===0||this.verifyDataLabelOverflow(G))this.placeDataLabels(),u&&g&&o(this.points,function(b){k=b.connector;w=b.labelPos;if((v=b.dataLabel)&&v._pos&&b.visible)E=v._attr.visibility,z=v.connX,y=v.connY,j=l?["M",z+(w[6]==="left"?5:-5),y,"C",z,y,2*w[2]-w[4],2*w[3]-w[5],w[2],w[3],"L",w[4],w[5]]:["M",z+(w[6]==="left"?5:-5),y,"L", +w[2],w[3],"L",w[4],w[5]],k?(k.animate({d:j}),k.attr("visibility",E)):b.connector=k=a.chart.renderer.path(j).attr({"stroke-width":g,stroke:e.connectorColor||b.color||"#606060",visibility:E}).add(a.dataLabelsGroup);else if(k)b.connector=k.destroy()})}},I.pie.prototype.placeDataLabels=function(){o(this.points,function(a){var b=a.dataLabel;if(b&&a.visible)(a=b._pos)?(b.attr(b._attr),b[b.moved?"animate":"attr"](a),b.moved=!0):b&&b.attr({y:-9999})})},I.pie.prototype.alignDataLabel=Aa,I.pie.prototype.verifyDataLabelOverflow= +function(a){var b=this.center,c=this.options,d=c.center,e=c.minSize||80,f=e,g;d[0]!==null?f=t(b[2]-t(a[1],a[3]),e):(f=t(b[2]-a[1]-a[3],e),b[0]+=(a[3]-a[1])/2);d[1]!==null?f=t(F(f,b[2]-t(a[0],a[2])),e):(f=t(F(f,b[2]-a[0]-a[2]),e),b[1]+=(a[0]-a[2])/2);fp(this.translatedThreshold,g.yAxis.len)),k=p(c.inside,!!this.options.stacking);if(h){d=D(h);if(d.y<0)d.height+=d.y,d.y=0;h=d.y+d.height-g.yAxis.len;h>0&&(d.height-=h);f&&(d={x:g.yAxis.len-d.y-d.height,y:g.xAxis.len-d.x-d.width,width:d.height,height:d.width});if(!k)f?(d.x+=i?0:d.width,d.width=0):(d.y+=i?d.height:0,d.height=0)}c.align=p(c.align,!f||k?"center":i?"right":"left");c.verticalAlign=p(c.verticalAlign, +f||k?"middle":i?"top":"bottom");Q.prototype.alignDataLabel.call(this,a,b,c,d,e)};(function(a){var b=a.Chart,c=a.each,d=a.pick,e=a.addEvent;b.prototype.callbacks.push(function(a){function b(){var e=[];c(a.series,function(a){var b=a.options.dataLabels,f=a.dataLabelCollections||["dataLabel"];(b.enabled||a._hasPointLabels)&&!b.allowOverlap&&a.visible&&c(f,function(b){c(a.points,function(a){if(a[b])a[b].labelrank=d(a.labelrank,a.shapeArgs&&a.shapeArgs.height),e.push(a[b])})})});a.hideOverlappingLabels(e)} +b();e(a,"redraw",b)});b.prototype.hideOverlappingLabels=function(a){var b=a.length,d,e,k,j,l,m,n,o,p;for(e=0;el.x+n.translateX+(k.width-p)||m.x+o.translateX+(j.width- +p)l.y+n.translateY+(k.height-p)||m.y+o.translateY+(j.height-p)h;if(b.series.length&&(i||l>F(j.dataMin,j.min))&&(!i||k + +
+
+

+ Activity Dashboard +

+
+
+
+
+

+
+
+
+ +

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

+

All Activity

+

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

+ +
+
+
+
+ +

+

Planned Activity

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

+ +
+
+
+
+ +

+

+

Completed Activities

+

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

+ +
+
+
+
+ +

+

+

Today's Activities

+

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

+ +
+
+
+
+ +

+

+

Overdue Activities

+

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

+ +
+
+
+
+ +

+

+

Cancelled Activities

+

+
+
+
+ +
+
+
+ +
+
+

+

Activity Type

+

+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Planned Activities
No Data Available
NameActivity TypeAssigned toDead Line DateViewOrigin
+ + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Today's Activities
No Data Available
NameActivity TypeAssigned toDead Line DateViewOrigin
+ + + + + + + + + + + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Completed Activities
No Data Available
NameActivity TypeAssigned toDead Line DateView
+ + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Overdue Activities
No Data Available
NameActivity TypeAssigned toDead Line DateView
+ + + + + + + + + +
+
+
+ diff --git a/activity_dashboard_mngmnt/views/activity_dashbord.xml b/activity_dashboard_mngmnt/views/activity_dashbord.xml new file mode 100644 index 000000000..cad0785c9 --- /dev/null +++ b/activity_dashboard_mngmnt/views/activity_dashbord.xml @@ -0,0 +1,38 @@ + + + + + Cancel + + + code + + if records: + action = records.activity_cancel() + + + + Mark As Done + + + code + + if records: + action = records.activity_done() + + + + + Dashboard + activity_dashboard + current + + + + + + \ No newline at end of file diff --git a/activity_dashboard_mngmnt/views/activity_tag.xml b/activity_dashboard_mngmnt/views/activity_tag.xml new file mode 100644 index 000000000..54a7f9161 --- /dev/null +++ b/activity_dashboard_mngmnt/views/activity_tag.xml @@ -0,0 +1,54 @@ + + + + + sales.team.crm.tag.view.form + activity.tag + +
+ +
+
+ + + + + +
+
+
+
+ + Activities + activity.tag + tree,form + + + + sales.team.crm.tag.view.tree + activity.tag + + + + + + + + + + + + +
diff --git a/activity_dashboard_mngmnt/views/my_activity.xml b/activity_dashboard_mngmnt/views/my_activity.xml new file mode 100644 index 000000000..057aafd9f --- /dev/null +++ b/activity_dashboard_mngmnt/views/my_activity.xml @@ -0,0 +1,83 @@ + + + + + mail.activity.type.view.form + mail.activity + +
+
+
+ + + + + + + + + + + + +
+ Recommended Activities + +
+
+ + + + + + + + + + + + + +
+
+ +
+
+ + activity.view.tree + mail.activity + + + + + + + + + + + + + + Activities + mail.activity + tree,form + { 'tree_view_ref':'mail_activity_type_view_form_'} + + + + + + + +
+
\ No newline at end of file diff --git a/all_in_one_dynamic_custom_fields/README.rst b/all_in_one_dynamic_custom_fields/README.rst new file mode 100644 index 000000000..a55bd153e --- /dev/null +++ b/all_in_one_dynamic_custom_fields/README.rst @@ -0,0 +1,45 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +All in One Custom Dynamic Fields +================================ + +All in One Custom Dynamic Fields + +Installation +============ +- www.odoo.com/documentation/16.0/setup/install.html +- Install our custom addon + +Company +------- +* `Cybrosys Techno Solutions `__ + +Credits +------- +* Developer: + Version 15: Minhaj T @cybrosys + Version 16: Amaya Aravind EV @cybrosys + + +Contacts +-------- +* Mail Contact : odoo@cybrosys.com + +Bug Tracker +----------- +Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. + +Maintainer +========== +.. image:: https://cybrosys.com/images/logo.png + :target: https://cybrosys.com + +This module is maintained by Cybrosys Technologies. + +For support and more information, please visit `Our Website `__ + +Further information +=================== +HTML Description: ``__ \ No newline at end of file diff --git a/all_in_one_dynamic_custom_fields/__init__.py b/all_in_one_dynamic_custom_fields/__init__.py new file mode 100644 index 000000000..0bf9c8572 --- /dev/null +++ b/all_in_one_dynamic_custom_fields/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies (). +# Author: Cybrosys Techno Solutions () +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### + +from . import models diff --git a/all_in_one_dynamic_custom_fields/__manifest__.py b/all_in_one_dynamic_custom_fields/__manifest__.py new file mode 100644 index 000000000..40a34f219 --- /dev/null +++ b/all_in_one_dynamic_custom_fields/__manifest__.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies (). +# Author: Cybrosys Techno Solutions () +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### + +{ + 'name': 'All in One Dynamic Fields', + 'version': '16.0.1.0.0', + 'summary': 'Create Custom Fields As Per Your Need Without Any Coding.', + 'description': 'All in One Dynamic Fields, All in One Custom Fields, Dynamic Fields, Custom Fields, Create Fields Dynamically', + 'category': 'Extra Tools', + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'website': "https://www.cybrosys.com", + 'license': 'AGPL-3', + 'depends': ['base'], + 'data': [ + 'data/widget_data.xml', + 'security/security.xml', + 'security/ir.model.access.csv', + 'views/dynamic_fields.xml', + ], + 'images': ['static/description/banner.png'], + 'installable': True, + 'auto_install': False, + 'application': False, +} diff --git a/all_in_one_dynamic_custom_fields/data/widget_data.xml b/all_in_one_dynamic_custom_fields/data/widget_data.xml new file mode 100644 index 000000000..f3df52478 --- /dev/null +++ b/all_in_one_dynamic_custom_fields/data/widget_data.xml @@ -0,0 +1,40 @@ + + + + + + image + Image + + + + many2many_tags + Many2many Tags + + + + binary + Binary + + + + radio + Radio + + + + priority + Priority + + + + monetary + Monetary + + + + selection + Selection + + + diff --git a/all_in_one_dynamic_custom_fields/doc/RELEASE_NOTES.md b/all_in_one_dynamic_custom_fields/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..0aac4a54f --- /dev/null +++ b/all_in_one_dynamic_custom_fields/doc/RELEASE_NOTES.md @@ -0,0 +1,7 @@ +## Module + +#### 15.12.2022 +#### Version 16.0.1.0.0 +##### ADD +- Initial commit for All in One Custom Dynamic Fields + diff --git a/all_in_one_dynamic_custom_fields/models/__init__.py b/all_in_one_dynamic_custom_fields/models/__init__.py new file mode 100644 index 000000000..0451c323f --- /dev/null +++ b/all_in_one_dynamic_custom_fields/models/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies (). +# Author: Cybrosys Techno Solutions () +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### + +from . import ir_model_fields +from . import field_widgets +from . import dynamic_fields diff --git a/all_in_one_dynamic_custom_fields/models/dynamic_fields.py b/all_in_one_dynamic_custom_fields/models/dynamic_fields.py new file mode 100644 index 000000000..91e673112 --- /dev/null +++ b/all_in_one_dynamic_custom_fields/models/dynamic_fields.py @@ -0,0 +1,217 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies (). +# Author: Cybrosys Techno Solutions () +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### + +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError + + +class DynamicFields(models.Model): + _name = 'dynamic.fields' + _rec_name = 'field_description' + _description = 'Custom Dynamic Fields' + _inherit = 'ir.model.fields' + + @api.model + def get_possible_field_types(self): + """Return all available field types other than 'one2many' and + 'reference' fields.""" + field_list = sorted((key, key) for key in fields.MetaField.by_type) + field_list.remove(('one2many', 'one2many')) + field_list.remove(('reference', 'reference')) + return field_list + + position_field = fields.Many2one('ir.model.fields', string='Field Name', + required=True, + ondelete='cascade') + position = fields.Selection([('before', 'Before'), + ('after', 'After')], string='Position', + required=True) + model_id = fields.Many2one('ir.model', string='Model', required=True, + index=True, ondelete='cascade', + help="The model this field belongs to") + ref_model_id = fields.Many2one('ir.model', string='Model', index=True) + selection_field = fields.Char(string="Selection Options") + rel_field = fields.Many2one('ir.model.fields', string='Related Field') + field_type = fields.Selection(selection='get_possible_field_types', + string='Field Type', required=True) + ttype = fields.Selection(string="Field Type", related='field_type') + widget = fields.Many2one('dynamic.field.widgets', string='Widget') + groups = fields.Many2many('res.groups', 'employee_dynamic_fields_group_rel', + 'field_id', 'group_id') + extra_features = fields.Boolean(string="Show Extra Properties") + status = fields.Selection([ + ('draft', 'Draft'), + ('form', 'Field Created'), + ('tree', 'Added in Tree View'), + ], string='Status', index=True, readonly=True, tracking=True, + copy=False, default='draft', + required=True, help='Record Status') + + form_view_id = fields.Many2one('ir.ui.view', string="Form View ID", + required=True) + form_view_inherit_id = fields.Char(string="Form View Inherit Id", + related='form_view_id.xml_id') + add_field_in_tree = fields.Boolean(string="Add Field to the Tree View", + default=False) + tree_view_id = fields.Many2one('ir.ui.view', string="Tree View ID") + tree_view_inherit_id = fields.Char(string="Tree View Inherit Id", + related='tree_view_id.xml_id') + + def create_dynamic_fields(self): + self.write({'status': 'form'}) + if self.field_type == 'monetary' and not self.env[ + 'ir.model.fields'].sudo().search([ + ('model', '=', self.model_id.id), + ('name', '=', 'currency_id')]): + self.env['ir.model.fields'].sudo().create({ + 'name': 'x_currency_id', + 'field_description': 'Currency', + 'model_id': self.model_id.id, + 'ttype': 'many2one', + 'relation': 'res.currency', + 'is_dynamic_field': True + }) + self.env['ir.model.fields'].sudo().create({ + 'name': self.name, + 'field_description': self.field_description, + 'model_id': self.model_id.id, + 'ttype': self.field_type, + 'relation': self.ref_model_id.model, + 'required': self.required, + 'index': self.index, + 'store': self.store, + 'help': self.help, + 'readonly': self.readonly, + 'selection': self.selection_field, + 'copied': self.copied, + 'is_dynamic_field': True + }) + inherit_form_view_name = str( + self.form_view_id.name) + ".inherit.dynamic.custom." + \ + str(self.field_description) + ".field" + xml_id = self.form_view_id.xml_id + inherit_id = self.env.ref(xml_id) + arch_base = _('' + '' + '' + '' + '' + '') % (self.position_field.name, + self.position, self.name) + if self.widget: + arch_base = _('' + '' + '' + '' + '' + '') % (self.position_field.name, + self.position, self.name, + self.widget.name) + self.form_view_id = self.env['ir.ui.view'].sudo().create({ + 'name': inherit_form_view_name, + 'type': 'form', + 'model': self.model_id.model, + 'mode': 'extension', + 'inherit_id': inherit_id.id, + 'arch_base': arch_base, + 'active': True + }) + + def add_field_to_tree_view(self): + if self.env['ir.model.fields'].sudo().search( + [('model', '=', self.model_id.model), + ('name', '=', 'state')]): + self.write({'status': 'tree'}) + if self.add_field_in_tree: + inherit_tree_view_name = str( + self.tree_view_id.name) + ".inherit.dynamic.custom" + \ + str(self.field_description) + ".field" + tree_view_arch_base = _( + '' + '' + '''''' + '''''' + '''''' + '''''') % (self.name) + self.tree_view_id = self.env['ir.ui.view'].sudo().create({ + 'name': inherit_tree_view_name, + 'type': 'tree', + 'model': self.model_id.model, + 'mode': 'extension', + 'inherit_id': self.tree_view_id.id, + 'arch_base': tree_view_arch_base, + 'active': True}) + + else: + raise ValidationError( + _('Error! Selected Model You cannot add a custom field to the tree view.' + 'The feature only applies to the model tree/list view that contains the state ' + 'field.')) + + @api.depends('model_id') + @api.onchange('model_id') + def set_domain(self): + """Return the fields that currently present in the form""" + form_view_ids = self.model_id.view_ids.filtered( + lambda l: l.type == 'form' and l.mode == 'primary') + tree_view_ids = self.model_id.view_ids.filtered( + lambda l: l.type == 'tree' and l.mode == 'primary') + fields = self.env['ir.model.fields'].sudo().search([ + ('model', '=', self.model_id.model)]) + field_list = [] + for rec in fields: + for field in rec: + field_list.append(field.id) + return {'domain': { + 'form_view_id': [('id', 'in', form_view_ids.ids)], + 'tree_view_id': [('id', 'in', tree_view_ids.ids)], + 'position_field': [('id', 'in', field_list)] + }} + + @api.depends('field_type') + @api.onchange('field_type') + def onchange_field_type(self): + if self.field_type: + if self.field_type == 'binary': + return {'domain': {'widget': [('name', '=', 'image')]}} + elif self.field_type == 'many2many': + return {'domain': {'widget': [ + ('name', 'in', ['many2many_tags', 'binary'])]}} + elif self.field_type == 'selection': + return {'domain': { + 'widget': [('name', 'in', ['radio', 'priority'])]}} + elif self.field_type == 'float': + return {'domain': {'widget': [('name', '=', 'monetary')]}} + elif self.field_type == 'many2one': + return {'domain': {'widget': [('name', '=', 'selection')]}} + else: + return {'domain': {'widget': [('id', '=', False)]}} + return {'domain': {'widget': [('id', '=', False)]}} + + def unlink(self): + if self.form_view_id: + self.form_view_id.active = False + if self.tree_view_id: + self.tree_view_id.active = False + res = super(DynamicFields, self).unlink() + return res \ No newline at end of file diff --git a/all_in_one_dynamic_custom_fields/models/field_widgets.py b/all_in_one_dynamic_custom_fields/models/field_widgets.py new file mode 100644 index 000000000..e03fe5f4c --- /dev/null +++ b/all_in_one_dynamic_custom_fields/models/field_widgets.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies (). +# Author: Cybrosys Techno Solutions () +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### + +from odoo import models, fields + + +class FieldWidgets(models.Model): + """We can't filter a selection field dynamically + so when we select a field its widgets also need to change according to the selected + field type, we can't do it by a 'selection' field, need a 'Many2one' field. + """ + + _name = 'dynamic.field.widgets' + _rec_name = 'description' + _description = 'Field Widgets' + + name = fields.Char(string="Name") + description = fields.Char(string="Description") diff --git a/all_in_one_dynamic_custom_fields/models/ir_model_fields.py b/all_in_one_dynamic_custom_fields/models/ir_model_fields.py new file mode 100644 index 000000000..a6cf59471 --- /dev/null +++ b/all_in_one_dynamic_custom_fields/models/ir_model_fields.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies (). +# Author: Cybrosys Techno Solutions () +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### + +from odoo import models, fields + + +class IrModelFields(models.Model): + """Adding a new field to understand the dynamically created fields.""" + + _inherit = 'ir.model.fields' + + is_dynamic_field = fields.Boolean(string="Dynamic Field") diff --git a/all_in_one_dynamic_custom_fields/security/ir.model.access.csv b/all_in_one_dynamic_custom_fields/security/ir.model.access.csv new file mode 100644 index 000000000..9c05b7930 --- /dev/null +++ b/all_in_one_dynamic_custom_fields/security/ir.model.access.csv @@ -0,0 +1,5 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_all_in_one_dynamic_custom_fields_administrator,dynamic.fields,model_dynamic_fields,all_in_one_dynamic_custom_fields.group_all_in_one_dynamic_custom_fields_administrator,1,1,1,1 +access_all_in_one_dynamic_custom_fields_user,dynamic.fields,model_dynamic_fields,all_in_one_dynamic_custom_fields.group_all_in_one_dynamic_custom_fields_user,1,0,0,0 +access_dynamic_field_widgets,"dynamic.field.widgets","model_dynamic_field_widgets",,1,1,1,1 + diff --git a/all_in_one_dynamic_custom_fields/security/security.xml b/all_in_one_dynamic_custom_fields/security/security.xml new file mode 100644 index 000000000..763ab2096 --- /dev/null +++ b/all_in_one_dynamic_custom_fields/security/security.xml @@ -0,0 +1,32 @@ + + + + + + All in One Custom Dynamic Fields + Helps you handle your All-in-One Custom Dynamic Fields Permissions + 5 + + + + User + + + + + Administrator + + + + + + + + diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/check.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/check.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/chevron.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/chevron.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/cogs.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/cogs.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/consultation.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/consultation.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/ecom-black.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/ecom-black.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/education-black.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/education-black.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/hotel-black.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/hotel-black.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/license.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/license.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/lifebuoy.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/lifebuoy.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/manufacturing-black.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/manufacturing-black.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/pos-black.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/pos-black.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/puzzle.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/puzzle.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/restaurant-black.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/restaurant-black.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/service-black.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/service-black.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/trading-black.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/trading-black.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/training.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/training.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/update.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/update.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/user.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/user.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/icons/wrench.png b/all_in_one_dynamic_custom_fields/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/icons/wrench.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/misc/categories.png b/all_in_one_dynamic_custom_fields/static/description/assets/misc/categories.png new file mode 100644 index 000000000..bedf1e0b1 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/misc/categories.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/misc/check-box.png b/all_in_one_dynamic_custom_fields/static/description/assets/misc/check-box.png new file mode 100644 index 000000000..42caf24b9 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/misc/check-box.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/misc/compass.png b/all_in_one_dynamic_custom_fields/static/description/assets/misc/compass.png new file mode 100644 index 000000000..d5fed8faa Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/misc/compass.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/misc/corporate.png b/all_in_one_dynamic_custom_fields/static/description/assets/misc/corporate.png new file mode 100644 index 000000000..2eb13edbf Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/misc/corporate.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/misc/customer-support.png b/all_in_one_dynamic_custom_fields/static/description/assets/misc/customer-support.png new file mode 100644 index 000000000..79efc72ed Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/misc/customer-support.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/misc/cybrosys-logo.png b/all_in_one_dynamic_custom_fields/static/description/assets/misc/cybrosys-logo.png new file mode 100644 index 000000000..cc3cc0ccf Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/misc/cybrosys-logo.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/misc/features.png b/all_in_one_dynamic_custom_fields/static/description/assets/misc/features.png new file mode 100644 index 000000000..b41769f77 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/misc/features.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/misc/logo.png b/all_in_one_dynamic_custom_fields/static/description/assets/misc/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/misc/logo.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/misc/pictures.png b/all_in_one_dynamic_custom_fields/static/description/assets/misc/pictures.png new file mode 100644 index 000000000..56d255fe9 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/misc/pictures.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/misc/pie-chart.png b/all_in_one_dynamic_custom_fields/static/description/assets/misc/pie-chart.png new file mode 100644 index 000000000..426e05244 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/misc/pie-chart.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/misc/right-arrow.png b/all_in_one_dynamic_custom_fields/static/description/assets/misc/right-arrow.png new file mode 100644 index 000000000..730984a06 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/misc/right-arrow.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/misc/star.png b/all_in_one_dynamic_custom_fields/static/description/assets/misc/star.png new file mode 100644 index 000000000..2eb9ab29f Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/misc/star.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/misc/support.png b/all_in_one_dynamic_custom_fields/static/description/assets/misc/support.png new file mode 100644 index 000000000..4f18b8b82 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/misc/support.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/misc/whatsapp.png b/all_in_one_dynamic_custom_fields/static/description/assets/misc/whatsapp.png new file mode 100644 index 000000000..d513a5356 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/misc/whatsapp.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/modules/1.png b/all_in_one_dynamic_custom_fields/static/description/assets/modules/1.png new file mode 100644 index 000000000..5238bdeab Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/modules/1.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/modules/2.png b/all_in_one_dynamic_custom_fields/static/description/assets/modules/2.png new file mode 100644 index 000000000..1ae7cfe3b Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/modules/2.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/modules/3.png b/all_in_one_dynamic_custom_fields/static/description/assets/modules/3.png new file mode 100644 index 000000000..3c3ff1afb Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/modules/3.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/modules/4.png b/all_in_one_dynamic_custom_fields/static/description/assets/modules/4.png new file mode 100644 index 000000000..3fae4631e Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/modules/4.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/modules/5.gif b/all_in_one_dynamic_custom_fields/static/description/assets/modules/5.gif new file mode 100644 index 000000000..2a5f8e659 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/modules/5.gif differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/modules/6.png b/all_in_one_dynamic_custom_fields/static/description/assets/modules/6.png new file mode 100644 index 000000000..7f2815273 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/modules/6.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/1.png b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/1.png new file mode 100644 index 000000000..22991c072 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/1.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/10.png b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/10.png new file mode 100644 index 000000000..4f7164b1c Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/10.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/11.png b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/11.png new file mode 100644 index 000000000..4d6173fce Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/11.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/12.png b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/12.png new file mode 100644 index 000000000..ad345419a Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/12.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/2.png b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/2.png new file mode 100644 index 000000000..b8a4acf9c Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/2.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/3.png b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/3.png new file mode 100644 index 000000000..2861cb2e6 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/3.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/4.png b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/4.png new file mode 100644 index 000000000..e0138c10e Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/4.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/5.png b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/5.png new file mode 100644 index 000000000..14c5918ac Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/5.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/6.png b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/6.png new file mode 100644 index 000000000..964e8f161 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/6.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/7.png b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/7.png new file mode 100644 index 000000000..6dd4aa0e4 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/7.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/8.png b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/8.png new file mode 100644 index 000000000..a859133ca Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/8.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/9.png b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/9.png new file mode 100644 index 000000000..a0ff105fd Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/9.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/hero.gif b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/hero.gif new file mode 100644 index 000000000..6619d4dd2 Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/assets/screenshots/hero.gif differ diff --git a/all_in_one_dynamic_custom_fields/static/description/banner.png b/all_in_one_dynamic_custom_fields/static/description/banner.png new file mode 100644 index 000000000..faa759b4a Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/banner.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/icon.png b/all_in_one_dynamic_custom_fields/static/description/icon.png new file mode 100644 index 000000000..8c72ad74f Binary files /dev/null and b/all_in_one_dynamic_custom_fields/static/description/icon.png differ diff --git a/all_in_one_dynamic_custom_fields/static/description/index.html b/all_in_one_dynamic_custom_fields/static/description/index.html new file mode 100644 index 000000000..bd493d7a7 --- /dev/null +++ b/all_in_one_dynamic_custom_fields/static/description/index.html @@ -0,0 +1,580 @@ +
+ +
+ +
+
+ Community +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+ +
+
+
+ +

+ All In One Dynamic Fields

+

Create custom fields in as per you need without any coding.

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

Explore This + Module

+
+
+ + + +
+
+ +
+

Overview +

+
+
+
+ All in One Custom Dynamic Fields module helps with easy creation of custom fields in as per you need without any coding. + This module helps to add new fields on the form view and tree view as per requirement. +
+
+ + + +
+
+ +
+

Features +

+
+
+
+
+ + Creates custom fields in any modules without coding. +
+
+ + The position of the fields can be easily set. +
+
+ + Widgets can be selected for custom fields. +
+
+ + Can set the field properties (help,required,copied,read-only,indexed) +
+
+
+ + +
+
+ + + +
+
+ +
+

Screenshots +

+
+
+
+ +
+

Create Custom Dynamic Fields +

+

After installation, give the user access to 'Create Custom Dynamic Fields' from user settings.

+ +
+ +
+

Create Fields +

+

Open the module and Click Create Button to create new custom fields and fill the details.Then click the Create Field.

+ + +
+ +
+

Model +

+

Select the model for which you want to create a custom field.

+ + +
+ +
+

Field Type +

+

Choose the field type

+ +
+ +
+

List / Tree View +

+

The custom field also we can add to the module List/Tree view. + Note: The feature only applies to the model tree/list view that contains the state field.

+ + + +
+ +
+

Sale Order Form View +

+

A new custom field is created in the sale order form view.

+ +
+ +
+

Sale Order Tree/List View +

+

A new custom field is created in the sale order tree view.

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

Related + Products +

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

Our Services +

+
+ +
+
+
+
+ +
+
+ Odoo + Customization
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Support
+
+ + +
+
+ +
+
+ Hire + Odoo + Developer
+
+ +
+
+ +
+
+ Odoo + Integration
+
+ +
+
+ +
+
+ Odoo + Migration
+
+ + +
+
+ +
+
+ Odoo + Consultancy
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Licensing Consultancy
+
+
+ +
+ + + + + +
+
+ +
+

Our + Industries +

+
+ +
+
+
+
+ +
+ Trading +
+

+ Easily procure + and + sell your products

+
+
+ +
+
+ +
+ POS +
+

+ Easy + configuration + and convivial experience

+
+
+ +
+
+ +
+ Education +
+

+ A platform for + educational management

+
+
+ +
+
+ +
+ Manufacturing +
+

+ Plan, track and + schedule your operations

+
+
+ +
+
+ +
+ E-commerce & Website +
+

+ Mobile + friendly, + awe-inspiring product pages

+
+
+ +
+
+ +
+ Service Management +
+

+ Keep track of + services and invoice

+
+
+ +
+
+ +
+ Restaurant +
+

+ Run your bar or + restaurant methodically

+
+
+ +
+
+ +
+ Hotel Management +
+

+ An + all-inclusive + hotel management application

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

Support +

+
+
+
+
+
+
+ +
+
+

Need Help?

+

Got questions or need help? Get in touch.

+ +

+ odoo@cybrosys.com

+
+
+
+
+
+
+
+ +
+
+

WhatsApp

+

Say hi to us on WhatsApp!

+ +

+91 86068 + 27707

+
+
+
+
+
+
+
+ +
+
+
+ \ No newline at end of file diff --git a/all_in_one_dynamic_custom_fields/views/dynamic_fields.xml b/all_in_one_dynamic_custom_fields/views/dynamic_fields.xml new file mode 100644 index 000000000..eae1d57a9 --- /dev/null +++ b/all_in_one_dynamic_custom_fields/views/dynamic_fields.xml @@ -0,0 +1,109 @@ + + + + custom_dynamic_fields_view_tree + dynamic.fields + + + + + + + + + + + + custom_dynamic_fields_view_form + dynamic.fields + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + Custom Dynamic Fields + dynamic.fields + tree,form + +

+ Create New Custom Dynamic Field! +

+
+
+ + + + + + +
diff --git a/custom_pivot_report/README.rst b/custom_pivot_report/README.rst new file mode 100755 index 000000000..7012a5397 --- /dev/null +++ b/custom_pivot_report/README.rst @@ -0,0 +1,40 @@ +Custom Pivot Report +=================== +* Custom Pivot Report module for Odoo 16 + +Installation +============ + - www.odoo.com/documentation/16.0/setup/install.html + - Install our custom addon + +License +------- +General Public License, Version 3 (LGPL v3). +(https://www.odoo.com/documentation/user/16.0/legal/licenses/licenses.html) + +Company +------- +* 'Cybrosys Techno Solutions '__ + +Credits +------- +* 'Cybrosys Techno Solutions '__ + +Contacts +-------- +* Mail Contact : odoo@cybrosys.com + +Bug Tracker +----------- +Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. + +Maintainer +========== +This module is maintained by Cybrosys Technologies. + +For support and more information, please visit https://www.cybrosys.com + +Further information +=================== +HTML Description: ``__ + diff --git a/custom_pivot_report/__init__.py b/custom_pivot_report/__init__.py new file mode 100644 index 000000000..db4ddb8ad --- /dev/null +++ b/custom_pivot_report/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import models +from .hooks import report_uninstall_hook diff --git a/custom_pivot_report/__manifest__.py b/custom_pivot_report/__manifest__.py new file mode 100644 index 000000000..e72690d2a --- /dev/null +++ b/custom_pivot_report/__manifest__.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +{ + 'name': 'Custom Pivot Report', + "description": """Create Custom Pivot Report For Any Model Without Coding, Dynamic Pivot Report, Custom Pivot Report, Dynamic Report, Pivot View""", + "summary": """Create Custom Pivot Report For Any Model Without Coding""", + "category": "Tools", + 'version': '16.0.1.0.0', + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'website': 'https://www.cybrosys.com', + 'depends': ['base'], + 'images': ['static/description/banner.png'], + 'data': [ + 'security/ir.model.access.csv', + 'views/custom_report.xml', + ], + 'license': 'LGPL-3', + 'installable': True, + 'application': False, + 'auto_install': False, + 'uninstall_hook': 'report_uninstall_hook' +} diff --git a/custom_pivot_report/doc/RELEASE_NOTES.md b/custom_pivot_report/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..ff28b9a7b --- /dev/null +++ b/custom_pivot_report/doc/RELEASE_NOTES.md @@ -0,0 +1,6 @@ +## Module + +#### 15.12.2022 +#### Version 16.0.1.0.0 +#### ADD +- Initial commit for custom_pivot_report \ No newline at end of file diff --git a/custom_pivot_report/hooks.py b/custom_pivot_report/hooks.py new file mode 100644 index 000000000..0e7ba31b0 --- /dev/null +++ b/custom_pivot_report/hooks.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from odoo import api, SUPERUSER_ID + + +def report_uninstall_hook(cr, registry): + # Uninstall hook to delete all the custom pivot reports + env = api.Environment(cr, SUPERUSER_ID, {}) + records = env['custom.report'].search([]) + for record in records: + record.unlink() diff --git a/custom_pivot_report/models/__init__.py b/custom_pivot_report/models/__init__.py new file mode 100644 index 000000000..1f672a5b7 --- /dev/null +++ b/custom_pivot_report/models/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import custom_report \ No newline at end of file diff --git a/custom_pivot_report/models/custom_report.py b/custom_pivot_report/models/custom_report.py new file mode 100644 index 000000000..04294fcc9 --- /dev/null +++ b/custom_pivot_report/models/custom_report.py @@ -0,0 +1,161 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from odoo import models, fields, api, _ + + +class IrUiView(models.Model): + _inherit = 'ir.ui.view' + + custom_id = fields.Char(string='Custom ID') + + +class IrUiMenu(models.Model): + _inherit = 'ir.ui.menu' + + custom_id = fields.Char(string='Custom ID') + + +class IrActionsActWindow(models.Model): + _inherit = 'ir.actions.act_window' + + custom_id = fields.Char(string='Custom ID') + + +class CustomReport(models.Model): + _name = 'custom.report' + _description = 'Custom Report' + + name = fields.Char(string='Name') + model_id = fields.Many2one('ir.model', string='Model', required=True, domain="[('transient', '=', False),]", + ondelete='cascade') + fields_ids = fields.One2many('custom.report.fields', 'report_id', string='Fields', required=True) + menu_id = fields.Many2one('ir.ui.menu', string='Menu', required=True, ondelete='cascade', + help="The menu where you want to create a new menu item.") + menu_group_id = fields.Many2many('res.groups', string='Menu Group', required=True, ondelete='cascade') + view_type = fields.Selection([('pivot', 'Pivot'), ('graph', 'Graph')], string='View Type') + + def unlink(self): + for rec in self: + # Searching the view + view = self.env['ir.ui.view'].search( + [('custom_id', '=', str(rec.id) + '_' + rec.model_id.model + '_' + rec.menu_id.complete_name)]) + # search the action + action = self.env['ir.actions.act_window'].search( + [('custom_id', '=', str(rec.id) + '_' + 'pivot' + '_' + '_' + 'current',)]) + # search the menu + menu = self.env['ir.ui.menu'].search( + [('custom_id', '=', str(rec.id) + '_' + rec.menu_id.complete_name + '_' + rec.model_id.model)]) + view.sudo().unlink() + action.sudo().unlink() + menu.sudo().unlink() + return super().unlink() + + @api.constrains('menu_id', 'fields_ids', 'model_id', 'name', 'menu_group_id') + def _create_menu_id(self): + view_id = self.env['ir.ui.view'].search( + [('custom_id', '=', str(self.id) + '_' + self.model_id.model + '_' + self.menu_id.complete_name)]) + arch_base = '''\n''' % (self.name) + for rec in self.fields_ids: + if rec.row: + arch_base += ''' + \n''' % (rec.custom_field_id.name, rec.label) + elif rec.measure: + arch_base += ''' + \n''' % (rec.custom_field_id.name, rec.label) + else: + arch_base += '''\n''' % (rec.custom_field_id.name, rec.label) + + arch_base += '''\n''' + view_value = { + 'name': _(self.name), + 'type': 'pivot', + 'custom_id': str(self.id) + '_' + self.model_id.model + '_' + self.menu_id.complete_name, + 'model': self.model_id.model, + 'mode': 'primary', + 'active': True, + 'arch_base': arch_base, + 'groups_id': [(6, 0, [self.menu_group_id.id])], + } + if not view_id: + # Creating the view + view_obj = self.env['ir.ui.view'].create(view_value) + else: + view_id.sudo().write(view_value) + view_obj = view_id + value = { + 'type': 'ir.actions.act_window', + 'name': _(self.name), + 'res_model': self.model_id.model, + 'custom_id': str(self.id) + '_' + 'pivot' + '_' + '_' + 'current', + 'view_mode': 'pivot', + 'view_id': view_obj.id, + 'target': 'current', + } + action_id = self.env['ir.actions.act_window'].search( + [('custom_id', '=', str(self.id) + '_' + 'pivot' + '_' + '_' + 'current')]) + if not action_id: + # Creating the action + action = self.env['ir.actions.act_window'].create(value) + else: + action_id.sudo().write(value) + action = action_id + value = { + 'name': self.name, + 'complete_name': self.menu_id.complete_name + '/' + self.name, + 'action': 'ir.actions.act_window,%s' % (action.id), + 'parent_id': self.menu_id.id, + 'custom_id': str(self.id) + '_' + self.menu_id.complete_name + '_' + self.model_id.model, + 'groups_id': [(6, 0, [self.menu_group_id.id])], + } + menu_id = self.env['ir.ui.menu'].search( + [('custom_id', '=', str(self.id) + '_' + self.menu_id.complete_name + '_' + self.model_id.model)]) + if not menu_id: + # Creating the menu + menu = self.env['ir.ui.menu'].create(value) + else: + menu_id.sudo().write(value) + + +class CustomReportFields(models.Model): + _name = 'custom.report.fields' + _description = 'Custom Report Fields' + + custom_field_id = fields.Many2one('ir.model.fields', string='Custom Report', + required=True, ondelete='cascade') + report_id = fields.Many2one('custom.report', string='Parent ', ondelete='cascade') + label = fields.Char(string='Label') + row = fields.Boolean(string='Row', default=0) + measure = fields.Boolean(string='Mesure', default=0) + measurable = fields.Boolean(string='Measurable', default=0) + rowable = fields.Boolean(string='Row able', default=0) + + @api.onchange('custom_field_id') + def onchange_custom_field_id(self): + model_id = self.env.context.get('parent_id') + self.label = self.custom_field_id.field_description + self.report_id = model_id + if self.custom_field_id.ttype in ['float', 'integer', 'many2one', 'monetary']: + self.measurable = True + if self.custom_field_id.ttype in ['many2many', 'one2many']: + self.rowable = True + return {'domain': {'custom_field_id': [('model_id.id', '=', model_id)]}} diff --git a/custom_pivot_report/security/ir.model.access.csv b/custom_pivot_report/security/ir.model.access.csv new file mode 100755 index 000000000..4268a401d --- /dev/null +++ b/custom_pivot_report/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +custom.report,custom_report,model_custom_report,base.group_user,1,1,1,1, +custom.report.fields,custom_report_field,model_custom_report_fields,base.group_user,1,1,1,1, diff --git a/custom_pivot_report/static/description/assets/icons/check.png b/custom_pivot_report/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/check.png differ diff --git a/custom_pivot_report/static/description/assets/icons/chevron.png b/custom_pivot_report/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/chevron.png differ diff --git a/custom_pivot_report/static/description/assets/icons/cogs.png b/custom_pivot_report/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/cogs.png differ diff --git a/custom_pivot_report/static/description/assets/icons/consultation.png b/custom_pivot_report/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/consultation.png differ diff --git a/custom_pivot_report/static/description/assets/icons/ecom-black.png b/custom_pivot_report/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/ecom-black.png differ diff --git a/custom_pivot_report/static/description/assets/icons/education-black.png b/custom_pivot_report/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/education-black.png differ diff --git a/custom_pivot_report/static/description/assets/icons/hotel-black.png b/custom_pivot_report/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/hotel-black.png differ diff --git a/custom_pivot_report/static/description/assets/icons/license.png b/custom_pivot_report/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/license.png differ diff --git a/custom_pivot_report/static/description/assets/icons/lifebuoy.png b/custom_pivot_report/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/lifebuoy.png differ diff --git a/custom_pivot_report/static/description/assets/icons/logo.png b/custom_pivot_report/static/description/assets/icons/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/logo.png differ diff --git a/custom_pivot_report/static/description/assets/icons/manufacturing-black.png b/custom_pivot_report/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/manufacturing-black.png differ diff --git a/custom_pivot_report/static/description/assets/icons/pos-black.png b/custom_pivot_report/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/pos-black.png differ diff --git a/custom_pivot_report/static/description/assets/icons/puzzle.png b/custom_pivot_report/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/puzzle.png differ diff --git a/custom_pivot_report/static/description/assets/icons/restaurant-black.png b/custom_pivot_report/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/restaurant-black.png differ diff --git a/custom_pivot_report/static/description/assets/icons/service-black.png b/custom_pivot_report/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/service-black.png differ diff --git a/custom_pivot_report/static/description/assets/icons/trading-black.png b/custom_pivot_report/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/trading-black.png differ diff --git a/custom_pivot_report/static/description/assets/icons/training.png b/custom_pivot_report/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/training.png differ diff --git a/custom_pivot_report/static/description/assets/icons/update.png b/custom_pivot_report/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/update.png differ diff --git a/custom_pivot_report/static/description/assets/icons/user.png b/custom_pivot_report/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/user.png differ diff --git a/custom_pivot_report/static/description/assets/icons/wrench.png b/custom_pivot_report/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/custom_pivot_report/static/description/assets/icons/wrench.png differ diff --git a/custom_pivot_report/static/description/assets/misc/categories.png b/custom_pivot_report/static/description/assets/misc/categories.png new file mode 100644 index 000000000..bedf1e0b1 Binary files /dev/null and b/custom_pivot_report/static/description/assets/misc/categories.png differ diff --git a/custom_pivot_report/static/description/assets/misc/check-box.png b/custom_pivot_report/static/description/assets/misc/check-box.png new file mode 100644 index 000000000..42caf24b9 Binary files /dev/null and b/custom_pivot_report/static/description/assets/misc/check-box.png differ diff --git a/custom_pivot_report/static/description/assets/misc/compass.png b/custom_pivot_report/static/description/assets/misc/compass.png new file mode 100644 index 000000000..d5fed8faa Binary files /dev/null and b/custom_pivot_report/static/description/assets/misc/compass.png differ diff --git a/custom_pivot_report/static/description/assets/misc/corporate.png b/custom_pivot_report/static/description/assets/misc/corporate.png new file mode 100644 index 000000000..2eb13edbf Binary files /dev/null and b/custom_pivot_report/static/description/assets/misc/corporate.png differ diff --git a/custom_pivot_report/static/description/assets/misc/customer-support.png b/custom_pivot_report/static/description/assets/misc/customer-support.png new file mode 100644 index 000000000..79efc72ed Binary files /dev/null and b/custom_pivot_report/static/description/assets/misc/customer-support.png differ diff --git a/custom_pivot_report/static/description/assets/misc/cybrosys-logo.png b/custom_pivot_report/static/description/assets/misc/cybrosys-logo.png new file mode 100644 index 000000000..cc3cc0ccf Binary files /dev/null and b/custom_pivot_report/static/description/assets/misc/cybrosys-logo.png differ diff --git a/custom_pivot_report/static/description/assets/misc/features.png b/custom_pivot_report/static/description/assets/misc/features.png new file mode 100644 index 000000000..b41769f77 Binary files /dev/null and b/custom_pivot_report/static/description/assets/misc/features.png differ diff --git a/custom_pivot_report/static/description/assets/misc/logo.png b/custom_pivot_report/static/description/assets/misc/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/custom_pivot_report/static/description/assets/misc/logo.png differ diff --git a/custom_pivot_report/static/description/assets/misc/pictures.png b/custom_pivot_report/static/description/assets/misc/pictures.png new file mode 100644 index 000000000..56d255fe9 Binary files /dev/null and b/custom_pivot_report/static/description/assets/misc/pictures.png differ diff --git a/custom_pivot_report/static/description/assets/misc/pie-chart.png b/custom_pivot_report/static/description/assets/misc/pie-chart.png new file mode 100644 index 000000000..426e05244 Binary files /dev/null and b/custom_pivot_report/static/description/assets/misc/pie-chart.png differ diff --git a/custom_pivot_report/static/description/assets/misc/right-arrow.png b/custom_pivot_report/static/description/assets/misc/right-arrow.png new file mode 100644 index 000000000..730984a06 Binary files /dev/null and b/custom_pivot_report/static/description/assets/misc/right-arrow.png differ diff --git a/custom_pivot_report/static/description/assets/misc/star.png b/custom_pivot_report/static/description/assets/misc/star.png new file mode 100644 index 000000000..2eb9ab29f Binary files /dev/null and b/custom_pivot_report/static/description/assets/misc/star.png differ diff --git a/custom_pivot_report/static/description/assets/misc/support.png b/custom_pivot_report/static/description/assets/misc/support.png new file mode 100644 index 000000000..4f18b8b82 Binary files /dev/null and b/custom_pivot_report/static/description/assets/misc/support.png differ diff --git a/custom_pivot_report/static/description/assets/misc/whatsapp.png b/custom_pivot_report/static/description/assets/misc/whatsapp.png new file mode 100644 index 000000000..d513a5356 Binary files /dev/null and b/custom_pivot_report/static/description/assets/misc/whatsapp.png differ diff --git a/custom_pivot_report/static/description/assets/modules/1.png b/custom_pivot_report/static/description/assets/modules/1.png new file mode 100644 index 000000000..5238bdeab Binary files /dev/null and b/custom_pivot_report/static/description/assets/modules/1.png differ diff --git a/custom_pivot_report/static/description/assets/modules/2.png b/custom_pivot_report/static/description/assets/modules/2.png new file mode 100644 index 000000000..1ae7cfe3b Binary files /dev/null and b/custom_pivot_report/static/description/assets/modules/2.png differ diff --git a/custom_pivot_report/static/description/assets/modules/3.png b/custom_pivot_report/static/description/assets/modules/3.png new file mode 100644 index 000000000..3c3ff1afb Binary files /dev/null and b/custom_pivot_report/static/description/assets/modules/3.png differ diff --git a/custom_pivot_report/static/description/assets/modules/4.png b/custom_pivot_report/static/description/assets/modules/4.png new file mode 100644 index 000000000..3fae4631e Binary files /dev/null and b/custom_pivot_report/static/description/assets/modules/4.png differ diff --git a/custom_pivot_report/static/description/assets/modules/5.gif b/custom_pivot_report/static/description/assets/modules/5.gif new file mode 100644 index 000000000..2a5f8e659 Binary files /dev/null and b/custom_pivot_report/static/description/assets/modules/5.gif differ diff --git a/custom_pivot_report/static/description/assets/modules/6.png b/custom_pivot_report/static/description/assets/modules/6.png new file mode 100644 index 000000000..7f2815273 Binary files /dev/null and b/custom_pivot_report/static/description/assets/modules/6.png differ diff --git a/custom_pivot_report/static/description/assets/modules/budget_image.png b/custom_pivot_report/static/description/assets/modules/budget_image.png new file mode 100644 index 000000000..b50130c7d Binary files /dev/null and b/custom_pivot_report/static/description/assets/modules/budget_image.png differ diff --git a/custom_pivot_report/static/description/assets/modules/credit_image.png b/custom_pivot_report/static/description/assets/modules/credit_image.png new file mode 100644 index 000000000..3ad04ecfd Binary files /dev/null and b/custom_pivot_report/static/description/assets/modules/credit_image.png differ diff --git a/custom_pivot_report/static/description/assets/modules/employee_image.png b/custom_pivot_report/static/description/assets/modules/employee_image.png new file mode 100644 index 000000000..30ad58232 Binary files /dev/null and b/custom_pivot_report/static/description/assets/modules/employee_image.png differ diff --git a/custom_pivot_report/static/description/assets/modules/export_image.png b/custom_pivot_report/static/description/assets/modules/export_image.png new file mode 100644 index 000000000..492980ad0 Binary files /dev/null and b/custom_pivot_report/static/description/assets/modules/export_image.png differ diff --git a/custom_pivot_report/static/description/assets/modules/gantt_image.png b/custom_pivot_report/static/description/assets/modules/gantt_image.png new file mode 100644 index 000000000..1ae7cfe3b Binary files /dev/null and b/custom_pivot_report/static/description/assets/modules/gantt_image.png differ diff --git a/custom_pivot_report/static/description/assets/modules/quotation_image.png b/custom_pivot_report/static/description/assets/modules/quotation_image.png new file mode 100644 index 000000000..499b1a72f Binary files /dev/null and b/custom_pivot_report/static/description/assets/modules/quotation_image.png differ diff --git a/custom_pivot_report/static/description/assets/screenshots/hero.gif b/custom_pivot_report/static/description/assets/screenshots/hero.gif new file mode 100644 index 000000000..c840ea790 Binary files /dev/null and b/custom_pivot_report/static/description/assets/screenshots/hero.gif differ diff --git a/custom_pivot_report/static/description/assets/screenshots/img1.png b/custom_pivot_report/static/description/assets/screenshots/img1.png new file mode 100644 index 000000000..fdc8834f2 Binary files /dev/null and b/custom_pivot_report/static/description/assets/screenshots/img1.png differ diff --git a/custom_pivot_report/static/description/assets/screenshots/img2.1.png b/custom_pivot_report/static/description/assets/screenshots/img2.1.png new file mode 100644 index 000000000..d30c0b4a0 Binary files /dev/null and b/custom_pivot_report/static/description/assets/screenshots/img2.1.png differ diff --git a/custom_pivot_report/static/description/assets/screenshots/img2.png b/custom_pivot_report/static/description/assets/screenshots/img2.png new file mode 100644 index 000000000..d23bd4ab8 Binary files /dev/null and b/custom_pivot_report/static/description/assets/screenshots/img2.png differ diff --git a/custom_pivot_report/static/description/assets/screenshots/img3.1.png b/custom_pivot_report/static/description/assets/screenshots/img3.1.png new file mode 100644 index 000000000..ff7c8c8f0 Binary files /dev/null and b/custom_pivot_report/static/description/assets/screenshots/img3.1.png differ diff --git a/custom_pivot_report/static/description/assets/screenshots/img3.png b/custom_pivot_report/static/description/assets/screenshots/img3.png new file mode 100644 index 000000000..dc162f3b3 Binary files /dev/null and b/custom_pivot_report/static/description/assets/screenshots/img3.png differ diff --git a/custom_pivot_report/static/description/assets/screenshots/img4.1.png b/custom_pivot_report/static/description/assets/screenshots/img4.1.png new file mode 100644 index 000000000..f3d612af3 Binary files /dev/null and b/custom_pivot_report/static/description/assets/screenshots/img4.1.png differ diff --git a/custom_pivot_report/static/description/assets/screenshots/img4.png b/custom_pivot_report/static/description/assets/screenshots/img4.png new file mode 100644 index 000000000..ff7c8c8f0 Binary files /dev/null and b/custom_pivot_report/static/description/assets/screenshots/img4.png differ diff --git a/custom_pivot_report/static/description/banner.png b/custom_pivot_report/static/description/banner.png new file mode 100644 index 000000000..a3060d797 Binary files /dev/null and b/custom_pivot_report/static/description/banner.png differ diff --git a/custom_pivot_report/static/description/icon.png b/custom_pivot_report/static/description/icon.png new file mode 100644 index 000000000..09c812f48 Binary files /dev/null and b/custom_pivot_report/static/description/icon.png differ diff --git a/custom_pivot_report/static/description/index.html b/custom_pivot_report/static/description/index.html new file mode 100644 index 000000000..44b5f77e1 --- /dev/null +++ b/custom_pivot_report/static/description/index.html @@ -0,0 +1,619 @@ +
+ +
+ +
+
+ Community +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+ + + +

Custom Pivot Report

+

Create Custom Pivot Report For Any Model Without Coding.

+ + + +
+ + +
+
+ +
+

Explore This + Module

+
+ + + + +
+
+ +
+

Overview +

+
+
+
+ In the Custom Pivot Report App, user can configure the menu, fields, and security for pivot reports. A + user can easily set rows and measures for the pivot report. +
+
+ + + +
+
+ +
+

Features +

+
+
+
+
+ +
+ Create custom pivot report for any module + +
+
+
+ +
+ Place your custom report under any menu + +
+
+ + + + + + + + + + + + + + + + +
+
+
+ +
+ Set custom row and measure for pivot + +
+
+
+ +
+ Set security group for report. + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+ +
+

Screenshots +

+
+
+
+ +
+

Custom Report Creation

+

User can create custom pivot report for any module and specify the report menu position. Along with + this, user can provide security group and select custom fields for the report.

+ +
+ +
+

User added report menu

+

+ +
+ +
+

Custom created pivot report

+

+ +
+ +
+

+

+ +
+ +
+

+

+ +
+ +
+

+

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

Related + Products +

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

Our Services +

+
+ +
+
+
+
+ +
+
+ Odoo + Customization
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Support
+
+ + +
+
+ +
+
+ Hire + Odoo + Developer
+
+ +
+
+ +
+
+ Odoo + Integration
+
+ +
+
+ +
+
+ Odoo + Migration
+
+ + +
+
+ +
+
+ Odoo + Consultancy
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Licensing Consultancy
+
+
+ +
+ + + + + +
+
+ +
+

Our + Industries +

+
+ +
+
+
+
+ +
+ Trading +
+

+ Easily procure + and + sell your products

+
+
+ +
+
+ +
+ POS +
+

+ Easy + configuration + and convivial experience

+
+
+ +
+
+ +
+ Education +
+

+ A platform for + educational management

+
+
+ +
+
+ +
+ Manufacturing +
+

+ Plan, track and + schedule your operations

+
+
+ +
+
+ +
+ E-commerce & Website +
+

+ Mobile + friendly, + awe-inspiring product pages

+
+
+ +
+
+ +
+ Service Management +
+

+ Keep track of + services and invoice

+
+
+ +
+
+ +
+ Restaurant +
+

+ Run your bar or + restaurant methodically

+
+
+ +
+
+ +
+ Hotel Management +
+

+ An + all-inclusive + hotel management application

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

Support +

+
+
+
+
+
+
+ +
+
+

Need Help?

+

Got questions or need help? Get in touch.

+ +

+ odoo@cybrosys.com

+
+
+
+
+
+
+
+ +
+
+

WhatsApp

+

Say hi to us on WhatsApp!

+ +

+91 86068 + 27707

+
+
+
+
+
+
+
+ +
+
+
+ \ No newline at end of file diff --git a/custom_pivot_report/views/custom_report.xml b/custom_pivot_report/views/custom_report.xml new file mode 100644 index 000000000..e338fa5e5 --- /dev/null +++ b/custom_pivot_report/views/custom_report.xml @@ -0,0 +1,67 @@ + + + + + Custom Reports + ir.actions.act_window + custom.report + tree,form + +

+ Create record +

+
+
+ + Custom Report + custom.report + tree + + + + + + + + + + Custom Report + custom.report + form + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+
\ No newline at end of file diff --git a/purchase_report_generator/README.rst b/purchase_report_generator/README.rst new file mode 100644 index 000000000..afba092cd --- /dev/null +++ b/purchase_report_generator/README.rst @@ -0,0 +1,43 @@ +Purchase All In One Report Generator +==================================== +* Purchase All In One Dynamic Report For Odoo 16 community And Enterprise editions + +Installation +============ + - www.odoo.com/documentation/16.0/setup/install.html + - Install our custom addon + +License +------- +General Public License, Version 3 (LGPL v3). +(https://www.odoo.com/documentation/user/13.0/legal/licenses/licenses.html) + +Company +------- +* 'Cybrosys Techno Solutions `__ + +Credits +------- +* Developer: +(v15) Sreelakshmi @ Cybrosys +(v16) Swaroop N P @ Cybrosys + + +Contacts +-------- +* Mail Contact : odoo@cybrosys.com + +Bug Tracker +----------- +Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. + +Maintainer +========== +This module is maintained by Cybrosys Technologies. + +For support and more information, please visit https://www.cybrosys.com + +Further information +=================== +HTML Description: ``__ + diff --git a/purchase_report_generator/__init__.py b/purchase_report_generator/__init__.py new file mode 100644 index 000000000..4a84e60d7 --- /dev/null +++ b/purchase_report_generator/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies(). +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import models +from . import report +from . import controllers diff --git a/purchase_report_generator/__manifest__.py b/purchase_report_generator/__manifest__.py new file mode 100644 index 000000000..c5df15c1f --- /dev/null +++ b/purchase_report_generator/__manifest__.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies(). +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# + +{ + 'name': 'Purchase All In One Report Generator', + 'version': '16.0.1.0.0', + 'summary': "Dynamic Purchase Report Maker", + 'description': "Dynamic Purchase Report Maker", + 'category': 'Purchase', + 'author': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'website': 'https://www.cybrosys.com', + 'depends': ['purchase'], + 'data': [ + 'security/ir.model.access.csv', + 'views/purchase_report.xml', + 'report/purchase_order_report.xml' + ], + 'assets': { + 'web.assets_backend': [ + 'purchase_report_generator/static/src/js/purchase_report.js', + 'purchase_report_generator/static/src/css/purchase_report.css', + 'purchase_report_generator/static/src/xml/purchase_report_view.xml', + ], + + }, + 'images': ['static/description/banner.png'], + 'license': 'AGPL-3', + 'installable': True, + 'auto_install': False, + 'auto_install': False, +} diff --git a/purchase_report_generator/controllers/__init__.py b/purchase_report_generator/controllers/__init__.py new file mode 100644 index 000000000..8edd0b2d2 --- /dev/null +++ b/purchase_report_generator/controllers/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies(). +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import main diff --git a/purchase_report_generator/controllers/main.py b/purchase_report_generator/controllers/main.py new file mode 100644 index 000000000..da449b9ad --- /dev/null +++ b/purchase_report_generator/controllers/main.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies(). +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# + +import json +from odoo import http +from odoo.http import content_disposition, request +# from odoo.addons.web.controllers.main import _serialize_exception +from odoo.tools import html_escape + + +class TBXLSXReportController(http.Controller): + @http.route('/purchase_dynamic_xlsx_reports', type='http', auth='user', methods=['POST'], csrf=False) + def get_report_xlsx(self, model, options, output_format, report_data, report_name, dfr_data, **kw): + uid = request.session.uid + report_obj = request.env[model].with_user(uid) + dfr_data = dfr_data + options = options + token = 'dummy-because-api-expects-one' + try: + if output_format == 'xlsx': + response = request.make_response( + None, + headers=[ + ('Content-Type', 'application/vnd.ms-excel'), + ('Content-Disposition', content_disposition(report_name + '.xlsx')) + ] + ) + report_obj.get_purchase_xlsx_report(options, response, report_data, dfr_data) + response.set_cookie('fileToken', token) + return response + except Exception as e: + # se = _serialize_exception(e) + #testssss + se=0 + error = { + 'code': 200, + 'message': 'Odoo Server Error', + 'data': se + } + return request.make_response(html_escape(json.dumps(error))) diff --git a/purchase_report_generator/doc/RELEASE_NOTES.md b/purchase_report_generator/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..4b1f40c27 --- /dev/null +++ b/purchase_report_generator/doc/RELEASE_NOTES.md @@ -0,0 +1,7 @@ +## Module + +#### 15.12.2022 +#### Version 16.0.1.0.0 +#### ADD +Initial Commit + diff --git a/purchase_report_generator/models/__init__.py b/purchase_report_generator/models/__init__.py new file mode 100644 index 000000000..10ad2e518 --- /dev/null +++ b/purchase_report_generator/models/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies(). +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import purchase_report diff --git a/purchase_report_generator/models/purchase_report.py b/purchase_report_generator/models/purchase_report.py new file mode 100644 index 000000000..1cde75a4f --- /dev/null +++ b/purchase_report_generator/models/purchase_report.py @@ -0,0 +1,508 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies(). +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from odoo import models, fields, api +import io +import json + +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class DynamicPurchaseReport(models.Model): + _name = "dynamic.purchase.report" + + purchase_report = fields.Char(string="Purchase Report") + date_from = fields.Datetime(string="Date From") + date_to = fields.Datetime(string="Date to") + report_type = fields.Selection([ + ('report_by_order', 'Report By Order'), + ('report_by_order_detail', 'Report By Order Detail'), + ('report_by_product', 'Report By Product'), + ('report_by_categories', 'Report By Categories'), + ('report_by_purchase_representative', + 'Report By Purchase Representative'), + ('report_by_state', 'Report By State')], default='report_by_order') + + @api.model + def purchase_report(self, option): + orders = self.env['purchase.order'].search([]) + report_values = self.env['dynamic.purchase.report'].search( + [('id', '=', option[0])]) + data = { + 'report_type': report_values.report_type, + 'model': self, + } + + if report_values.date_from: + data.update({ + 'date_from': report_values.date_from, + }) + if report_values.date_to: + data.update({ + 'date_to': report_values.date_to, + }) + filters = self.get_filter(option) + report = self._get_report_values(data) + lines = self._get_report_values(data).get('PURCHASE') + + return { + 'name': "Purchase Orders", + 'type': 'ir.actions.client', + 'tag': 's_r', + 'orders': data, + 'filters': filters, + 'report_lines': lines, + } + + def get_filter(self, option): + data = self.get_filter_data(option) + filters = {} + if data.get('report_type') == 'report_by_order': + filters['report_type'] = 'Report By Order' + elif data.get('report_type') == 'report_by_order_detail': + filters['report_type'] = 'Report By Order Detail' + elif data.get('report_type') == 'report_by_product': + filters['report_type'] = 'Report By Product' + elif data.get('report_type') == 'report_by_categories': + filters['report_type'] = 'Report By Categories' + elif data.get('report_type') == 'report_by_purchase_representative': + filters['report_type'] = 'Report By Purchase Representative' + elif data.get('report_type') == 'report_by_state': + filters['report_type'] = 'Report By State' + else: + filters['report_type'] = 'report_by_order' + + return filters + + def get_filter_data(self, option): + r = self.env['dynamic.purchase.report'].search([('id', '=', option[0])]) + default_filters = {} + + filter_dict = { + 'report_type': r.report_type, + } + filter_dict.update(default_filters) + return filter_dict + + @api.model + def create(self, vals): + + res = super(DynamicPurchaseReport, self).create(vals) + return res + + def write(self, vals): + res = super(DynamicPurchaseReport, self).write(vals) + return res + + def _get_report_sub_lines(self, data, report, date_from, date_to): + report_sub_lines = [] + new_filter = None + + if data.get('report_type') == 'report_by_order': + query = ''' + select l.name,l.date_order,l.partner_id,l.amount_total,l.notes,l.user_id,res_partner.name as partner, + res_users.partner_id as user_partner,sum(purchase_order_line.product_qty),l.id as id, + (SELECT res_partner.name as salesman FROM res_partner WHERE res_partner.id = res_users.partner_id) + from purchase_order as l + left join res_partner on l.partner_id = res_partner.id + left join res_users on l.user_id = res_users.id + left join purchase_order_line on l.id = purchase_order_line.order_id + ''' + term = 'Where ' + if data.get('date_from'): + query += "Where l.date_order >= '%s' " % data.get('date_from') + term = 'AND ' + if data.get('date_to'): + query += term + "l.date_order <= '%s' " % data.get('date_to') + query += "group by l.user_id,res_users.partner_id,res_partner.name,l.partner_id,l.date_order,l.name,l.amount_total,l.notes,l.id" + self._cr.execute(query) + report_by_order = self._cr.dictfetchall() + report_sub_lines.append(report_by_order) + elif data.get('report_type') == 'report_by_order_detail': + query = ''' + + select l.name,l.date_order,l.partner_id,l.amount_total,l.notes,l.user_id,res_partner.name as partner, + res_users.partner_id as user_partner,sum(purchase_order_line.product_qty), purchase_order_line.name as product, purchase_order_line.price_unit,purchase_order_line.price_subtotal,l.amount_total,purchase_order_line.product_id,product_product.default_code, + (SELECT res_partner.name as salesman FROM res_partner WHERE res_partner.id = res_users.partner_id) + from purchase_order as l + left join res_partner on l.partner_id = res_partner.id + left join res_users on l.user_id = res_users.id + left join purchase_order_line on l.id = purchase_order_line.order_id + left join product_product on purchase_order_line.product_id = product_product.id + ''' + term = 'Where ' + if data.get('date_from'): + query += "Where l.date_order >= '%s' " % data.get('date_from') + term = 'AND ' + if data.get('date_to'): + query += term + "l.date_order <= '%s' " % data.get('date_to') + query += "group by l.user_id,res_users.partner_id,res_partner.name,l.partner_id,l.date_order,l.name,l.amount_total,l.notes,purchase_order_line.name,purchase_order_line.price_unit,purchase_order_line.price_subtotal,l.amount_total,purchase_order_line.product_id,product_product.default_code" + self._cr.execute(query) + report_by_order_details = self._cr.dictfetchall() + report_sub_lines.append(report_by_order_details) + elif data.get('report_type') == 'report_by_product': + query = ''' + select l.amount_total,sum(purchase_order_line.product_qty) as qty, purchase_order_line.name as product, purchase_order_line.price_unit,product_product.default_code,product_category.name + from purchase_order as l + left join purchase_order_line on l.id = purchase_order_line.order_id + left join product_product on purchase_order_line.product_id = product_product.id + left join product_template on purchase_order_line.product_id = product_template.id + left join product_category on product_category.id = product_template.categ_id + ''' + term = 'Where ' + if data.get('date_from'): + query += "Where l.date_order >= '%s' " % data.get('date_from') + term = 'AND ' + if data.get('date_to'): + query += term + "l.date_order <= '%s' " % data.get('date_to') + query += "group by l.amount_total,purchase_order_line.name,purchase_order_line.price_unit,purchase_order_line.product_id,product_product.default_code,product_template.categ_id,product_category.name" + self._cr.execute(query) + report_by_product = self._cr.dictfetchall() + report_sub_lines.append(report_by_product) + elif data.get('report_type') == 'report_by_categories': + query = ''' + select product_category.name,sum(l.product_qty) as qty,sum(l.price_subtotal) as amount_total + from purchase_order_line as l + left join product_template on l.product_id = product_template.id + left join product_category on product_category.id = product_template.categ_id + left join purchase_order on l.order_id = purchase_order.id + ''' + term = 'Where ' + if data.get('date_from'): + query += "Where pos_order.date_order >= '%s' " % data.get( + 'date_from') + term = 'AND ' + if data.get('date_to'): + query += term + "pos_order.date_order <= '%s' " % data.get( + 'date_to') + query += "group by product_category.name" + self._cr.execute(query) + report_by_categories = self._cr.dictfetchall() + report_sub_lines.append(report_by_categories) + elif data.get('report_type') == 'report_by_purchase_representative': + query = ''' + select res_partner.name,sum(purchase_order_line.product_qty) as qty,sum(purchase_order_line.price_subtotal) as amount,count(l.id) as order + from purchase_order as l + left join res_users on l.user_id = res_users.id + left join res_partner on res_users.partner_id = res_partner.id + left join purchase_order_line on l.id = purchase_order_line.order_id + ''' + term = 'Where ' + if data.get('date_from'): + query += "Where l.date_order >= '%s' " % data.get('date_from') + term = 'AND ' + if data.get('date_to'): + query += term + "l.date_order <= '%s' " % data.get('date_to') + query += "group by res_partner.name" + self._cr.execute(query) + report_by_purchase_representative = self._cr.dictfetchall() + report_sub_lines.append(report_by_purchase_representative) + + elif data.get('report_type') == 'report_by_state': + query = ''' + select l.state,sum(purchase_order_line.product_qty) as qty,sum(purchase_order_line.price_subtotal) as amount,count(l.id) as order + from purchase_order as l + left join res_users on l.user_id = res_users.id + left join res_partner on res_users.partner_id = res_partner.id + left join purchase_order_line on l.id = purchase_order_line.order_id + ''' + term = 'Where ' + if data.get('date_from'): + query += "Where so.date_order >= '%s' " % data.get('date_from') + term = 'AND ' + if data.get('date_to'): + query += term + "so.date_order <= '%s' " % data.get('date_to') + query += "group by l.state" + self._cr.execute(query) + report_by_state = self._cr.dictfetchall() + + report_sub_lines.append(report_by_state) + return report_sub_lines + + def _get_report_values(self, data): + docs = data['model'] + date_from = data.get('date_from') + date_to = data.get('date_to') + if data['report_type'] == 'report_by_order_detail': + report = ['Report By Order Detail'] + elif data['report_type'] == 'report_by_product': + report = ['Report By Product'] + elif data['report_type'] == 'report_by_categories': + report = ['Report By Categories'] + elif data['report_type'] == 'report_by_purchase_representative': + report = ['Report By Purchase Representative'] + elif data['report_type'] == 'report_by_state': + report = ['Report By State'] + else: + report = ['Report By Order'] + + if data.get('report_type'): + report_res = \ + self._get_report_sub_lines(data, report, date_from, date_to)[0] + else: + report_res = self._get_report_sub_lines(data, report, date_from, + date_to) + + return { + 'doc_ids': self.ids, + 'docs': docs, + 'PURCHASE': report_res, + + } + + def get_purchase_xlsx_report(self, data, response, report_data, dfr_data): + report_data_main = json.loads(report_data) + output = io.BytesIO() + filters = json.loads(data) + + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + head = workbook.add_format({'align': 'center', 'bold': True, + 'font_size': '20px'}) + sub_heading = workbook.add_format( + {'align': 'center', 'bold': True, 'font_size': '10px', + 'border': 1, + 'border_color': 'black'}) + heading = workbook.add_format( + {'align': 'center', 'bold': True, 'font_size': '10px', + 'border': 2, + 'border_color': 'black'}) + txt = workbook.add_format({'font_size': '10px', 'border': 1}) + txt_l = workbook.add_format( + {'font_size': '10px', 'border': 1, 'bold': True}) + txt_v = workbook.add_format( + {'align': 'right', 'font_size': '10px', 'border': 1}) + sheet.merge_range('A2:H3', + 'Purchase Report', + head) + date_head = workbook.add_format({'align': 'center', 'bold': True, + 'font_size': '10px'}) + date_style = workbook.add_format({'align': 'center', + 'font_size': '10px'}) + + if filters.get('report_type') == 'report_by_order': + + sheet.merge_range('B5:D5', 'Report Type: ' + + filters.get('report_type'), txt_l) + + sheet.write('A7', 'Order', heading) + sheet.write('B7', 'Date Order', heading) + sheet.write('C7', 'Customer', heading) + sheet.write('D7', 'Purchase Representative', heading) + sheet.write('E7', 'Total Qty', heading) + sheet.write('F7', 'Amount Total', heading) + + lst = [] + for rec in report_data_main[0]: + lst.append(rec) + row = 6 + col = 0 + sheet.set_column(3, 0, 15) + sheet.set_column(4, 1, 15) + sheet.set_column(5, 2, 15) + sheet.set_column(6, 3, 15) + sheet.set_column(7, 4, 15) + sheet.set_column(8, 5, 15) + + for rec_data in report_data_main: + one_lst = [] + two_lst = [] + row += 1 + sheet.write(row, col, rec_data['name'], txt_l) + sheet.write(row, col + 1, rec_data['date_order'], txt_l) + sheet.write(row, col + 2, rec_data['partner'], txt_l) + sheet.write(row, col + 3, rec_data['salesman'], txt_l) + sheet.write(row, col + 4, rec_data['sum'], txt_l) + sheet.write(row, col + 5, rec_data['amount_total'], txt_l) + + if filters.get('report_type') == 'report_by_order_detail': + + sheet.merge_range('B5:D5', 'Report Type: ' + + filters.get('report_type'), txt_l) + + sheet.write('A7', 'Order', heading) + sheet.write('B7', 'Date Order', heading) + sheet.write('C7', 'Customer', heading) + sheet.write('D7', 'Purchase Representative', heading) + sheet.write('E7', 'Product Code', heading) + sheet.write('F7', 'Product Name', heading) + sheet.write('G7', 'Price unit', heading) + sheet.write('H7', 'Qty', heading) + sheet.write('I7', 'Price Total', heading) + + lst = [] + for rec in report_data_main[0]: + lst.append(rec) + row = 6 + col = 0 + sheet.set_column(3, 0, 15) + sheet.set_column(4, 1, 15) + sheet.set_column(5, 2, 15) + sheet.set_column(6, 3, 15) + sheet.set_column(7, 4, 15) + sheet.set_column(8, 5, 15) + sheet.set_column(9, 6, 15) + sheet.set_column(10, 7, 15) + sheet.set_column(11, 8, 15) + sheet.set_column(12, 9, 15) + + for rec_data in report_data_main: + one_lst = [] + two_lst = [] + row += 1 + sheet.write(row, col, rec_data['name'], txt_l) + sheet.write(row, col + 1, rec_data['date_order'], txt_l) + sheet.write(row, col + 2, rec_data['partner'], txt_l) + sheet.write(row, col + 3, rec_data['salesman'], txt_l) + sheet.write(row, col + 4, rec_data['default_code'], txt_l) + sheet.write(row, col + 5, rec_data['product'], txt_l) + sheet.write(row, col + 6, rec_data['price_unit'], txt_l) + sheet.write(row, col + 7, rec_data['sum'], txt_l) + sheet.write(row, col + 8, rec_data['amount_total'], txt_l) + + if filters.get('report_type') == 'report_by_product': + + sheet.merge_range('B5:D5', 'Report Type: ' + + filters.get('report_type'), txt_l) + + sheet.write('A7', 'Category', heading) + sheet.write('B7', 'Product Code', heading) + sheet.write('C7', 'Product Name', heading) + sheet.write('D7', 'Qty', heading) + sheet.write('E7', 'Amount Total', heading) + + lst = [] + for rec in report_data_main[0]: + lst.append(rec) + row = 6 + col = 0 + sheet.set_column(3, 0, 15) + sheet.set_column(4, 1, 15) + sheet.set_column(5, 2, 15) + sheet.set_column(6, 3, 15) + sheet.set_column(7, 4, 15) + + for rec_data in report_data_main: + one_lst = [] + two_lst = [] + row += 1 + sheet.write(row, col, rec_data['name'], txt_l) + sheet.write(row, col + 1, rec_data['default_code'], txt_l) + sheet.write(row, col + 2, rec_data['product'], txt_l) + sheet.write(row, col + 3, rec_data['qty'], txt_l) + sheet.write(row, col + 4, rec_data['amount_total'], txt_l) + + if filters.get('report_type') == 'report_by_categories': + + sheet.merge_range('B5:D5', 'Report Type: ' + + filters.get('report_type'), txt_l) + + sheet.write('B7', 'Category', heading) + sheet.write('C7', 'Qty', heading) + sheet.write('D7', 'Amount Total', heading) + + lst = [] + for rec in report_data_main[0]: + lst.append(rec) + row = 6 + col = 1 + sheet.set_column(3, 1, 15) + sheet.set_column(4, 2, 15) + sheet.set_column(5, 3, 15) + + for rec_data in report_data_main: + one_lst = [] + two_lst = [] + row += 1 + sheet.write(row, col, rec_data['name'], txt_l) + sheet.write(row, col + 1, rec_data['qty'], txt_l) + sheet.write(row, col + 2, rec_data['amount_total'], txt_l) + + if filters.get('report_type') == 'report_by_purchase_representative': + + sheet.merge_range('B5:D5', 'Report Type: ' + + filters.get('report_type'), txt_l) + + sheet.write('A7', 'Purchase Representative', heading) + sheet.write('B7', 'Total Order', heading) + sheet.write('C7', 'Total Qty', heading) + sheet.write('D7', 'Total Amount', heading) + + lst = [] + for rec in report_data_main[0]: + lst.append(rec) + row = 6 + col = 0 + sheet.set_column(3, 0, 15) + sheet.set_column(4, 1, 15) + sheet.set_column(5, 2, 15) + sheet.set_column(6, 3, 15) + + for rec_data in report_data_main: + one_lst = [] + two_lst = [] + row += 1 + sheet.write(row, col, rec_data['name'], txt_l) + sheet.write(row, col + 1, rec_data['order'], txt_l) + sheet.write(row, col + 2, rec_data['qty'], txt_l) + sheet.write(row, col + 3, rec_data['amount'], txt_l) + + if filters.get('report_type') == 'report_by_state': + + sheet.merge_range('B5:D5', 'Report Type: ' + + filters.get('report_type'), txt_l) + + sheet.write('A7', 'State', heading) + sheet.write('B7', 'Total Count', heading) + sheet.write('C7', 'Quantity', heading) + sheet.write('D7', 'Amount', heading) + + lst = [] + for rec in report_data_main[0]: + lst.append(rec) + row = 6 + col = 0 + sheet.set_column(3, 0, 15) + sheet.set_column(4, 1, 15) + sheet.set_column(5, 2, 15) + sheet.set_column(6, 3, 15) + + for rec_data in report_data_main: + one_lst = [] + two_lst = [] + row += 1 + if rec_data['state'] == 'draft': + sheet.write(row, col, 'Quotation', txt_l) + elif rec_data['state'] == 'sent': + sheet.write(row, col, 'Quotation Sent', txt_l) + elif rec_data['state'] == 'purchase': + sheet.write(row, col, 'Purchase Order', txt_l) + sheet.write(row, col + 1, rec_data['order'], txt_l) + sheet.write(row, col + 2, rec_data['qty'], txt_l) + sheet.write(row, col + 3, rec_data['amount'], txt_l) + + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/purchase_report_generator/report/__init__.py b/purchase_report_generator/report/__init__.py new file mode 100644 index 000000000..63bd9084d --- /dev/null +++ b/purchase_report_generator/report/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies(). +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import purchase_order_report diff --git a/purchase_report_generator/report/purchase_order_report.py b/purchase_report_generator/report/purchase_order_report.py new file mode 100644 index 000000000..d561c9bcf --- /dev/null +++ b/purchase_report_generator/report/purchase_order_report.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies(). +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from odoo import api, models, _ + + +class PurchaseOrder(models.AbstractModel): + _name = 'report.purchase_report_generator.purchase_order_report' + + @api.model + def _get_report_values(self, docids, data=None): + if self.env.context.get('purchase_order_report'): + + if data.get('report_data'): + data.update({'report_main_line_data': data.get('report_data')['report_lines'], + 'Filters': data.get('report_data')['filters'], + 'company': self.env.company, + }) + return data diff --git a/purchase_report_generator/report/purchase_order_report.xml b/purchase_report_generator/report/purchase_order_report.xml new file mode 100644 index 000000000..c4214fb13 --- /dev/null +++ b/purchase_report_generator/report/purchase_order_report.xml @@ -0,0 +1,461 @@ + + + + + + + + + + + + + + + + + + Purchase All In One Report + dynamic.purchase.report + qweb-pdf + purchase_report_generator.purchase_order_report + purchase_report_generator.purchase_order_report + + + \ No newline at end of file diff --git a/purchase_report_generator/security/ir.model.access.csv b/purchase_report_generator/security/ir.model.access.csv new file mode 100644 index 000000000..2fbc685dd --- /dev/null +++ b/purchase_report_generator/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_dynamic_purchase_report,access.dynamic.purchase.report,model_dynamic_purchase_report,base.group_user,1,1,1,1 diff --git a/purchase_report_generator/static/description/assets/icons/check.png b/purchase_report_generator/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/check.png differ diff --git a/purchase_report_generator/static/description/assets/icons/chevron.png b/purchase_report_generator/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/chevron.png differ diff --git a/purchase_report_generator/static/description/assets/icons/cogs.png b/purchase_report_generator/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/cogs.png differ diff --git a/purchase_report_generator/static/description/assets/icons/consultation.png b/purchase_report_generator/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/consultation.png differ diff --git a/purchase_report_generator/static/description/assets/icons/ecom-black.png b/purchase_report_generator/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/ecom-black.png differ diff --git a/purchase_report_generator/static/description/assets/icons/education-black.png b/purchase_report_generator/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/education-black.png differ diff --git a/purchase_report_generator/static/description/assets/icons/hotel-black.png b/purchase_report_generator/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/hotel-black.png differ diff --git a/purchase_report_generator/static/description/assets/icons/license.png b/purchase_report_generator/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/license.png differ diff --git a/purchase_report_generator/static/description/assets/icons/lifebuoy.png b/purchase_report_generator/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/lifebuoy.png differ diff --git a/purchase_report_generator/static/description/assets/icons/logo.png b/purchase_report_generator/static/description/assets/icons/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/logo.png differ diff --git a/purchase_report_generator/static/description/assets/icons/manufacturing-black.png b/purchase_report_generator/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/manufacturing-black.png differ diff --git a/purchase_report_generator/static/description/assets/icons/pos-black.png b/purchase_report_generator/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/pos-black.png differ diff --git a/purchase_report_generator/static/description/assets/icons/puzzle.png b/purchase_report_generator/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/puzzle.png differ diff --git a/purchase_report_generator/static/description/assets/icons/restaurant-black.png b/purchase_report_generator/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/restaurant-black.png differ diff --git a/purchase_report_generator/static/description/assets/icons/service-black.png b/purchase_report_generator/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/service-black.png differ diff --git a/purchase_report_generator/static/description/assets/icons/trading-black.png b/purchase_report_generator/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/trading-black.png differ diff --git a/purchase_report_generator/static/description/assets/icons/training.png b/purchase_report_generator/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/training.png differ diff --git a/purchase_report_generator/static/description/assets/icons/update.png b/purchase_report_generator/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/update.png differ diff --git a/purchase_report_generator/static/description/assets/icons/user.png b/purchase_report_generator/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/user.png differ diff --git a/purchase_report_generator/static/description/assets/icons/wrench.png b/purchase_report_generator/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/purchase_report_generator/static/description/assets/icons/wrench.png differ diff --git a/purchase_report_generator/static/description/assets/misc/categories.png b/purchase_report_generator/static/description/assets/misc/categories.png new file mode 100644 index 000000000..bedf1e0b1 Binary files /dev/null and b/purchase_report_generator/static/description/assets/misc/categories.png differ diff --git a/purchase_report_generator/static/description/assets/misc/check-box.png b/purchase_report_generator/static/description/assets/misc/check-box.png new file mode 100644 index 000000000..42caf24b9 Binary files /dev/null and b/purchase_report_generator/static/description/assets/misc/check-box.png differ diff --git a/purchase_report_generator/static/description/assets/misc/compass.png b/purchase_report_generator/static/description/assets/misc/compass.png new file mode 100644 index 000000000..d5fed8faa Binary files /dev/null and b/purchase_report_generator/static/description/assets/misc/compass.png differ diff --git a/purchase_report_generator/static/description/assets/misc/corporate.png b/purchase_report_generator/static/description/assets/misc/corporate.png new file mode 100644 index 000000000..2eb13edbf Binary files /dev/null and b/purchase_report_generator/static/description/assets/misc/corporate.png differ diff --git a/purchase_report_generator/static/description/assets/misc/customer-support.png b/purchase_report_generator/static/description/assets/misc/customer-support.png new file mode 100644 index 000000000..79efc72ed Binary files /dev/null and b/purchase_report_generator/static/description/assets/misc/customer-support.png differ diff --git a/purchase_report_generator/static/description/assets/misc/cybrosys-logo.png b/purchase_report_generator/static/description/assets/misc/cybrosys-logo.png new file mode 100644 index 000000000..cc3cc0ccf Binary files /dev/null and b/purchase_report_generator/static/description/assets/misc/cybrosys-logo.png differ diff --git a/purchase_report_generator/static/description/assets/misc/features.png b/purchase_report_generator/static/description/assets/misc/features.png new file mode 100644 index 000000000..b41769f77 Binary files /dev/null and b/purchase_report_generator/static/description/assets/misc/features.png differ diff --git a/purchase_report_generator/static/description/assets/misc/logo.png b/purchase_report_generator/static/description/assets/misc/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/purchase_report_generator/static/description/assets/misc/logo.png differ diff --git a/purchase_report_generator/static/description/assets/misc/pictures.png b/purchase_report_generator/static/description/assets/misc/pictures.png new file mode 100644 index 000000000..56d255fe9 Binary files /dev/null and b/purchase_report_generator/static/description/assets/misc/pictures.png differ diff --git a/purchase_report_generator/static/description/assets/misc/pie-chart.png b/purchase_report_generator/static/description/assets/misc/pie-chart.png new file mode 100644 index 000000000..426e05244 Binary files /dev/null and b/purchase_report_generator/static/description/assets/misc/pie-chart.png differ diff --git a/purchase_report_generator/static/description/assets/misc/right-arrow.png b/purchase_report_generator/static/description/assets/misc/right-arrow.png new file mode 100644 index 000000000..730984a06 Binary files /dev/null and b/purchase_report_generator/static/description/assets/misc/right-arrow.png differ diff --git a/purchase_report_generator/static/description/assets/misc/star.png b/purchase_report_generator/static/description/assets/misc/star.png new file mode 100644 index 000000000..2eb9ab29f Binary files /dev/null and b/purchase_report_generator/static/description/assets/misc/star.png differ diff --git a/purchase_report_generator/static/description/assets/misc/support.png b/purchase_report_generator/static/description/assets/misc/support.png new file mode 100644 index 000000000..4f18b8b82 Binary files /dev/null and b/purchase_report_generator/static/description/assets/misc/support.png differ diff --git a/purchase_report_generator/static/description/assets/misc/whatsapp.png b/purchase_report_generator/static/description/assets/misc/whatsapp.png new file mode 100644 index 000000000..d513a5356 Binary files /dev/null and b/purchase_report_generator/static/description/assets/misc/whatsapp.png differ diff --git a/purchase_report_generator/static/description/assets/modules/1.png b/purchase_report_generator/static/description/assets/modules/1.png new file mode 100644 index 000000000..5238bdeab Binary files /dev/null and b/purchase_report_generator/static/description/assets/modules/1.png differ diff --git a/purchase_report_generator/static/description/assets/modules/2.png b/purchase_report_generator/static/description/assets/modules/2.png new file mode 100644 index 000000000..1ae7cfe3b Binary files /dev/null and b/purchase_report_generator/static/description/assets/modules/2.png differ diff --git a/purchase_report_generator/static/description/assets/modules/3.png b/purchase_report_generator/static/description/assets/modules/3.png new file mode 100644 index 000000000..3c3ff1afb Binary files /dev/null and b/purchase_report_generator/static/description/assets/modules/3.png differ diff --git a/purchase_report_generator/static/description/assets/modules/4.png b/purchase_report_generator/static/description/assets/modules/4.png new file mode 100644 index 000000000..3fae4631e Binary files /dev/null and b/purchase_report_generator/static/description/assets/modules/4.png differ diff --git a/purchase_report_generator/static/description/assets/modules/5.gif b/purchase_report_generator/static/description/assets/modules/5.gif new file mode 100644 index 000000000..2a5f8e659 Binary files /dev/null and b/purchase_report_generator/static/description/assets/modules/5.gif differ diff --git a/purchase_report_generator/static/description/assets/modules/6.png b/purchase_report_generator/static/description/assets/modules/6.png new file mode 100644 index 000000000..7f2815273 Binary files /dev/null and b/purchase_report_generator/static/description/assets/modules/6.png differ diff --git a/purchase_report_generator/static/description/assets/modules/customer_so_history.png b/purchase_report_generator/static/description/assets/modules/customer_so_history.png new file mode 100644 index 000000000..a49d2394c Binary files /dev/null and b/purchase_report_generator/static/description/assets/modules/customer_so_history.png differ diff --git a/purchase_report_generator/static/description/assets/modules/individual_product_report.png b/purchase_report_generator/static/description/assets/modules/individual_product_report.png new file mode 100644 index 000000000..cd5d80e40 Binary files /dev/null and b/purchase_report_generator/static/description/assets/modules/individual_product_report.png differ diff --git a/purchase_report_generator/static/description/assets/modules/sale_discount_total.png b/purchase_report_generator/static/description/assets/modules/sale_discount_total.png new file mode 100644 index 000000000..924a65196 Binary files /dev/null and b/purchase_report_generator/static/description/assets/modules/sale_discount_total.png differ diff --git a/purchase_report_generator/static/description/assets/modules/sale_report_advanced.png b/purchase_report_generator/static/description/assets/modules/sale_report_advanced.png new file mode 100644 index 000000000..1c019bdd8 Binary files /dev/null and b/purchase_report_generator/static/description/assets/modules/sale_report_advanced.png differ diff --git a/purchase_report_generator/static/description/assets/modules/sale_report_format_editor.png b/purchase_report_generator/static/description/assets/modules/sale_report_format_editor.png new file mode 100644 index 000000000..c58491994 Binary files /dev/null and b/purchase_report_generator/static/description/assets/modules/sale_report_format_editor.png differ diff --git a/purchase_report_generator/static/description/assets/modules/sales_order_delivery_status.png b/purchase_report_generator/static/description/assets/modules/sales_order_delivery_status.png new file mode 100644 index 000000000..94c023fe1 Binary files /dev/null and b/purchase_report_generator/static/description/assets/modules/sales_order_delivery_status.png differ diff --git a/purchase_report_generator/static/description/assets/screenshots/hero.gif b/purchase_report_generator/static/description/assets/screenshots/hero.gif new file mode 100644 index 000000000..e122ffde0 Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/hero.gif differ diff --git a/purchase_report_generator/static/description/assets/screenshots/purchase_report1.png b/purchase_report_generator/static/description/assets/screenshots/purchase_report1.png new file mode 100644 index 000000000..e9211d5df Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/purchase_report1.png differ diff --git a/purchase_report_generator/static/description/assets/screenshots/purchase_report2.png b/purchase_report_generator/static/description/assets/screenshots/purchase_report2.png new file mode 100644 index 000000000..01617ec3f Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/purchase_report2.png differ diff --git a/purchase_report_generator/static/description/assets/screenshots/purchase_report3.png b/purchase_report_generator/static/description/assets/screenshots/purchase_report3.png new file mode 100644 index 000000000..4eb31120b Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/purchase_report3.png differ diff --git a/purchase_report_generator/static/description/assets/screenshots/purchase_report4.png b/purchase_report_generator/static/description/assets/screenshots/purchase_report4.png new file mode 100644 index 000000000..7e0f99c17 Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/purchase_report4.png differ diff --git a/purchase_report_generator/static/description/assets/screenshots/purchase_report5.png b/purchase_report_generator/static/description/assets/screenshots/purchase_report5.png new file mode 100644 index 000000000..ae443b3d5 Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/purchase_report5.png differ diff --git a/purchase_report_generator/static/description/assets/screenshots/purchase_report6.png b/purchase_report_generator/static/description/assets/screenshots/purchase_report6.png new file mode 100644 index 000000000..47e8d9f2b Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/purchase_report6.png differ diff --git a/purchase_report_generator/static/description/assets/screenshots/purchase_report7.png b/purchase_report_generator/static/description/assets/screenshots/purchase_report7.png new file mode 100644 index 000000000..bddb3439d Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/purchase_report7.png differ diff --git a/purchase_report_generator/static/description/assets/screenshots/sale_category_report.png b/purchase_report_generator/static/description/assets/screenshots/sale_category_report.png new file mode 100644 index 000000000..04a9d69a9 Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/sale_category_report.png differ diff --git a/purchase_report_generator/static/description/assets/screenshots/sale_detail_report.png b/purchase_report_generator/static/description/assets/screenshots/sale_detail_report.png new file mode 100644 index 000000000..6c579c46e Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/sale_detail_report.png differ diff --git a/purchase_report_generator/static/description/assets/screenshots/sale_order_report.png b/purchase_report_generator/static/description/assets/screenshots/sale_order_report.png new file mode 100644 index 000000000..0641e6ca8 Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/sale_order_report.png differ diff --git a/purchase_report_generator/static/description/assets/screenshots/sale_product_report.png b/purchase_report_generator/static/description/assets/screenshots/sale_product_report.png new file mode 100644 index 000000000..d49014dec Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/sale_product_report.png differ diff --git a/purchase_report_generator/static/description/assets/screenshots/sale_report.png b/purchase_report_generator/static/description/assets/screenshots/sale_report.png new file mode 100644 index 000000000..d7886d7c2 Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/sale_report.png differ diff --git a/purchase_report_generator/static/description/assets/screenshots/sale_salesman_report.png b/purchase_report_generator/static/description/assets/screenshots/sale_salesman_report.png new file mode 100644 index 000000000..84ff27ca0 Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/sale_salesman_report.png differ diff --git a/purchase_report_generator/static/description/assets/screenshots/sale_state_report.png b/purchase_report_generator/static/description/assets/screenshots/sale_state_report.png new file mode 100644 index 000000000..de880c23b Binary files /dev/null and b/purchase_report_generator/static/description/assets/screenshots/sale_state_report.png differ diff --git a/purchase_report_generator/static/description/banner.png b/purchase_report_generator/static/description/banner.png new file mode 100644 index 000000000..d0c3fec9e Binary files /dev/null and b/purchase_report_generator/static/description/banner.png differ diff --git a/purchase_report_generator/static/description/icon.png b/purchase_report_generator/static/description/icon.png new file mode 100644 index 000000000..d8d40f326 Binary files /dev/null and b/purchase_report_generator/static/description/icon.png differ diff --git a/purchase_report_generator/static/description/index.html b/purchase_report_generator/static/description/index.html new file mode 100644 index 000000000..729d5ae90 --- /dev/null +++ b/purchase_report_generator/static/description/index.html @@ -0,0 +1,622 @@ +
+ +
+ +
+
+ Community +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+ + + +

Purchase All In One Report Generator

+

Dynamic Purchase Report Maker

+ + + +
+ + +
+
+ +
+

Explore This + Module

+
+ + + + +
+
+ +
+

Overview +

+
+
+
+ This app useful to provide different Purchase Reports to do analysis. It shows the Purchase analysis of a company in many aspects such as by order,order details,salesman and so on. It can be filtered out based on different date ranges too. +
+
+ + + +
+
+ +
+

Features +

+
+
+
+
+ +
+ Community & Enterprise Support + Available in Odoo 15.0 Community and Enterprise. +
+
+
+ +
+ Generates Report for a specific date range. + +
+
+
+ +
+ Filtering based on different aspects + +
+
+ + + + + + + + +
+
+
+ +
+ Print Reports in both PDF and XLSX format. + . +
+
+
+ +
+ Drilled on approach. + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+ +
+

Screenshots +

+
+
+
+ +
+

All In One Purchase Report Menu

+

+ +
+ +
+

Purchase Report Based On Orders

+

+ +
+ +
+

Purchase Report Based On Order Details

+

+ +
+ +
+

Purchase Report Based On Product

+

+ +
+ +
+

Purchase Report Based On Categories

+

+ +
+ +
+

Purchase Report Based On Purchase Representativ

+

+ +
+ +
+

Purchase Report Based On State

+

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

Related + Products +

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

Our Services +

+
+ +
+
+
+
+ +
+
+ Odoo + Customization
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Support
+
+ + +
+
+ +
+
+ Hire + Odoo + Developer
+
+ +
+
+ +
+
+ Odoo + Integration
+
+ +
+
+ +
+
+ Odoo + Migration
+
+ + +
+
+ +
+
+ Odoo + Consultancy
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Licensing Consultancy
+
+
+ +
+ + + + + +
+
+ +
+

Our + Industries +

+
+ +
+
+
+
+ +
+ Trading +
+

+ Easily procure + and + sell your products

+
+
+ +
+
+ +
+ POS +
+

+ Easy + configuration + and convivial experience

+
+
+ +
+
+ +
+ Education +
+

+ A platform for + educational management

+
+
+ +
+
+ +
+ Manufacturing +
+

+ Plan, track and + schedule your operations

+
+
+ +
+
+ +
+ E-commerce & Website +
+

+ Mobile + friendly, + awe-inspiring product pages

+
+
+ +
+
+ +
+ Service Management +
+

+ Keep track of + services and invoice

+
+
+ +
+
+ +
+ Restaurant +
+

+ Run your bar or + restaurant methodically

+
+
+ +
+
+ +
+ Hotel Management +
+

+ An + all-inclusive + hotel management application

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

Support +

+
+
+
+
+
+
+ +
+
+

Need Help?

+

Got questions or need help? Get in touch.

+ +

+ odoo@cybrosys.com

+
+
+
+
+
+
+
+ +
+
+

WhatsApp

+

Say hi to us on WhatsApp!

+ +

+91 86068 + 27707

+
+
+
+
+
+
+
+ +
+
+
+ \ No newline at end of file diff --git a/purchase_report_generator/static/src/css/purchase_report.css b/purchase_report_generator/static/src/css/purchase_report.css new file mode 100644 index 000000000..e76627c6d --- /dev/null +++ b/purchase_report_generator/static/src/css/purchase_report.css @@ -0,0 +1,184 @@ + +.time_range_pr { + width: 125px; + border: 2px solid #ccc; + border-radius: 5px; + padding: 10px; + +} + +.apply_sale { + margin-right: 5px; + padding: 4px; +} + +#apply_filter { + margin-right: 25px; +} + +#pdf, #xlsx, #apply_filter { + color: white; + background-color: #7c7bad; + border-color: #7c7bad; +} + +a.dropdown-toggle.report-type { + margin: 10px; +} + +.search-Result-Selection { + border: 2px solid #ccc; + max-width: 360px; + min-width: 250px; + margin-right: 10px; + margin-left: 10px; + border-radius: 5px; + min-height: 40px; +} +.search-Result-Selection:hover{ + border:2px solid #eaeaea; + +} +.dropdown-togglereport-type{ + min-height: 40px; + padding-top: 10px; +} + + +.low_case, #report_res { + text-transform: capitalize; + text-align: center; + +} + +#report_res{ + padding-right: 3px; + +} + + +.print-btns { + margin-bottom: 30px; +} + +.table_pr_head { + background-color: #7c7bad; + color: #fff; + padding: 20px; + margin: 20px; + height: 57px; + width: 100%; + border: 1px solid #000; +} + +tr.pr-line { + height: 48px; +} + +tr.table_pr_head th { + font-size: 18px; + text-align: center; +} + +.table_view_pr { + overflow-y: scroll; + position: relative; + height: 500px; + max-width: 100%; + +} +.my_custom_dropdown{ + min-width:200px; + height:150px; + padding: 20px; +} +.my_custom_dropdown input{ + height:25px; + +} +.report_type{ + min-height: 100px; + min-width: 150px; + padding: 30px 10px 10px; +} + + +.sub_container_right, .print-btns { + + display: flex; + justify-content: space-around; + align-items: center; + +} + +@media screen and (max-width: 768px) { + .sub_container_right, .print-btns { + flex-flow: column; + align-items: center; + + } + + .sub_container_left { + order: 2; + margin: 0; + } + + .sub_container_right { + flex-flow: row; + margin-bottom: 10px; + flex-wrap: wrap !important; + width: 100%; + + } + + + +} + + +@media screen and (max-width: 576px) { + + + .apply_filter { + width: 100%; + position: relative; + + } + + #apply_filter { + position: absolute; + margin: 30px 0 0; + top: 50%; + left: 50%; + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + } + + #date_chose { + margin-bottom: 15px; + } + + .search-Result-Selection { + text-align: center; + } + + .sub_container_left { + order: 2; + display: flex; + margin-top: 50px; + width: 100%; + justify-content: center; + align-items: center; + flex-wrap: wrap !important; + + } + + .sub_container_right { + flex-direction: column; + } + + + +} + + diff --git a/purchase_report_generator/static/src/js/purchase_report.js b/purchase_report_generator/static/src/js/purchase_report.js new file mode 100644 index 000000000..7f05d9654 --- /dev/null +++ b/purchase_report_generator/static/src/js/purchase_report.js @@ -0,0 +1,242 @@ +odoo.define('purchase_report_generator.purchase_report', function(require) { + 'use strict'; + var AbstractAction = require("web.AbstractAction"); + var core = require("web.core"); + var rpc = require('web.rpc'); + var _t = core._t; + var QWeb = core.qweb; + var datepicker = require("web.datepicker"); + var time = require('web.time'); + + var framework = require('web.framework'); + var session = require('web.session'); + + var PurchaseReport = AbstractAction.extend({ + template: 'PurchaseReport', + events: { + 'click #apply_filter': 'apply_filter', + 'click #pdf': 'print_pdf', + 'click #xlsx': 'print_xlsx', + 'click .view_purchase_order': 'button_view_order', + 'mousedown div.input-group.date[data-target-input="nearest"]': '_onCalendarIconClick', + + // 'click #date_chose': '_onCalendarIconClick', + + + }, + + + init: function(parent, action) { + this._super(parent, action); + this.report_lines = action.report_lines; + this.wizard_id = action.context.wizard | null; + + }, + start: function() { + var self = this; + self.initial_render = true; + rpc.query({ + model: 'dynamic.purchase.report', + method: 'create', + args: [{ + }] + }).then(function(res) { + self.wizard_id = res; + console.log('res',res); + self.load_data(self.initial_render); + console.log('self',self.initial_render); +// self.apply_filter(); + }) + }, + + _onCalendarIconClick: function(ev) { + console.log('calendar icon clicked',ev); + var $calendarInputGroup = $(ev.currentTarget); + console.log('calendar icon clicked2',$calendarInputGroup); + var calendarOptions = { + + minDate: moment({ + y: 1000 + }), + maxDate: moment().add(200, 'y'), + calendarWeeks: true, + defaultDate: moment().format(), + sideBySide: true, + buttons: { + showClear: true, + showClose: true, + showToday: true, + }, + + icons: { + date: 'fa fa-calendar', + + }, + locale: moment.locale(), + format: time.getLangDateFormat(), + widgetParent: 'body', + allowInputToggle: true, + }; + console.log(calendarOptions,'sdsdsdsadn') + + $calendarInputGroup.datetimepicker(calendarOptions); + }, + + + load_data: function(initial_render = true) { + var self = this; + self._rpc({ + model: 'dynamic.purchase.report', + method: 'purchase_report', + args: [ + [this.wizard_id] + ], + }).then(function(datas) { + if (initial_render) { + self.$('.filter_view_pr').html(QWeb.render('PurchaseFilterView', { + filter_data: datas['filters'], + + })); + self.$el.find('.report_type').select2({ + placeholder: ' Report Type...', + }); + + } + + if (datas['orders']) + self.$('.table_view_pr').html(QWeb.render('PurchaseOrderTable', { + filter: datas['filters'], + order: datas['orders'], + report_lines: datas['report_lines'], + main_lines: datas['report_main_line'] + + })); + }) + }, + + print_pdf: function(e) { + e.preventDefault(); + var self = this; + var action_title = self._title; + self._rpc({ + model: 'dynamic.purchase.report', + method: 'purchase_report', + args: [ + [self.wizard_id] + ], + }).then(function(data) { + var action = { + 'type': 'ir.actions.report', + 'report_type': 'qweb-pdf', + 'report_name': 'purchase_report_generator.purchase_order_report', + 'report_file': 'purchase_report_generator.purchase_order_report', + 'data': { + 'report_data': data + }, + 'context': { + 'active_model': 'purchase.report', + 'landscape': 1, + 'purchase_order_report': true + + }, + 'display_name': 'Purchase Order', + }; + return self.do_action(action); + }); + + }, + print_xlsx: function() { + var self = this; + self._rpc({ + model: 'dynamic.purchase.report', + method: 'purchase_report', + args: [ + [self.wizard_id] + ], + }).then(function(data) { + + var action = { + 'data': { + 'model': 'dynamic.purchase.report', + 'options': JSON.stringify(data['orders']), + 'output_format': 'xlsx', + 'report_data': JSON.stringify(data['report_lines']), + 'report_name': 'Purchase Report', + 'dfr_data': JSON.stringify(data), + }, + }; + self.downloadXlsx(action); + + }); + }, + + downloadXlsx: function (action){ + framework.blockUI(); + session.get_file({ + url: '/purchase_dynamic_xlsx_reports', + data: action.data, + complete: framework.unblockUI, + error: (error) => this.call('crash_manager', 'rpc_error', error), + }); + }, + + button_view_order: function(event) { + event.preventDefault(); + var self = this; + var context = {}; + this.do_action({ + name: _t("Purchase Order"), + type: 'ir.actions.act_window', + res_model: 'purchase.order', + view_type: 'form', + domain: [ + ['id', '=', $(event.target).closest('.view_purchase_order').attr('id')] + ], + views: [ + [false, 'list'], + [false, 'form'] + ], + target: 'current' + }); + }, + // + apply_filter: function() { + var self = this; + self.initial_render = false; + + var filter_data_selected = {}; + + if (this.$el.find('.datetimepicker-input[name="date_from"]').val()) { + filter_data_selected.date_from = moment(this.$el.find('.datetimepicker-input[name="date_from"]').val(), time.getLangDateFormat()).locale('en').format('YYYY-MM-DD'); + } + + if (this.$el.find('.datetimepicker-input[name="date_to"]').val()) { + filter_data_selected.date_to = moment(this.$el.find('.datetimepicker-input[name="date_to"]').val(), time.getLangDateFormat()).locale('en').format('YYYY-MM-DD'); + } + if ($(".report_type").length) { + console.log() + var report_res = document.getElementById("report_res") + filter_data_selected.report_type = $(".report_type")[1].value + report_res.value = $(".report_type")[1].value + report_res.innerHTML = report_res.value.replaceAll('_', ' '); + if ($(".report_type")[1].value == "") { + report_res.innerHTML = "report_by_order"; + + } + } + rpc.query({ + model: 'dynamic.purchase.report', + method: 'write', + args: [ + self.wizard_id, filter_data_selected + ], + }).then(function(res) { + self.initial_render = false; + self.load_data(self.initial_render); + }); + }, + + }); + core.action_registry.add("pu_r", PurchaseReport); + return PurchaseReport; +}); \ No newline at end of file diff --git a/purchase_report_generator/static/src/xml/purchase_report_view.xml b/purchase_report_generator/static/src/xml/purchase_report_view.xml new file mode 100644 index 000000000..f8b03b0e9 --- /dev/null +++ b/purchase_report_generator/static/src/xml/purchase_report_view.xml @@ -0,0 +1,509 @@ + + +
+
+
+

Purchase Report

+
+
+
+
+
+
+
+
+ +
+ + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OrderDate OrderCustomerPurchase RepresentativeTotal QtyAmount TotalNote
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OrderDate OrderCustomerPurchase RepresentativeProduct CodeProduct NamePrice unitQtyPrice Subtotal
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
CategoryProduct CodeProduct NameQtyAmount Total
+ + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + +
CategoryQtyAmount Total
+ + + + + + + + + + + + +
+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + +
Purchase RepresentativeTotal OrderTotal QtyTotal Amount
+ + + + + + + + + + + + + + + + +
+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + +
StateTotal CountQuantityAmount
+ + + + + + + + + + + + + + + + +
+
+
+
+ diff --git a/purchase_report_generator/views/purchase_report.xml b/purchase_report_generator/views/purchase_report.xml new file mode 100644 index 000000000..6735b3502 --- /dev/null +++ b/purchase_report_generator/views/purchase_report.xml @@ -0,0 +1,12 @@ + + + + Purchase Report + pu_r + + + + + \ No newline at end of file diff --git a/sale_report_advanced/README.rst b/sale_report_advanced/README.rst new file mode 100644 index 000000000..26c79986f --- /dev/null +++ b/sale_report_advanced/README.rst @@ -0,0 +1,41 @@ +Advanced Sales Reports +======================= +* Advanced Sales Reports for Odoo 15 + +Installation +============ + - www.odoo.com/documentation/15.0/setup/install.html + - Install our custom addon + +License +------- +General Public License, Version 3 (LGPL v3). +(https://www.odoo.com/documentation/user/13.0/legal/licenses/licenses.html) + +Company +------- +* 'Cybrosys Techno Solutions `__ + +Credits +------- +* Developer: +Athul @ Cybrosys + +Contacts +-------- +* Mail Contact : odoo@cybrosys.com + +Bug Tracker +----------- +Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. + +Maintainer +========== +This module is maintained by Cybrosys Technologies. + +For support and more information, please visit https://www.cybrosys.com + +Further information +=================== +HTML Description: ``__ + diff --git a/sale_report_advanced/__init__.py b/sale_report_advanced/__init__.py new file mode 100644 index 000000000..ee8eaa85d --- /dev/null +++ b/sale_report_advanced/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import wizard +from . import controllers diff --git a/sale_report_advanced/__manifest__.py b/sale_report_advanced/__manifest__.py new file mode 100644 index 000000000..230248216 --- /dev/null +++ b/sale_report_advanced/__manifest__.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +{ + 'name': 'Advanced Sales Reports', + 'version': '16.0.1.0.0', + 'summary': 'Advanced sales reports for Odoo 15', + 'description': """module helps you to print reports like Sales Analysis, Sales By Category, + Sales Indent, Sales Invoice ,Product Profit ,Hourly Sales in PDF and XLSX format.""", + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'category': 'Sales', + 'website': 'https://www.cybrosys.com', + 'depends': ['sale_management', 'base','account'], + 'data': [ + 'security/ir.model.access.csv', + 'wizard/sale_report.xml', + 'wizard/sale_invoice.xml', + 'wizard/sale_analysis.xml', + 'wizard/weekly_wise.xml', + 'wizard/sale_category.xml', + 'wizard/sale_indent.xml', + 'views/report_view.xml', + 'report/sale_reports.xml', + 'report/invoice_analysis_template.xml', + 'report/sales_indent_template.xml', + 'report/sale_profit_template.xml', + 'report/sales_category_template.xml', + 'report/sales_analysis_template.xml', + 'report/sales_weekly_template.xml', + ], + 'images': ['static/description/banner.png'], + 'installable': True, + 'application': True, + 'license': 'LGPL-3', + 'assets': { + 'web.assets_backend': [ + 'sale_report_advanced/static/src/js/action_manager.js', + ], + }, +} diff --git a/sale_report_advanced/controllers/__init__.py b/sale_report_advanced/controllers/__init__.py new file mode 100644 index 000000000..507637791 --- /dev/null +++ b/sale_report_advanced/controllers/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +from . import profit diff --git a/sale_report_advanced/controllers/profit.py b/sale_report_advanced/controllers/profit.py new file mode 100644 index 000000000..84cae02ca --- /dev/null +++ b/sale_report_advanced/controllers/profit.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + + +import json +from odoo import http +from odoo.http import content_disposition, request +# from odoo.addons.web.controllers.main import _serialize_exception +from odoo.tools import html_escape + +class XLSXReportController(http.Controller): + @http.route('/xlsx_reports', type='http', auth='user', methods=['POST'], csrf=False) + def get_report_xlsx(self, model, options, output_format, report_name, **kw): + print('xlsx_controller') + uid = request.session.uid + print(uid,'uid') + options = json.loads(options) + print('options',options) + report_obj = request.env[model].sudo().browse(uid) + print('report_obj',report_obj) + token = 'dummy-because-api-expects-one' + try: + if output_format == 'xlsx': + response = request.make_response( + None, + headers=[('Content-Type', 'application/vnd.ms-excel'), + ('Content-Disposition', content_disposition(report_name + '.xlsx')) + ] + ) + report_obj.get_xlsx_report(options, response) + response.set_cookie('fileToken', token) + return response + except Exception as e: + se = http.serialize_exception(e) + error = { + 'code': 200, + 'message': 'Odoo Server Error', + 'data': se + } + return request.make_response(html_escape(json.dumps(error))) diff --git a/sale_report_advanced/doc/RELEASE_NOTES.md b/sale_report_advanced/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..dba3bca48 --- /dev/null +++ b/sale_report_advanced/doc/RELEASE_NOTES.md @@ -0,0 +1,6 @@ +## Module + +#### 8.4.2022 +#### Version 16.0.1.0.0 +##### ADD +- Initial Commit for sale_report_advanced diff --git a/sale_report_advanced/report/invoice_analysis_template.xml b/sale_report_advanced/report/invoice_analysis_template.xml new file mode 100644 index 000000000..a321d4663 --- /dev/null +++ b/sale_report_advanced/report/invoice_analysis_template.xml @@ -0,0 +1,95 @@ + + + + \ No newline at end of file diff --git a/sale_report_advanced/report/sale_profit_template.xml b/sale_report_advanced/report/sale_profit_template.xml new file mode 100644 index 000000000..9e8ca02a3 --- /dev/null +++ b/sale_report_advanced/report/sale_profit_template.xml @@ -0,0 +1,345 @@ + + + + \ No newline at end of file diff --git a/sale_report_advanced/report/sale_reports.xml b/sale_report_advanced/report/sale_reports.xml new file mode 100644 index 000000000..a2ea7a0b4 --- /dev/null +++ b/sale_report_advanced/report/sale_reports.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/sale_report_advanced/report/sales_analysis_template.xml b/sale_report_advanced/report/sales_analysis_template.xml new file mode 100644 index 000000000..77d442f42 --- /dev/null +++ b/sale_report_advanced/report/sales_analysis_template.xml @@ -0,0 +1,167 @@ + + + + \ No newline at end of file diff --git a/sale_report_advanced/report/sales_category_template.xml b/sale_report_advanced/report/sales_category_template.xml new file mode 100644 index 000000000..f7719e8a5 --- /dev/null +++ b/sale_report_advanced/report/sales_category_template.xml @@ -0,0 +1,111 @@ + + + + \ No newline at end of file diff --git a/sale_report_advanced/report/sales_indent_template.xml b/sale_report_advanced/report/sales_indent_template.xml new file mode 100644 index 000000000..e1d1e5b75 --- /dev/null +++ b/sale_report_advanced/report/sales_indent_template.xml @@ -0,0 +1,59 @@ + + + + \ No newline at end of file diff --git a/sale_report_advanced/report/sales_weekly_template.xml b/sale_report_advanced/report/sales_weekly_template.xml new file mode 100644 index 000000000..7f22bcafd --- /dev/null +++ b/sale_report_advanced/report/sales_weekly_template.xml @@ -0,0 +1,66 @@ + + + + \ No newline at end of file diff --git a/sale_report_advanced/security/ir.model.access.csv b/sale_report_advanced/security/ir.model.access.csv new file mode 100644 index 000000000..28e71bf3e --- /dev/null +++ b/sale_report_advanced/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_sale_report_advance,access.sale.report.advance,model_sale_report_advance,,1,1,1,1 +access_sale_report_invoice,access.sale.report.invoice,model_sale_report_invoice,,1,1,1,1 +access_sale_report_category,access.sale.report.category,model_sale_report_category,,1,1,1,1 +access_sale_report_indent,access.sale.report.indent,model_sale_report_indent,,1,1,1,1 +access_sale_report_analysis,access.sale.report.analysis,model_sale_report_analysis,,1,1,1,1 +access_sale_report_weekly,access.sale.report.weekly,model_sale_report_weekly,,1,1,1,1 diff --git a/sale_report_advanced/static/description/assets/icons/check.png b/sale_report_advanced/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/check.png differ diff --git a/sale_report_advanced/static/description/assets/icons/chevron.png b/sale_report_advanced/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/chevron.png differ diff --git a/sale_report_advanced/static/description/assets/icons/cogs.png b/sale_report_advanced/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/cogs.png differ diff --git a/sale_report_advanced/static/description/assets/icons/consultation.png b/sale_report_advanced/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/consultation.png differ diff --git a/sale_report_advanced/static/description/assets/icons/ecom-black.png b/sale_report_advanced/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/ecom-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/education-black.png b/sale_report_advanced/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/education-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/hotel-black.png b/sale_report_advanced/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/hotel-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/license.png b/sale_report_advanced/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/license.png differ diff --git a/sale_report_advanced/static/description/assets/icons/lifebuoy.png b/sale_report_advanced/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/lifebuoy.png differ diff --git a/sale_report_advanced/static/description/assets/icons/manufacturing-black.png b/sale_report_advanced/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/manufacturing-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/pos-black.png b/sale_report_advanced/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/pos-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/puzzle.png b/sale_report_advanced/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/puzzle.png differ diff --git a/sale_report_advanced/static/description/assets/icons/restaurant-black.png b/sale_report_advanced/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/restaurant-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/service-black.png b/sale_report_advanced/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/service-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/trading-black.png b/sale_report_advanced/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/trading-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/training.png b/sale_report_advanced/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/training.png differ diff --git a/sale_report_advanced/static/description/assets/icons/update.png b/sale_report_advanced/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/update.png differ diff --git a/sale_report_advanced/static/description/assets/icons/user.png b/sale_report_advanced/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/user.png differ diff --git a/sale_report_advanced/static/description/assets/icons/wrench.png b/sale_report_advanced/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/wrench.png differ diff --git a/sale_report_advanced/static/description/assets/misc/categories.png b/sale_report_advanced/static/description/assets/misc/categories.png new file mode 100644 index 000000000..bedf1e0b1 Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/categories.png differ diff --git a/sale_report_advanced/static/description/assets/misc/check-box.png b/sale_report_advanced/static/description/assets/misc/check-box.png new file mode 100644 index 000000000..42caf24b9 Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/check-box.png differ diff --git a/sale_report_advanced/static/description/assets/misc/compass.png b/sale_report_advanced/static/description/assets/misc/compass.png new file mode 100644 index 000000000..d5fed8faa Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/compass.png differ diff --git a/sale_report_advanced/static/description/assets/misc/corporate.png b/sale_report_advanced/static/description/assets/misc/corporate.png new file mode 100644 index 000000000..2eb13edbf Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/corporate.png differ diff --git a/sale_report_advanced/static/description/assets/misc/customer-support.png b/sale_report_advanced/static/description/assets/misc/customer-support.png new file mode 100644 index 000000000..79efc72ed Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/customer-support.png differ diff --git a/sale_report_advanced/static/description/assets/misc/cybrosys-logo.png b/sale_report_advanced/static/description/assets/misc/cybrosys-logo.png new file mode 100644 index 000000000..cc3cc0ccf Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/cybrosys-logo.png differ diff --git a/sale_report_advanced/static/description/assets/misc/features.png b/sale_report_advanced/static/description/assets/misc/features.png new file mode 100644 index 000000000..b41769f77 Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/features.png differ diff --git a/sale_report_advanced/static/description/assets/misc/logo.png b/sale_report_advanced/static/description/assets/misc/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/logo.png differ diff --git a/sale_report_advanced/static/description/assets/misc/pictures.png b/sale_report_advanced/static/description/assets/misc/pictures.png new file mode 100644 index 000000000..56d255fe9 Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/pictures.png differ diff --git a/sale_report_advanced/static/description/assets/misc/pie-chart.png b/sale_report_advanced/static/description/assets/misc/pie-chart.png new file mode 100644 index 000000000..426e05244 Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/pie-chart.png differ diff --git a/sale_report_advanced/static/description/assets/misc/right-arrow.png b/sale_report_advanced/static/description/assets/misc/right-arrow.png new file mode 100644 index 000000000..730984a06 Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/right-arrow.png differ diff --git a/sale_report_advanced/static/description/assets/misc/star.png b/sale_report_advanced/static/description/assets/misc/star.png new file mode 100644 index 000000000..2eb9ab29f Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/star.png differ diff --git a/sale_report_advanced/static/description/assets/misc/support.png b/sale_report_advanced/static/description/assets/misc/support.png new file mode 100644 index 000000000..4f18b8b82 Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/support.png differ diff --git a/sale_report_advanced/static/description/assets/misc/whatsapp.png b/sale_report_advanced/static/description/assets/misc/whatsapp.png new file mode 100644 index 000000000..d513a5356 Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/whatsapp.png differ diff --git a/sale_report_advanced/static/description/assets/modules/1.png b/sale_report_advanced/static/description/assets/modules/1.png new file mode 100644 index 000000000..5238bdeab Binary files /dev/null and b/sale_report_advanced/static/description/assets/modules/1.png differ diff --git a/sale_report_advanced/static/description/assets/modules/2.png b/sale_report_advanced/static/description/assets/modules/2.png new file mode 100644 index 000000000..1ae7cfe3b Binary files /dev/null and b/sale_report_advanced/static/description/assets/modules/2.png differ diff --git a/sale_report_advanced/static/description/assets/modules/3.png b/sale_report_advanced/static/description/assets/modules/3.png new file mode 100644 index 000000000..3c3ff1afb Binary files /dev/null and b/sale_report_advanced/static/description/assets/modules/3.png differ diff --git a/sale_report_advanced/static/description/assets/modules/4.png b/sale_report_advanced/static/description/assets/modules/4.png new file mode 100644 index 000000000..3fae4631e Binary files /dev/null and b/sale_report_advanced/static/description/assets/modules/4.png differ diff --git a/sale_report_advanced/static/description/assets/modules/5.gif b/sale_report_advanced/static/description/assets/modules/5.gif new file mode 100644 index 000000000..2a5f8e659 Binary files /dev/null and b/sale_report_advanced/static/description/assets/modules/5.gif differ diff --git a/sale_report_advanced/static/description/assets/modules/6.png b/sale_report_advanced/static/description/assets/modules/6.png new file mode 100644 index 000000000..7f2815273 Binary files /dev/null and b/sale_report_advanced/static/description/assets/modules/6.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/1.png b/sale_report_advanced/static/description/assets/screenshots/1.png new file mode 100644 index 000000000..bea9857a9 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/1.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/10.png b/sale_report_advanced/static/description/assets/screenshots/10.png new file mode 100644 index 000000000..225e8827d Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/10.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/11.png b/sale_report_advanced/static/description/assets/screenshots/11.png new file mode 100644 index 000000000..a3ddce531 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/11.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/12.png b/sale_report_advanced/static/description/assets/screenshots/12.png new file mode 100644 index 000000000..b9156f998 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/12.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/13.png b/sale_report_advanced/static/description/assets/screenshots/13.png new file mode 100644 index 000000000..b8a85c439 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/13.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/14.png b/sale_report_advanced/static/description/assets/screenshots/14.png new file mode 100644 index 000000000..f200fbf7b Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/14.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/15.png b/sale_report_advanced/static/description/assets/screenshots/15.png new file mode 100644 index 000000000..89014b016 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/15.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/16.png b/sale_report_advanced/static/description/assets/screenshots/16.png new file mode 100644 index 000000000..8be9d2763 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/16.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/17.png b/sale_report_advanced/static/description/assets/screenshots/17.png new file mode 100644 index 000000000..5109c3801 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/17.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/18.png b/sale_report_advanced/static/description/assets/screenshots/18.png new file mode 100644 index 000000000..73588552f Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/18.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/2.png b/sale_report_advanced/static/description/assets/screenshots/2.png new file mode 100644 index 000000000..e7fd60331 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/2.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/3.png b/sale_report_advanced/static/description/assets/screenshots/3.png new file mode 100644 index 000000000..7d86b8989 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/3.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/4.png b/sale_report_advanced/static/description/assets/screenshots/4.png new file mode 100644 index 000000000..c8af85a74 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/4.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/5.png b/sale_report_advanced/static/description/assets/screenshots/5.png new file mode 100644 index 000000000..2fed023d9 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/5.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/6.png b/sale_report_advanced/static/description/assets/screenshots/6.png new file mode 100644 index 000000000..8b63ef3ac Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/6.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/7.png b/sale_report_advanced/static/description/assets/screenshots/7.png new file mode 100644 index 000000000..dbf318ce9 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/7.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/8.png b/sale_report_advanced/static/description/assets/screenshots/8.png new file mode 100644 index 000000000..e5b564ed5 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/8.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/9.png b/sale_report_advanced/static/description/assets/screenshots/9.png new file mode 100644 index 000000000..5e4d178cc Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/9.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/hero.gif b/sale_report_advanced/static/description/assets/screenshots/hero.gif new file mode 100644 index 000000000..b1bdcbaa1 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/hero.gif differ diff --git a/sale_report_advanced/static/description/banner.png b/sale_report_advanced/static/description/banner.png new file mode 100644 index 000000000..8658f1826 Binary files /dev/null and b/sale_report_advanced/static/description/banner.png differ diff --git a/sale_report_advanced/static/description/icon.png b/sale_report_advanced/static/description/icon.png new file mode 100644 index 000000000..42e5edd54 Binary files /dev/null and b/sale_report_advanced/static/description/icon.png differ diff --git a/sale_report_advanced/static/description/index.html b/sale_report_advanced/static/description/index.html new file mode 100644 index 000000000..27a2d8c52 --- /dev/null +++ b/sale_report_advanced/static/description/index.html @@ -0,0 +1,649 @@ +
+ +
+ +
+
+ Community +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+ +
+
+
+ +

+ Advanced Sales Reports

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

Explore This + Module

+
+ + + + +
+
+ +
+

Overview +

+
+
+
+ The module helps you to print reports like Sales Analysis, Sales By Category, Sales Indent, Sales + Invoice ,Product Profit ,Hourly Sales in PDF and XLSX format. +
+
+ + + +
+
+ +
+

Features +

+
+
+
+
+ + Sales Analysis Report. +
+
+ + Sales Category Report. +
+
+ + Sales Indent Report. +
+
+ + Sales Invoice Analysis Report +
+
+ + Product Profit Report +
+
+ + Hourly Sales Report +
+
+
+ + +
+
+ + + +
+
+ +
+

Screenshots +

+
+
+
+ +
+

Product Profit Report +

+ + +
+
+ + +

PDF report

+ +
+
+ + +

XLSX report

+ +
+ +
+

Sales Invoice Analysis Report +

+ + +
+
+ + +

PDF report

+ +
+
+ + +

XLSX report

+ +
+ +
+

Sales Category Report +

+ + +
+
+ + +

PDF report

+ +
+
+ + +

XLSX report

+ +
+ +
+

Sales Indent Report +

+ + +
+
+ + +

PDF report

+ +
+
+ + +

XLSX report

+ +
+ +
+

Sales Analysis Report +

+ + +
+
+ + +

PDF report

+ +
+
+ + +

XLSX report

+ +
+ + +
+

Hourly Sales Report +

+ + +
+
+ + +

PDF report

+ +
+
+ + +

XLSX report

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

Related + Products +

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

Our Services +

+
+ +
+
+
+
+ +
+
+ Odoo + Customization
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Support
+
+ + +
+
+ +
+
+ Hire + Odoo + Developer
+
+ +
+
+ +
+
+ Odoo + Integration
+
+ +
+
+ +
+
+ Odoo + Migration
+
+ + +
+
+ +
+
+ Odoo + Consultancy
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Licensing Consultancy
+
+
+ +
+ + + + + +
+
+ +
+

Our + Industries +

+
+ +
+
+
+
+ +
+ Trading +
+

+ Easily procure + and + sell your products

+
+
+ +
+
+ +
+ POS +
+

+ Easy + configuration + and convivial experience

+
+
+ +
+
+ +
+ Education +
+

+ A platform for + educational management

+
+
+ +
+
+ +
+ Manufacturing +
+

+ Plan, track and + schedule your operations

+
+
+ +
+
+ +
+ E-commerce & Website +
+

+ Mobile + friendly, + awe-inspiring product pages

+
+
+ +
+
+ +
+ Service Management +
+

+ Keep track of + services and invoice

+
+
+ +
+
+ +
+ Restaurant +
+

+ Run your bar or + restaurant methodically

+
+
+ +
+
+ +
+ Hotel Management +
+

+ An + all-inclusive + hotel management application

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

Support +

+
+
+
+
+
+
+ +
+
+

Need Help?

+

Got questions or need help? Get in touch.

+ +

+ odoo@cybrosys.com

+
+
+
+
+
+
+
+ +
+
+

WhatsApp

+

Say hi to us on WhatsApp!

+ +

+91 86068 + 27707

+
+
+
+
+
+
+
+ +
+
+
+ \ No newline at end of file diff --git a/sale_report_advanced/static/src/js/action_manager.js b/sale_report_advanced/static/src/js/action_manager.js new file mode 100644 index 000000000..493033798 --- /dev/null +++ b/sale_report_advanced/static/src/js/action_manager.js @@ -0,0 +1,22 @@ +/** @odoo-module */ + +import { registry } from "@web/core/registry"; +import { download } from "@web/core/network/download"; +import framework from 'web.framework'; +import session from 'web.session'; + +registry.category("ir.actions.report handlers").add("xlsx", async (action) => { + if (action.report_type === 'xlsx') { + framework.blockUI(); + var def = $.Deferred(); + console.log('sdcscvdcvdcdcdcbdcddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'); + session.get_file({ + url: '/xlsx_reports', + data: action.data, + success: def.resolve.bind(def), +// error: (error) => this.call('crash_manager', 'rpc_error', error), + complete: framework.unblockUI, + }); + return def; + } +}); diff --git a/sale_report_advanced/views/report_view.xml b/sale_report_advanced/views/report_view.xml new file mode 100644 index 000000000..fe468b7e1 --- /dev/null +++ b/sale_report_advanced/views/report_view.xml @@ -0,0 +1,32 @@ + + + + + + + + \ No newline at end of file diff --git a/sale_report_advanced/wizard/__init__.py b/sale_report_advanced/wizard/__init__.py new file mode 100644 index 000000000..554344ef2 --- /dev/null +++ b/sale_report_advanced/wizard/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import sale_report +from . import sale_invoice +from . import sale_category +from . import sale_indent +from . import sale_analysis +from . import weekly_wise \ No newline at end of file diff --git a/sale_report_advanced/wizard/sale_analysis.py b/sale_report_advanced/wizard/sale_analysis.py new file mode 100644 index 000000000..f6f7b949e --- /dev/null +++ b/sale_report_advanced/wizard/sale_analysis.py @@ -0,0 +1,367 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +import json +import io +from xlsxwriter import workbook + +from odoo.tools import date_utils +from odoo import fields, models +from odoo.exceptions import UserError, ValidationError + +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class SaleReportAdvance(models.TransientModel): + _name = "sale.report.analysis" + + customer_ids = fields.Many2many('res.partner', string="Customers", required=True) + product_ids = fields.Many2many('product.product', string='Products') + from_date = fields.Date(string="Start Date") + to_date = fields.Date(string="End Date") + status = fields.Selection( + [('all', 'All'), ('draft', 'Draft'), ('sent', 'Quotation Sent'), ('sale', 'Sale Order'), ('done', 'Locked')], + string='Status', default='all', reqired=True) + print_type = fields.Selection( + [('sale', 'Sale Order'), ('product', 'Products')], + string='Print By', default='sale', reqired=True) + today_date = fields.Date(default=fields.Date.today()) + + def get_analysis_report(self): + datas = self._get_data() + return self.env.ref('sale_report_advanced.action_sales_analysis').report_action([], data=datas) + + def _get_data(self): + + result = [] + if self.print_type == 'sale': + if not self.status == 'all': + sale_order = self.env['sale.order'].sudo().search([('state', '=', self.status),('state','!=','cancel')]) + filtered = self._get_filtered(sale_order) + + else: + sale_order = self.env['sale.order'].search([('state','!=','cancel')]) + filtered = self._get_filtered(sale_order) + + for rec in filtered: + paid = self._get_total_paid_amount(rec.invoice_ids) + res = { + 'so': rec.name, + 'date': rec.date_order, + 'sales_person': rec.user_id.name, + 's_amt': rec.amount_total, + 'p_amt': paid, + 'balance': rec.amount_total - paid, + 'partner_id': rec.partner_id, + } + result.append(res) + else: + if not self.status == 'all': + sale_order_line = self.env['sale.order.line'].search([('order_id.state', '=', self.status),('order_id.state','!=','cancel')]) + filtered = self._get_filtered_order_line(sale_order_line) + + else: + sale_order_line = self.env['sale.order.line'].search([('order_id.state','!=','cancel')]) + filtered = self._get_filtered_order_line(sale_order_line) + + for rec in filtered: + res = { + 'so': rec.order_id.name, + 'date': rec.order_id.date_order, + 'product_id': rec.product_id.name, + 'price': rec.product_id.list_price, + 'quantity': rec.product_uom_qty, + 'discount': rec.discount, + 'tax': rec.product_id.taxes_id.amount, + 'total': rec.price_subtotal, + 'partner_id': rec.order_id.partner_id, + } + result.append(res) + datas = { + 'ids': self, + 'model': 'sale.report.analysis', + 'form': result, + 'partner_id': self._get_customers(), + 'start_date': self.from_date, + 'end_date': self.to_date, + 'type': self.print_type + + } + return datas + + def _get_total_paid_amount(self, invoices): + total = 0 + for inv in invoices: + if inv.payment_state == 'paid': + total += inv.amount_total + return total + + def _get_filtered_order_line(self, sale_order_line): + if self.from_date and self.to_date: + filtered = list(filter(lambda + x: x.order_id.date_order.date() >= self.from_date and x.order_id.date_order.date() <= self.to_date and x.order_id.partner_id in self.customer_ids and x.product_id in self.product_ids, + sale_order_line)) + elif not self.from_date and self.to_date: + filtered = list(filter(lambda + x: x.order_id.date_order.date() <= self.to_date and x.order_id.partner_id in self.customer_ids and x.product_id in self.product_ids, + sale_order_line)) + elif self.from_date and not self.to_date: + filtered = list(filter(lambda + x: x.order_id.date_order.date() >= self.from_date and x.order_id.partner_id in self.customer_ids and x.product_id in self.product_ids, + sale_order_line)) + else: + filtered = list(filter(lambda + x: x.order_id.partner_id in self.customer_ids and x.product_id in self.product_ids, + sale_order_line)) + return filtered + + def _get_filtered(self, sale_order): + + if self.from_date and self.to_date: + filtered = list(filter(lambda + x: x.date_order.date() >= self.from_date and x.date_order.date() <= self.to_date and x.partner_id in self.customer_ids, + sale_order)) + elif not self.from_date and self.to_date: + filtered = list(filter(lambda + x: x.date_order.date() <= self.to_date and x.partner_id in self.customer_ids, + sale_order)) + elif self.from_date and not self.to_date: + filtered = list(filter(lambda + x: x.date_order.date() >= self.from_date and x.partner_id in self.customer_ids, + sale_order)) + else: + filtered = list(filter(lambda + x: x.partner_id in self.customer_ids, + sale_order)) + return filtered + + def _get_customers(self): + customers = [] + for rec in self.customer_ids: + a = { + 'id': rec, + 'name': rec.name + } + customers.append(a) + return customers + + def get_excel_analysis_report(self): + datas = self._get_data() + return { + 'type': 'ir.actions.report', + 'report_type': 'xlsx', + 'data': {'model': 'sale.report.analysis', + 'output_format': 'xlsx', + 'options': json.dumps(datas, default=date_utils.json_default), + 'report_name': 'Excel Report Name', + }, + } + + def get_xlsx_report(self, data, response): + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + record = [] + cell_format = workbook.add_format({'font_size': '12px', }) + head = workbook.add_format({'align': 'center', 'bold': True, 'font_size': '20px'}) + txt = workbook.add_format({'font_size': '10px', 'align': 'center'}) + + if data['type'] == 'sale': + sheet.merge_range('G2:L3', 'Sales Analysis Report', head) + if data['start_date'] and data['end_date']: + sheet.write('G6', 'From:', cell_format) + sheet.merge_range('H6:I6', data['start_date'], txt) + sheet.write('J6', 'To:', cell_format) + sheet.merge_range('K6:L6', data['end_date'], txt) + h_col = 9 + else: + sheet.merge_range('G2:N3', 'Sales Analysis Report', head) + if data['start_date'] and data['end_date']: + sheet.write('G6', 'From:', cell_format) + sheet.merge_range('H6:I6', data['start_date'], txt) + sheet.write('K6', 'To:', cell_format) + sheet.merge_range('L6:N6', data['end_date'], txt) + h_col = 10 + + format1 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bg_color': '#f5f9ff', 'border': 1}) + format2 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#6BA6FE', 'border': 1}) + format3 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, 'bg_color': '#95ff63', 'border': 1}) + format4 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True}) + if data['partner_id']: + record = data['partner_id'] + h_row = 7 + count = 0 + row = 5 + row_number = 6 + t_row = 6 + for rec in record: + if data['type'] == 'sale': + sheet.merge_range(h_row, h_col - 3, h_row, h_col + 2, rec['name'], format1) + row = row + count + 3 + col = 6 + sheet.write(row, col, 'Order', format2) + col += 1 + sheet.write(row, col, 'Date', format2) + sheet.set_column('H:H', 17) + col += 1 + sheet.write(row, col, 'Sales Person', format2) + sheet.set_column('I:I', 15) + col += 1 + sheet.write(row, col, 'Sales Amount', format2) + sheet.set_column('J:J', 13) + col += 1 + sheet.write(row, col, 'Amount Paid', format2) + sheet.set_column('K:K', 12) + col += 1 + sheet.write(row, col, 'Balance', format2) + sheet.set_column('L:L', 10) + # col = 6 + count = 0 + row_number = row_number + count + 3 + t_samt = 0 + t_pamt = 0 + t_bal = 0 + t_col = 8 + for val in data['form']: + if val['partner_id'] == rec['id']: + count += 1 + column_number = 6 + sheet.write(row_number, column_number, val['so'], format1) + column_number += 1 + sheet.write(row_number, column_number, val['date'], format1) + sheet.set_column('H:H', 17) + column_number += 1 + sheet.write(row_number, column_number, val['sales_person'], format1) + sheet.set_column('I:I', 15) + column_number += 1 + sheet.write(row_number, column_number, val['s_amt'], format1) + sheet.set_column('J:J', 13) + t_samt += val['s_amt'] + column_number += 1 + sheet.write(row_number, column_number, val['p_amt'], format1) + sheet.set_column('K:K', 12) + t_pamt += val['p_amt'] + column_number += 1 + sheet.write(row_number, column_number, val['balance'], format1) + sheet.set_column('L:L', 10) + t_bal += val['balance'] + column_number += 1 + row_number += 1 + t_row = t_row + count + 3 + sheet.write(t_row, t_col, 'Total', format4) + t_col += 1 + sheet.write(t_row, t_col, t_samt, format4) + t_col += 1 + sheet.write(t_row, t_col, t_pamt, format4) + t_col += 1 + sheet.write(t_row, t_col, t_bal, format4) + h_row = h_row + count + 3 + + if data['type'] == 'product': + sheet.merge_range(h_row, h_col - 4, h_row, h_col + 3, rec['name'], format1) + row = row + count + 3 + col = 6 + sheet.write(row, col, 'Order', format2) + col += 1 + sheet.write(row, col, 'Date', format2) + sheet.set_column('H:H', 17) + col += 1 + sheet.write(row, col, 'Product', format2) + sheet.set_column('I:I', 17) + col += 1 + sheet.write(row, col, 'Quantity', format2) + sheet.set_column('J:J', 7) + col += 1 + sheet.write(row, col, 'Price', format2) + sheet.set_column('K:K', 12) + col += 1 + sheet.write(row, col, 'Discount(%)', format2) + sheet.set_column('L:L', 12) + col += 1 + sheet.write(row, col, 'Tax(%)', format2) + sheet.set_column('M:M', 10) + col += 1 + sheet.write(row, col, 'Subtotal', format2) + sheet.set_column('N:N', 10) + col += 1 + count = 0 + row_number = row_number + count + 3 + t_qty = 0 + t_price = 0 + t_tax = 0 + t_total = 0 + t_col = 8 + for val in data['form']: + if val['partner_id'] == rec['id']: + count += 1 + column_number = 6 + sheet.write(row_number, column_number, val['so'], format1) + column_number += 1 + sheet.write(row_number, column_number, val['date'], format1) + sheet.set_column('H:H', 17) + column_number += 1 + sheet.write(row_number, column_number, val['product_id'], format1) + sheet.set_column('I:I', 17) + column_number += 1 + sheet.write(row_number, column_number, val['quantity'], format1) + sheet.set_column('J:J', 7) + t_qty += val['quantity'] + column_number += 1 + sheet.write(row_number, column_number, val['price'], format1) + sheet.set_column('K:K', 12) + t_price += val['price'] + column_number += 1 + sheet.write(row_number, column_number, val['discount'], format1) + sheet.set_column('L:L', 12) + column_number += 1 + sheet.write(row_number, column_number, val['tax'], format1) + sheet.set_column('M:M', 10) + t_tax += val['tax'] + column_number += 1 + sheet.write(row_number, column_number, val['total'], format1) + sheet.set_column('N:N', 10) + t_total += val['total'] + column_number += 1 + row_number += 1 + t_row = t_row + count + 3 + sheet.write(t_row, t_col, 'Total', format4) + t_col += 1 + sheet.write(t_row, t_col, t_qty, format4) + t_col += 1 + sheet.write(t_row, t_col, t_price, format4) + t_col += 2 + sheet.write(t_row, t_col, t_tax, format4) + t_col += 1 + sheet.write(t_row, t_col, t_total, format4) + h_row = h_row + count + 3 + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/sale_report_advanced/wizard/sale_analysis.xml b/sale_report_advanced/wizard/sale_analysis.xml new file mode 100644 index 000000000..23678697b --- /dev/null +++ b/sale_report_advanced/wizard/sale_analysis.xml @@ -0,0 +1,39 @@ + + + + Sales Analysis report + sale.report.analysis + +
+ + + + + + + + + + + + + + +
+
+
+
+
+ + Sales Analysis Report + ir.actions.act_window + sale.report.analysis + form + + new + +
\ No newline at end of file diff --git a/sale_report_advanced/wizard/sale_category.py b/sale_report_advanced/wizard/sale_category.py new file mode 100644 index 000000000..2c689252c --- /dev/null +++ b/sale_report_advanced/wizard/sale_category.py @@ -0,0 +1,260 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +import json +import io +from xlsxwriter import workbook + +from odoo.tools import date_utils +from odoo import fields, models +from odoo.exceptions import UserError, ValidationError + +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class SaleReportAdvance(models.TransientModel): + _name = "sale.report.category" + + category_ids = fields.Many2many('product.category', string="Categories",required=True) + from_date = fields.Date(string="Start Date") + to_date = fields.Date(string="End Date") + company_ids = fields.Many2many('res.company', string='Companies') + today_date = fields.Date(default=fields.Date.today()) + + def get_category_report(self): + datas = self._get_data() + return self.env.ref('sale_report_advanced.action_sale_category').report_action([], data=datas) + + def _get_data(self): + sale_order_line = self.env['sale.order.line'].search([('order_id.state','!=','cancel')]) + if self.from_date and self.to_date and self.company_ids: + order_lines = list(filter(lambda + x: self.from_date >= x.order_id.date_order.date() <= self.to_date and x.order_id.company_id in self.company_ids, + sale_order_line)) + category = self._get_category() + res = self._get_category_wise(order_lines, category) + + elif not self.from_date and self.to_date and self.company_ids: + order_lines = list(filter(lambda + x: x.order_id.date_order.date() <= self.to_date and x.order_id.company_id in self.company_ids, + sale_order_line)) + category = self._get_category() + res = self._get_category_wise(order_lines, category) + elif self.from_date and not self.to_date and self.company_ids: + order_lines = list(filter(lambda + x: self.from_date >= x.order_id.date_order.date() and x.order_id.company_id in self.company_ids, + sale_order_line)) + category = self._get_category() + res = self._get_category_wise(order_lines, category) + elif self.from_date and self.to_date and not self.company_ids: + order_lines = list(filter(lambda + x: self.from_date >= x.order_id.date_order.date() <= self.to_date, + sale_order_line)) + category = self._get_category() + res = self._get_category_wise(order_lines, category) + + elif not self.from_date and not self.to_date and self.company_ids: + order_lines = list(filter(lambda + x: x.order_id.company_id in self.company_ids, + sale_order_line)) + category = self._get_category() + res = self._get_category_wise(order_lines, category) + elif not self.from_date and self.to_date and not self.company_ids: + order_lines = list(filter(lambda + x: x.order_id.date_order.date() <= self.to_date, + sale_order_line)) + category = self._get_category() + res = self._get_category_wise(order_lines, category) + elif self.from_date and not self.to_date and not self.company_ids: + order_lines = list(filter(lambda + x: self.from_date >= x.order_id.date_order.date(), + sale_order_line)) + category = self._get_category() + res = self._get_category_wise(order_lines, category) + else: + category = self._get_category() + res = self._get_category_wise(sale_order_line, category) + + datas = { + 'ids': self, + 'model': 'sale.report.category', + 'form': res, + 'categ_id': category, + 'start_date': self.from_date, + 'end_date': self.to_date, + + } + return datas + + def _get_category_wise(self, order_lines, category): + result = [] + for cat in category: + for lines in order_lines: + if cat['id'] == lines.product_id.categ_id: + total = lines.product_id.taxes_id.amount +lines.price_subtotal + res = { + 'so': lines.order_id.name, + 'date': lines.order_id.date_order, + 'product_id': lines.product_id.name, + 'quantity': lines.product_uom_qty, + 'tax': lines.product_id.taxes_id.amount, + 'uom':lines.product_id.uom_id.name, + 'price': lines.product_id.list_price, + 'subtotal': lines.price_subtotal, + 'total': total, + 'category_id': lines.product_id.categ_id, + } + result.append(res) + return result + + def _get_category(self): + category = [] + for rec in self.category_ids: + a = { + 'id': rec, + 'name': rec.complete_name + } + category.append(a) + return category + + def get_excel_category_report(self): + datas = self._get_data() + return { + 'type': 'ir.actions.report', + 'report_type': 'xlsx', + 'data': {'model': 'sale.report.category', + 'output_format': 'xlsx', + 'options': json.dumps(datas, default=date_utils.json_default), + 'report_name': 'Excel Report Name', + }, + } + def get_xlsx_report(self, data, response): + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + record = [] + cell_format = workbook.add_format({'font_size': '12px', }) + head = workbook.add_format({'align': 'center', 'bold': True, 'font_size': '20px'}) + txt = workbook.add_format({'font_size': '10px', 'align': 'center'}) + sheet.merge_range('G2:O3', 'Sales Category Report', head) + if data['start_date'] and data['end_date']: + sheet.write('G6', 'From:', cell_format) + sheet.merge_range('H6:I6', data['start_date'], txt) + sheet.write('M6', 'To:', cell_format) + sheet.merge_range('N6:O6', data['end_date'], txt) + format1 = workbook.add_format( + {'font_size': 10, 'align': 'center','bg_color':'#bbd5fc','border': 1}) + format2 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#6BA6FE', 'border': 1}) + format3 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True,'border': 1}) + format4 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True,'bg_color':'#c0dbfa'}) + h_row = 7 + h_col = 10 + count = 0 + row = 5 + col = 6 + row_number = 6 + t_row = 6 + if data['categ_id']: + record = data['categ_id'] + for rec in record: + sheet.merge_range(h_row,h_col-4,h_row,h_col+4,rec['name'], format4) + row = row + count + 3 + sheet.write(row, col, 'Order', format2) + col += 1 + sheet.write(row, col, 'Date', format2) + sheet.set_column('H:H',15) + col += 1 + sheet.write(row, col, 'Product', format2) + sheet.set_column('I:I',20) + col += 1 + sheet.write(row, col, 'UOM', format2) + col += 1 + sheet.write(row, col, 'Quantity', format2) + col += 1 + sheet.write(row, col, 'Price', format2) + col += 1 + sheet.write(row, col, 'Tax(%)', format2) + col += 1 + sheet.write(row, col, 'Subtotal', format2) + col += 1 + sheet.write(row, col, 'Total', format2) + col += 1 + col = 6 + count = 0 + row_number = row_number + count + 3 + t_qty = 0 + t_price = 0 + t_subtotal = 0 + t_total = 0 + t_col = 9 + for val in data['form']: + if val['category_id'] == rec['id']: + count += 1 + column_number = 6 + sheet.write(row_number, column_number, val['so'], format1) + column_number += 1 + sheet.write(row_number, column_number, val['date'], format1) + sheet.set_column('H:H', 15) + column_number += 1 + sheet.write(row_number, column_number, val['product_id'], format1) + sheet.set_column('I:I', 20) + column_number += 1 + sheet.write(row_number, column_number, val['uom'], format1) + column_number += 1 + sheet.write(row_number, column_number, val['quantity'], format1) + t_qty += val['quantity'] + column_number += 1 + sheet.write(row_number, column_number, val['price'], format1) + t_price += val['price'] + column_number += 1 + sheet.write(row_number, column_number, val['tax'], format1) + column_number += 1 + sheet.write(row_number, column_number, val['subtotal'], format1) + t_subtotal += val['subtotal'] + column_number += 1 + sheet.write(row_number, column_number, val['total'], format1) + t_total += val['total'] + column_number += 1 + row_number += 1 + t_row = t_row + count + 3 + sheet.write(t_row, t_col, 'Total', format3) + t_col += 1 + sheet.write(t_row, t_col, t_qty, format3) + t_col += 1 + sheet.write(t_row, t_col, t_price, format3) + t_col += 2 + sheet.write(t_row, t_col, t_subtotal, format3) + t_col += 1 + sheet.write(t_row, t_col, t_total, format3) + t_col += 1 + h_row = h_row + count + 3 + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() \ No newline at end of file diff --git a/sale_report_advanced/wizard/sale_category.xml b/sale_report_advanced/wizard/sale_category.xml new file mode 100644 index 000000000..2efb06e02 --- /dev/null +++ b/sale_report_advanced/wizard/sale_category.xml @@ -0,0 +1,31 @@ + + + + Sales Report Category + sale.report.category + +
+ + + + + + +
+
+
+
+
+ + Sales Category Report + ir.actions.act_window + sale.report.category + form + + new + +
\ No newline at end of file diff --git a/sale_report_advanced/wizard/sale_indent.py b/sale_report_advanced/wizard/sale_indent.py new file mode 100644 index 000000000..a9c849499 --- /dev/null +++ b/sale_report_advanced/wizard/sale_indent.py @@ -0,0 +1,230 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +import json +import io +from xlsxwriter import workbook + +from odoo.tools import date_utils +from odoo import fields, models +from odoo.exceptions import UserError, ValidationError + +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class SaleReportAdvance(models.TransientModel): + _name = "sale.report.indent" + + customer_ids = fields.Many2many('res.partner', string="Customers", required=True) + category_ids = fields.Many2many('product.category', string="Categories", required=True) + from_date = fields.Date(string="Start Date") + to_date = fields.Date(string="End Date") + status = fields.Selection( + [('all', 'All'), ('draft', 'Draft'), ('sent', 'Quotation Sent'), ('sale', 'Sale Order'), ('done', 'Locked')], + string='Status', default='all', reqired=True) + company_ids = fields.Many2many('res.company', string='Companies') + today_date = fields.Date(default=fields.Date.today()) + + def get_category_report(self): + datas = self._get_data() + return self.env.ref('sale_report_advanced.action_sale_indent').report_action([], data=datas) + + def _get_data(self): + + sale_order_line = self.env['sale.order.line'].search([('order_id.state','!=','cancel')]) + if self.from_date and self.to_date and self.company_ids: + order_lines = list(filter(lambda + x: x.order_id.date_order.date() >= self.from_date and x.order_id.date_order.date() <= self.to_date and x.order_id.company_id in self.company_ids, + sale_order_line)) + res = self._get_orders(order_lines) + + elif not self.from_date and self.to_date and self.company_ids: + order_lines = list(filter(lambda + x: x.order_id.date_order.date() <= self.to_date and x.order_id.company_id in self.company_ids, + sale_order_line)) + res = self._get_orders(order_lines) + elif self.from_date and not self.to_date and self.company_ids: + order_lines = list(filter(lambda + x: x.order_id.date_order.date() >= self.from_date and x.order_id.company_id in self.company_ids, + sale_order_line)) + res = self._get_orders(order_lines) + elif self.from_date and self.to_date and not self.company_ids: + order_lines = list(filter(lambda + x: x.order_id.date_order.date() >= self.from_date and x.order_id.date_order.date()<= self.to_date, + sale_order_line)) + res = self._get_orders(order_lines) + + elif not self.from_date and not self.to_date and self.company_ids: + order_lines = list(filter(lambda + x: x.order_id.company_id in self.company_ids, + sale_order_line)) + res = self._get_orders(order_lines) + elif not self.from_date and self.to_date and not self.company_ids: + order_lines = list(filter(lambda + x: x.order_id.date_order.date() <= self.to_date, + sale_order_line)) + res = self._get_orders(order_lines) + elif self.from_date and not self.to_date and not self.company_ids: + order_lines = list(filter(lambda + x: x.order_id.date_order.date() >= self.from_date, + sale_order_line)) + res = self._get_orders(order_lines) + else: + res = self._get_orders(sale_order_line) + + datas = { + 'ids': self, + 'model': 'sale.report.indent', + 'form': res, + 'categ_id': self._get_category(), + 'partner_id': self._get_customers(), + 'start_date': self.from_date, + 'end_date': self.to_date, + + } + return datas + + def _get_orders(self, sale_order_line): + if self.status == 'all': + + filtered_order = list(filter(lambda + x: x.order_id.partner_id in self.customer_ids and x.product_id.categ_id in self.category_ids, + sale_order_line)) + else: + filtered_order = list(filter(lambda + x: x.order_id.partner_id in self.customer_ids and x.order_id.state in self.status and x.product_id.categ_id in self.category_ids, + sale_order_line)) + return self._get_customer_wise(filtered_order) + + def _get_customer_wise(self, order): + result = [] + for rec in order: + res = { + 'product_id': rec.product_id.name, + 'quantity': rec.product_uom_qty, + 'partner_id': rec.order_id.partner_id, + 'category_id': rec.product_id.categ_id, + } + result.append(res) + return result + + def _get_category(self): + category = [] + for rec in self.category_ids: + a = { + 'id': rec, + 'name': rec.complete_name + } + category.append(a) + return category + + def _get_customers(self): + customers = [] + for rec in self.customer_ids: + a = { + 'id': rec, + 'name': rec.name + } + customers.append(a) + return customers + + def get_excel_category_report(self): + datas = self._get_data() + return { + 'type': 'ir.actions.report', + 'report_type': 'xlsx', + 'data': {'model': 'sale.report.indent', + 'output_format': 'xlsx', + 'options': json.dumps(datas, default=date_utils.json_default), + 'report_name': 'Excel Report Name', + }, + } + + def get_xlsx_report(self, data, response): + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + cell_format = workbook.add_format({'font_size': '12px', }) + head = workbook.add_format({'align': 'center', 'bold': True, 'font_size': '20px'}) + txt = workbook.add_format({'font_size': '10px', 'align': 'center'}) + sheet.merge_range('G2:N3', 'Product Sales Indent Report', head) + if data['start_date'] and data['end_date']: + sheet.write('G6', 'From:', cell_format) + sheet.merge_range('H6:I6', data['start_date'], txt) + sheet.write('L6', 'To:', cell_format) + sheet.merge_range('M6:N6', data['end_date'], txt) + format1 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bg_color': '#f5f9ff', 'border': 1}) + format3 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, 'bg_color': '#6BA6FE', 'border': 1}) + format4 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, 'bg_color': '#b6d0fa', 'border': 1}) + if data['categ_id']: + categ = data['categ_id'] + if data['partner_id']: + partner = data['partner_id'] + h_row = 6 + h_col = 10 + c_row = 6 + row = 7 + row_number = 6 + for rec in partner: + count = 0 + h_row += 1 + sheet.merge_range(h_row, h_col - 1, h_row, h_col, rec['name'], format3) + c_row = c_row + 2 + row += 2 + row_number += 2 + for cat in categ: + count += 2 + c_col = 10 + col = 9 + sheet.merge_range(c_row, c_col - 1, c_row, c_col, cat['name'], format1) + sheet.write(row, col, 'Product', format4) + sheet.set_column('J:J', 17) + col += 1 + sheet.write(row, col, 'Quantity', format4) + sheet.set_column('K:K', 17) + row_number += 2 + c_count = 0 + for val in data['form']: + if val['category_id'] == cat['id'] and val['partner_id'] == rec['id']: + c_count += 1 + count += 1 + column_number = 9 + sheet.write(row_number, column_number, val['product_id'], format1) + sheet.set_column('J:J', 17) + column_number += 1 + sheet.write(row_number, column_number, val['quantity'], format1) + sheet.set_column('K:K', 17) + row_number += 1 + + row = row + c_count + 2 + c_row = c_row + c_count + 2 + h_row = h_row + count + 1 + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/sale_report_advanced/wizard/sale_indent.xml b/sale_report_advanced/wizard/sale_indent.xml new file mode 100644 index 000000000..72084bc3d --- /dev/null +++ b/sale_report_advanced/wizard/sale_indent.xml @@ -0,0 +1,37 @@ + + + + Product Sales Indent + sale.report.indent + +
+ + + + + + + + + + + + +
+
+
+
+
+ + Product Sales Indent Report + ir.actions.act_window + sale.report.indent + form + + new + +
\ No newline at end of file diff --git a/sale_report_advanced/wizard/sale_invoice.py b/sale_report_advanced/wizard/sale_invoice.py new file mode 100644 index 000000000..9df4c8746 --- /dev/null +++ b/sale_report_advanced/wizard/sale_invoice.py @@ -0,0 +1,271 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +import json +import io +from xlsxwriter import workbook + +from odoo.tools import date_utils +from odoo import fields, models + +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class SaleReportAdvance(models.TransientModel): + _name = "sale.report.invoice" + + customer_ids = fields.Many2many('res.partner', string="Customers",required=True) + from_date = fields.Date(string="Start Date") + to_date = fields.Date(string="End Date") + status = fields.Selection([('open', 'Open'), ('paid', 'Paid'), ('both', 'Both')], + string='Status', default='open', reqired=True) + company_ids = fields.Many2many('res.company', string='Companies') + today_date = fields.Date(default=fields.Date.today()) + + def get_invoice_report(self): + datas = self._get_data() + return self.env.ref('sale_report_advanced.action_invoice_analysis').report_action([], data=datas) + + def _get_data(self): + + sale = self.env['sale.order'].search([('state','!=','cancel')]) + + if self.from_date and self.to_date and self.company_ids: + sales_order = list(filter(lambda + x: x.date_order.date() >= self.from_date and x.date_order.date() <= self.to_date and x.company_id in self.company_ids, + sale)) + elif not self.from_date and self.to_date and self.company_ids: + sales_order = list(filter(lambda + x: x.date_order.date() <= self.to_date and x.company_id in self.company_ids, + sale)) + elif self.from_date and not self.to_date and self.company_ids: + sales_order = list(filter(lambda + x: x.date_order.date() >= self.from_date and x.company_id in self.company_ids, + sale)) + elif self.from_date and self.to_date and not self.company_ids: + sales_order = list(filter(lambda + x: x.date_order.date() >= self.from_date and x.date_order.date() <= self.to_date, + sale)) + elif not self.from_date and not self.to_date and self.company_ids: + sales_order = list(filter(lambda + x: x.company_id in self.company_ids, + sale)) + elif not self.from_date and self.to_date and not self.company_ids: + sales_order = list(filter(lambda + x: x.date_order.date() <= self.to_date, + sale)) + elif self.from_date and not self.to_date and not self.company_ids: + sales_order = list(filter(lambda + x: x.date_order.date() >= self.from_date, + sale)) + else: + sales_order = sale + + result = [] + customers = [] + for rec in self.customer_ids: + a = { + 'id': rec, + 'name': rec.name + } + customers.append(a) + for so in sales_order: + for cust in customers: + if cust['id'] == so['partner_id']: + if so.invoice_ids: + if self.status == 'open': + for inv in so.invoice_ids: + if inv.payment_state != 'paid': + res = { + 'so': so.name, + 'partner_id': so.partner_id, + 'order_date': so.date_order, + 'invoice': inv.name, + 'date': inv.invoice_date, + 'invoiced': inv.amount_total, + 'paid': inv.amount_total-inv.amount_residual, + 'due': inv.amount_residual, + } + result.append(res) + elif self.status == 'paid': + for inv in so.invoice_ids: + if inv.payment_state == 'paid': + res = { + 'so': so.name, + 'partner_id': so.partner_id, + 'order_date': so.date_order, + 'invoice': inv.name, + 'date': inv.invoice_date, + 'invoiced': inv.amount_total, + 'paid': inv.amount_total-inv.amount_residual, + 'due': inv.amount_residual, + } + result.append(res) + else: + for inv in so.invoice_ids: + res = { + 'so': so.name, + 'partner_id': so.partner_id, + 'order_date': so.date_order, + 'invoice': inv.name, + 'date': inv.invoice_date, + 'invoiced': inv.amount_total, + 'paid': inv.amount_total-inv.amount_residual, + 'due': inv.amount_residual, + } + result.append(res) + datas = { + 'ids': self, + 'model': 'sale.report.invoice', + 'form': result, + 'partner_id': customers, + 'start_date': self.from_date, + 'end_date': self.to_date, + 'status': self.status, + } + + return datas + + def get_excel_invoice_report(self): + datas = self._get_data() + return { + 'type': 'ir.actions.report', + 'report_type': 'xlsx', + 'data': {'model': 'sale.report.invoice', + 'output_format': 'xlsx', + 'options': json.dumps(datas, default=date_utils.json_default), + 'report_name': 'Excel Report Name', + }, + } + + def get_xlsx_report(self, data, response): + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + record =[] + cell_format = workbook.add_format({'font_size': '12px',}) + head = workbook.add_format({'align': 'center', 'bold': True, 'font_size': '20px'}) + txt = workbook.add_format({'font_size': '10px','align': 'center'}) + sheet.merge_range('G2:M3', 'Invoice Analysis Report', head) + if data['start_date'] and data['end_date']: + sheet.write('G6', 'From:', cell_format) + sheet.merge_range('H6:I6', data['start_date'], txt) + sheet.write('K6', 'To:', cell_format) + sheet.merge_range('L6:M6', data['end_date'], txt) + + format1 = workbook.add_format( + {'font_size': 10, 'align': 'left','bg_color':'#bbd5fc','border': 1}) + format4 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bg_color': '#bbd5fc', 'border': 1}) + format2 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#6BA6FE', 'border': 1}) + format3 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True}) + record = data['partner_id'] + h_row = 7 + h_col = 9 + count = 0 + row = 5 + col = 6 + row_number = 6 + t_row = 6 + if data['partner_id']: + for rec in record: + sheet.merge_range(h_row, h_col-3,h_row,h_col+3, rec['name'], format4) + row= row + count + 3 + sheet.write(row, col, 'Order Number', format2) + sheet.set_column('G:G', 12) + col += 1 + sheet.write(row, col, 'Order Date', format2) + sheet.set_column('H:H', 15) + col += 1 + sheet.write(row, col, 'Invoice Number', format2) + sheet.set_column('I:I', 13) + col += 1 + sheet.write(row, col, 'Invoice Date', format2) + sheet.set_column('J:J', 15) + col += 1 + sheet.write(row, col, 'Amount Invoiced', format2) + sheet.set_column('K:K', 11) + col += 1 + sheet.write(row, col, 'Amount Paid', format2) + sheet.set_column('L:L', 11) + col += 1 + sheet.write(row, col, 'Amount Due', format2) + sheet.set_column('M:M', 11) + col += 1 + col =6 + count=0 + t_invoiced = 0 + t_paid = 0 + t_due = 0 + row_number=row_number + count + 3 + t_col = 9 + for val in data['form']: + if val['partner_id'] == rec['id']: + count +=1 + column_number = 6 + sheet.write(row_number, column_number, val['so'],format1) + sheet.set_column('G:G', 12) + column_number += 1 + sheet.write(row_number, column_number, val['order_date'], format1) + sheet.set_column('H:H', 15) + column_number += 1 + sheet.write(row_number, column_number, val['invoice'], format1) + sheet.set_column('I:I', 13) + column_number += 1 + sheet.write(row_number, column_number, val['date'], format1) + sheet.set_column('J:J', 15) + column_number += 1 + sheet.write(row_number, column_number, val['invoiced'], format1) + sheet.set_column('K:K', 14) + t_invoiced += val['invoiced'] + column_number += 1 + sheet.write(row_number, column_number, val['paid'], format1) + sheet.set_column('L:L', 11) + t_paid += val['paid'] + column_number += 1 + sheet.write(row_number, column_number, val['due'], format1) + sheet.set_column('M:M', 11) + t_due += val['due'] + row_number += 1 + t_row = t_row + count + 3 + sheet.write(t_row, t_col, 'Total', format3) + sheet.set_column('J:J', 15) + t_col += 1 + sheet.write(t_row, t_col, t_invoiced, format3) + sheet.set_column('K:K', 14) + t_col += 1 + sheet.write(t_row, t_col, t_paid, format3) + sheet.set_column('L:L', 11) + t_col += 1 + sheet.write(t_row, t_col, t_due, format3) + sheet.set_column('M:M', 11) + h_row = h_row + count + 3 + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/sale_report_advanced/wizard/sale_invoice.xml b/sale_report_advanced/wizard/sale_invoice.xml new file mode 100644 index 000000000..779e883d4 --- /dev/null +++ b/sale_report_advanced/wizard/sale_invoice.xml @@ -0,0 +1,35 @@ + + + + Sales Invoice Analysis + sale.report.invoice + +
+ + + + + + + + + +
+
+
+
+
+ + Sale Invoice Report + ir.actions.act_window + sale.report.invoice + form + + new + +
\ No newline at end of file diff --git a/sale_report_advanced/wizard/sale_report.py b/sale_report_advanced/wizard/sale_report.py new file mode 100644 index 000000000..909979ca6 --- /dev/null +++ b/sale_report_advanced/wizard/sale_report.py @@ -0,0 +1,428 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +import json +import io +from datetime import datetime +from xlsxwriter import workbook + +from odoo.tools import date_utils +from odoo import fields, models +from odoo.exceptions import UserError, ValidationError + +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class SaleReportAdvance(models.TransientModel): + _name = "sale.report.advance" + + customer_ids = fields.Many2many('res.partner', string="Customers") + product_ids = fields.Many2many('product.product', string='Products') + from_date = fields.Date(string="Start Date") + to_date = fields.Date(string="End Date") + type = fields.Selection([('customer', 'Customers'), ('product', 'Products'), ('both', 'Both')], + string='Report Print By', default='customer', reqired=True) + company_ids = fields.Many2many('res.company', string='Companies') + today_date = fields.Date(default=fields.Date.today()) + + def _get_data(self): + sale = self.env['sale.order'].search([('state','!=','cancel')]) + sales_order_line = self.env['sale.order.line'].search([('order_id.state','!=','cancel')]) + + if self.from_date and self.to_date and self.company_ids: + sales_order = list(filter(lambda + x: x.date_order.date() >= self.from_date and x.date_order.date() <= self.to_date and x.company_id in self.company_ids, + sale)) + elif not self.from_date and self.to_date and self.company_ids: + sales_order = list(filter(lambda + x: x.date_order.date() <= self.to_date and x.company_id in self.company_ids, + sale)) + elif self.from_date and not self.to_date and self.company_ids: + sales_order = list(filter(lambda + x: x.date_order.date() >= self.from_date and x.company_id in self.company_ids, + sale)) + elif self.from_date and self.to_date and not self.company_ids: + sales_order = list(filter(lambda + x: x.date_order.date() >= self.from_date and x.date_order.date() <= self.to_date, + sale)) + elif not self.from_date and not self.to_date and self.company_ids: + sales_order = list(filter(lambda + x: x.company_id in self.company_ids, + sale)) + elif not self.from_date and self.to_date and not self.company_ids: + sales_order = list(filter(lambda + x: x.date_order.date() <= self.to_date, + sale)) + elif self.from_date and not self.to_date and not self.company_ids: + sales_order = list(filter(lambda + x: x.date_order.date() >= self.from_date, + sale)) + else: + sales_order = sale + result = [] + customers = [] + products = [] + for rec in self.customer_ids: + a = { + 'id': rec, + 'name': rec.name + } + customers.append(a) + for rec in self.product_ids: + a = { + 'id': rec, + 'name': rec.name + } + products.append(a) + + if self.type == 'product': + for rec in products: + for lines in sales_order_line: + if lines.product_id == rec['id']: + profit = round(lines.product_id.list_price - lines.product_id.standard_price, 2) + if lines.product_id.standard_price != 0: + margin = round((profit * 100) / lines.product_id.standard_price, 2) + res = { + 'sequence': lines.order_id.name, + 'date': lines.order_id.date_order, + 'product_id': lines.product_id, + 'quantity': lines.product_uom_qty, + 'cost': lines.product_id.standard_price, + 'price': lines.product_id.list_price, + 'profit': profit, + 'margin': margin, + 'partner': lines.order_id.partner_id.name, + } + result.append(res) + if self.type == 'customer': + for rec in customers: + for so in sales_order: + if so.partner_id == rec['id']: + for lines in so.order_line: + profit = round(lines.product_id.list_price - lines.product_id.standard_price, 2) + if lines.product_id.standard_price != 0: + margin = round((profit * 100) / lines.product_id.standard_price, 2) + res = { + 'sequence': so.name, + 'date': so.date_order, + 'product': lines.product_id.name, + 'quantity': lines.product_uom_qty, + 'cost': lines.product_id.standard_price, + 'price': lines.product_id.list_price, + 'profit': profit, + 'margin': margin, + 'partner_id': so.partner_id, + } + result.append(res) + if self.type == 'both': + for rec in customers: + for p in products: + for so in sales_order: + if so.partner_id == rec['id']: + for lines in so.order_line: + if lines.product_id == p['id']: + profit = round(lines.product_id.list_price - lines.product_id.standard_price, 2) + if lines.product_id.standard_price != 0: + margin = round((profit * 100) / lines.product_id.standard_price, 2) + res = { + 'sequence': so.name, + 'date': so.date_order, + 'product': lines.product_id.name, + 'quantity': lines.product_uom_qty, + 'cost': lines.product_id.standard_price, + 'price': lines.product_id.list_price, + 'profit': profit, + 'margin': margin, + 'partner': so.partner_id.name, + } + result.append(res) + if self.from_date and self.to_date and not self.customer_ids and not self.product_ids: + for so in sales_order: + for lines in so.order_line: + profit = round(lines.product_id.list_price - lines.product_id.standard_price, 2) + if lines.product_id.standard_price != 0: + margin = round((profit * 100) / lines.product_id.standard_price, 2) + res = { + 'sequence': so.name, + 'date': so.date_order, + 'product': lines.product_id.name, + 'quantity': lines.product_uom_qty, + 'cost': lines.product_id.standard_price, + 'price': lines.product_id.list_price, + 'profit': profit, + 'margin': margin, + 'partner': so.partner_id.name, + } + result.append(res) + + datas = { + 'ids': self, + 'model': 'sale.report.advance', + 'form': result, + 'partner_id': customers, + 'product_id': products, + 'start_date': self.from_date, + 'end_date': self.to_date, + 'type': self.type, + 'no_value': False, + + } + if self.from_date and self.to_date and not self.customer_ids and not self.product_ids: + datas['no_value']=True + return datas + + def get_report(self): + datas = self._get_data() + return self.env.ref('sale_report_advanced.action_sale_report').report_action([], data=datas) + + def get_excel_report(self): + datas = self._get_data() + return { + 'type': 'ir.actions.report', + 'report_type': 'xlsx', + 'data': {'model': 'sale.report.advance', + 'output_format': 'xlsx', + 'options': json.dumps(datas, default=date_utils.json_default), + 'report_name': 'Excel Report Name', + }, + } + + def get_xlsx_report(self, data, response): + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + record = [] + cell_format = workbook.add_format({'font_size': '12px', }) + head = workbook.add_format({'align': 'center', 'bold': True, 'font_size': '20px'}) + txt = workbook.add_format({'font_size': '10px', 'align': 'center'}) + sheet.merge_range('G2:N3', 'Sales Profit Report', head) + if data['start_date'] and data['end_date']: + sheet.write('G6', 'From:', cell_format) + sheet.merge_range('H6:I6', data['start_date'], txt) + sheet.write('L6', 'To:', cell_format) + sheet.merge_range('M6:N6', data['end_date'], txt) + format1 = workbook.add_format( + {'font_size': 10, 'align': 'center','bg_color':'#bbd5fc','border': 1}) + format2 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#6BA6FE', 'border': 1}) + format4 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True,'border': 1}) + format3 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, 'bg_color': '#c0dbfa', 'border': 1}) + if data['type'] == 'product': + record = data['product_id'] + if data['type'] == 'customer': + record = data['partner_id'] + h_row = 7 + h_col = 9 + count = 0 + row = 5 + col = 6 + row_number = 6 + t_row = 6 + if data['type'] == 'product' or data['type'] == 'customer': + for rec in record: + sheet.merge_range(h_row, h_col-3,h_row,h_col+4,rec['name'], format3) + row = row + count + 3 + sheet.write(row, col, 'Order', format2) + col += 1 + sheet.write(row, col, 'Date', format2) + sheet.set_column('H:H', 15) + col += 1 + if data['type'] == 'product': + sheet.write(row, col, 'Customer', format2) + sheet.set_column('I:I', 20) + col += 1 + elif data['type'] == 'customer': + sheet.write(row, col, 'Product', format2) + sheet.set_column('I:I', 20) + col += 1 + sheet.write(row, col, 'Quantity', format2) + col += 1 + sheet.write(row, col, 'Cost', format2) + col += 1 + sheet.write(row, col, 'Price', format2) + col += 1 + sheet.write(row, col, 'Profit', format2) + col += 1 + sheet.write(row, col, 'Margin(%)', format2) + col += 1 + col = 6 + count = 0 + row_number = row_number + count + 3 + t_qty = 0 + t_cost = 0 + t_price = 0 + t_profit = 0 + t_margin = 0 + t_col = 8 + for val in data['form']: + if data['type'] == 'customer': + if val['partner_id'] == rec['id']: + count += 1 + column_number = 6 + sheet.write(row_number, column_number, val['sequence'], format1) + column_number += 1 + sheet.write(row_number, column_number, val['date'], format1) + sheet.set_column('H:H', 15) + column_number += 1 + sheet.write(row_number, column_number, val['product'], format1) + sheet.set_column('I:I', 20) + column_number += 1 + sheet.write(row_number, column_number, val['quantity'], format1) + t_qty += val['quantity'] + column_number += 1 + sheet.write(row_number, column_number, val['cost'], format1) + t_cost += val['cost'] + column_number += 1 + sheet.write(row_number, column_number, val['price'], format1) + t_price += val['price'] + column_number += 1 + sheet.write(row_number, column_number, val['profit'], format1) + t_profit += val['profit'] + column_number += 1 + sheet.write(row_number, column_number, val['margin'], format1) + t_margin += val['margin'] + column_number += 1 + row_number += 1 + if data['type'] == 'product': + if val['product_id'] == rec['id']: + count += 1 + column_number = 6 + sheet.write(row_number, column_number, val['sequence'], format1) + column_number += 1 + sheet.write(row_number, column_number, val['date'], format1) + sheet.set_column('H:H', 15) + column_number += 1 + sheet.write(row_number, column_number, val['partner'], format1) + sheet.set_column('I:I', 20) + column_number += 1 + sheet.write(row_number, column_number, val['quantity'], format1) + t_qty += val['quantity'] + column_number += 1 + sheet.write(row_number, column_number, val['cost'], format1) + t_cost += val['cost'] + column_number += 1 + sheet.write(row_number, column_number, val['price'], format1) + t_price += val['price'] + column_number += 1 + sheet.write(row_number, column_number, val['profit'], format1) + t_profit += val['profit'] + column_number += 1 + sheet.write(row_number, column_number, val['margin'], format1) + t_margin += val['margin'] + column_number += 1 + row_number += 1 + t_row = t_row + count + 3 + sheet.write(t_row, t_col, 'Total', format4) + t_col += 1 + sheet.write(t_row, t_col, t_qty, format4) + t_col += 1 + sheet.write(t_row, t_col, t_cost, format4) + t_col += 1 + sheet.write(t_row, t_col, t_price, format4) + t_col += 1 + sheet.write(t_row, t_col, t_profit, format4) + t_col += 1 + sheet.write(t_row, t_col, t_margin, format4) + t_col += 1 + h_row = h_row + count + 3 + if data['type'] == 'both' or data['no_value'] == True: + row += 3 + row_number += 2 + t_qty = 0 + t_cost = 0 + t_price = 0 + t_profit = 0 + t_margin = 0 + t_col = 9 + sheet.write(row, col, 'Order', format2) + col += 1 + sheet.write(row, col, 'Date', format2) + sheet.set_column('H:H', 15) + col += 1 + sheet.write(row, col, 'Customer', format2) + sheet.set_column('I:I', 20) + col += 1 + sheet.write(row, col, 'Product', format2) + sheet.set_column('J:J', 20) + col += 1 + sheet.write(row, col, 'Quantity', format2) + col += 1 + sheet.write(row, col, 'Cost', format2) + col += 1 + sheet.write(row, col, 'Price', format2) + col += 1 + sheet.write(row, col, 'Profit', format2) + col += 1 + sheet.write(row, col, 'Margin', format2) + col += 1 + row_number+=1 + for val in data['form']: + column_number = 6 + sheet.write(row_number, column_number, val['sequence'], format1) + column_number += 1 + sheet.write(row_number, column_number, val['date'], format1) + sheet.set_column('H:H', 15) + column_number += 1 + sheet.write(row_number, column_number, val['partner'], format1) + sheet.set_column('I:I', 20) + column_number += 1 + sheet.write(row_number, column_number, val['product'], format1) + sheet.set_column('J:J', 20) + column_number += 1 + sheet.write(row_number, column_number, val['quantity'], format1) + t_qty += val['quantity'] + column_number += 1 + sheet.write(row_number, column_number, val['cost'], format1) + t_cost += val['cost'] + column_number += 1 + sheet.write(row_number, column_number, val['price'], format1) + t_price += val['price'] + column_number += 1 + sheet.write(row_number, column_number, val['profit'], format1) + t_profit += val['profit'] + column_number += 1 + sheet.write(row_number, column_number, val['margin'], format1) + t_margin += val['margin'] + column_number += 1 + row_number += 1 + sheet.write(row_number, t_col, 'Total', format4) + t_col += 1 + sheet.write(row_number, t_col, t_qty, format4) + t_col += 1 + sheet.write(row_number, t_col, t_cost, format4) + t_col += 1 + sheet.write(row_number, t_col, t_price, format4) + t_col += 1 + sheet.write(row_number, t_col, t_profit, format4) + t_col += 1 + sheet.write(row_number, t_col, t_margin, format4) + t_col += 1 + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/sale_report_advanced/wizard/sale_report.xml b/sale_report_advanced/wizard/sale_report.xml new file mode 100644 index 000000000..1fe927039 --- /dev/null +++ b/sale_report_advanced/wizard/sale_report.xml @@ -0,0 +1,35 @@ + + + + Sales Product Profit + sale.report.advance + +
+ + + + + + + + + + +
+
+
+
+
+ + Sale Report + ir.actions.act_window + sale.report.advance + form + + new + +
\ No newline at end of file diff --git a/sale_report_advanced/wizard/weekly_wise.py b/sale_report_advanced/wizard/weekly_wise.py new file mode 100644 index 000000000..922ddbddf --- /dev/null +++ b/sale_report_advanced/wizard/weekly_wise.py @@ -0,0 +1,219 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + + +import json +import io +import calendar +from datetime import timedelta, datetime +from dateutil import rrule + +from reportlab.platypus.tableofcontents import delta +from xlsxwriter import workbook + +from odoo.tools import date_utils +from odoo import fields, models,_ +from calendar import monthrange +from odoo.exceptions import UserError, ValidationError + +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class SaleReportAdvance(models.TransientModel): + _name = "sale.report.weekly" + + date = fields.Date(string='Date', required=True) + invoice_status = fields.Selection( + [('invoiced', 'Fully Invoiced'), + ('to invoice', 'To Invoice'), + ('no', 'Nothing to Invoice')], + string='Invoice Status', default='no', required=True) + amount_type = fields.Selection( + [('total', 'Total Amount'), ('untax', 'Untaxed Amount')], + string='Total Amount', default='total') + today_date = fields.Date(default=fields.Date.today()) + + def get_weekly_report(self): + datas = self._get_data() + return self.env.ref('sale_report_advanced.action_sales_weekly').report_action([], data=datas) + + def _get_data(self): + result = [] + times=[{'id':'morning','name':'Morning (5:00-12:00)'}, {'id':'noon','name':'Noon (1:00-17:00)'},{'id':'evening','name':'Evening (18:00-23:00)'}] + if self.invoice_status == 'invoiced': + sale_order = self.env['sale.order'].sudo().search([('invoice_status', '=', self.invoice_status),('date_order','>',self.date),('state','!=','cancel')]) + elif self.invoice_status == 'to invoice': + sale_order = self.env['sale.order'].sudo().search([('invoice_status', '=', self.invoice_status),('date_order','>',self.date),('state','!=','cancel')]) + else: + sale_order = self.env['sale.order'].sudo().search([('invoice_status', '=', self.invoice_status),('date_order','>',self.date),('state','!=','cancel')]) + for rec in sale_order: + if self.amount_type=='total': + if rec.date_order.hour > 5 and rec.date_order.hour <=12: + res = { + 'order': rec.name, + 'amount':rec.amount_total , + 'time':'morning', + 'date':rec.date_order.date() + } + result.append(res) + + if rec.date_order.hour > 12 and rec.date_order.hour <=17: + res = { + 'order': rec.name, + 'amount': rec.amount_total, + 'time': 'noon', + 'date': rec.date_order.date() + + } + result.append(res) + + if rec.date_order.hour > 17 and rec.date_order.hour <=23: + res = { + 'order': rec.name, + 'amount': rec.amount_total, + 'time': 'evening', + 'date': rec.date_order.date() + + } + result.append(res) + else: + if rec.date_order.hour > 5 and rec.date_order.hour <= 12: + res = { + 'order':rec.name, + 'amount':rec.amount_untaxed, + 'time':'morning', + 'date': rec.date_order.date() + + } + result.append(res) + + if rec.date_order.hour > 12 and rec.date_order.hour <= 17: + res = { + 'order': rec.name, + 'amount': rec.amount_untaxed, + 'time': 'noon', + 'date': rec.date_order.date() + + } + result.append(res) + + if rec.date_order.hour > 17 and rec.date_order.hour <= 23: + res = { + 'order': rec.name, + 'amount': rec.amount_untaxed, + 'time': 'evening', + 'date': rec.date_order.date() + + } + result.append(res) + datas = { + 'ids': self, + 'model': 'sale.report.weekly', + 'form': result, + 'date': self.date, + 'type':self.amount_type, + 'times':times + + } + return datas + + def get_excel_weekly_report(self): + datas = self._get_data() + return { + 'type': 'ir.actions.report', + 'report_type': 'xlsx', + 'data': {'model': 'sale.report.weekly', + 'output_format': 'xlsx', + 'options': json.dumps(datas, default=date_utils.json_default), + 'report_name': 'Excel Report Name', + }, + } + + def get_xlsx_report(self, data, response): + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + head = workbook.add_format({'align': 'center', 'bold': True, 'font_size': '20px'}) + sheet.merge_range('G2:I3', 'Hourly Sales Report', head) + format1 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bg_color': '#f5f9ff', 'border': 1}) + format2 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#6BA6FE', 'border': 1}) + format3 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True,'bg_color':'#95ff63','border': 1}) + format4 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, 'border': 1}) + h_row = 7 + h_col = 7 + count = 0 + row = 4 + row_number = 5 + for rec in data['times']: + col = 6 + row = row + count + 4 + sheet.merge_range(h_row,h_col-1,h_row,h_col+1,rec['name'] , format3) + sheet.write(row, col, 'Order', format2) + sheet.set_column(row, col, 15) + col += 1 + sheet.write(row, col, 'Date', format2) + sheet.set_column(row, col, 15) + col += 1 + if data['type'] =='total': + sheet.write(row, col, 'Total', format2) + sheet.set_column(row, col, 15) + col += 1 + else: + sheet.write(row, col, 'Untaxed Total', format2) + sheet.set_column(row, col, 15) + col += 1 + t_total = 0 + + row_number = row_number +4 + count=0 + t_col = 7 + for val in data['form']: + if val['time'] == rec['id']: + count += 1 + column_number = 6 + sheet.write(row_number, column_number, val['order'], format1) + sheet.set_column(row_number, column_number, 15) + column_number += 1 + sheet.write(row_number, column_number, val['date'], format1) + sheet.set_column(row_number, column_number, 15) + column_number += 1 + sheet.write(row_number, column_number, val['amount'], format1) + t_total += val['amount'] + sheet.set_column(row_number, column_number, 15) + row_number+=1 + sheet.write(row_number, t_col, 'Total', format4) + sheet.set_column(row_number, t_col, 15) + t_col += 1 + sheet.write(row_number, t_col, t_total, format4) + h_row= h_row+ count+4 + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/sale_report_advanced/wizard/weekly_wise.xml b/sale_report_advanced/wizard/weekly_wise.xml new file mode 100644 index 000000000..5836c0576 --- /dev/null +++ b/sale_report_advanced/wizard/weekly_wise.xml @@ -0,0 +1,32 @@ + + + + Sales Hourly Wise report + sale.report.weekly + +
+ + + + + + + +
+
+
+
+
+ + Hourly Sales Report + ir.actions.act_window + sale.report.weekly + form + + new + +
\ No newline at end of file diff --git a/web_login_styles/README.rst b/web_login_styles/README.rst new file mode 100644 index 000000000..452c9d363 --- /dev/null +++ b/web_login_styles/README.rst @@ -0,0 +1,34 @@ +Login Styles v16 +================ +This module will helps you to customize the login layout and background. + + +Configuration +============= +* No additional configurations needed + +Company +------- +* `Cybrosys Techno Solutions `__ + +Credits +------- +* Developers: Version 15: Neethu UM, odoo@cybrosys.com + Version 16: Amaya Aravind EV, odoo@cybrosys.com + + +Contacts +-------- +* Mail Contact : odoo@cybrosys.com +* Website : https://cybrosys.com + +Bug Tracker +----------- +Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. + +Maintainer +========== +.. image:: https://cybrosys.com/images/logo.png + :target: https://cybrosys.com + +This module is maintained by Cybrosys Technologies. diff --git a/web_login_styles/__init__.py b/web_login_styles/__init__.py new file mode 100644 index 000000000..339721a08 --- /dev/null +++ b/web_login_styles/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import controllers +from . import models diff --git a/web_login_styles/__manifest__.py b/web_login_styles/__manifest__.py new file mode 100644 index 000000000..f0c34f99d --- /dev/null +++ b/web_login_styles/__manifest__.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +{ + 'name': 'Customize Login Page Style', + 'version': '16.0.1.0.0', + 'summary': 'Customize The Login Page With Different Styles', + 'description': 'Customize The Login Page With Different Styles', + 'category': 'Extra Tools', + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'website': 'https://www.cybrosys.com', + 'license': 'LGPL-3', + 'depends': [ + 'base', + 'base_setup', + 'web', + ], + 'data': [ + 'views/res_config_settings_views.xml', + 'views/webclient_templates_right.xml', + 'views/webclient_templates_left.xml', + 'views/webclient_templates_middle.xml', + ], + 'images': ['static/description/banner.png'], + 'installable': True, + 'auto_install': False, + 'application': False, +} diff --git a/web_login_styles/controllers/__init__.py b/web_login_styles/controllers/__init__.py new file mode 100644 index 000000000..b2a2bc71e --- /dev/null +++ b/web_login_styles/controllers/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import main diff --git a/web_login_styles/controllers/main.py b/web_login_styles/controllers/main.py new file mode 100644 index 000000000..4fdfe7662 --- /dev/null +++ b/web_login_styles/controllers/main.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +import json + +import werkzeug +import hashlib +from odoo.tools import pycompat + +from odoo import http +from odoo.http import request +import odoo +from werkzeug.urls import url_encode, iri_to_uri +from odoo.tools.translate import _ +from odoo.addons.web.controllers import main +from odoo import http +from odoo.addons.web.controllers.home import Home as WebHome +from odoo.addons.web.controllers.utils import is_user_internal +from odoo.http import request +from odoo.addons.web.controllers.utils import ensure_db, _get_login_redirect_url + + +# Shared parameters for all login/signup flows +SIGN_UP_REQUEST_PARAMS = {'db', 'login', 'debug', 'token', 'message', 'error', 'scope', 'mode', + 'redirect', 'redirect_hostname', 'email', 'name', 'partner_id', + 'password', 'confirm_password', 'city', 'country_id', 'lang'} + + +class Home(WebHome): + + @http.route('/web/login', type='http', auth="none") + def web_login(self, redirect=None, **kw): + ensure_db() + request.params['login_success'] = False + if request.httprequest.method == 'GET' and redirect and request.session.uid: + return request.redirect(redirect) + + if not request.uid: + request.update_env(user=odoo.SUPERUSER_ID) + + values = {k: v for k, v in request.params.items() if k in SIGN_UP_REQUEST_PARAMS} + try: + values['databases'] = http.db_list() + except odoo.exceptions.AccessDenied: + values['databases'] = None + + if request.httprequest.method == 'POST': + old_uid = request.uid + try: + uid = request.session.authenticate(request.session.db, request.params['login'], + request.params['password']) + request.params['login_success'] = True + return request.redirect(self._login_redirect(uid, redirect=redirect)) + except odoo.exceptions.AccessDenied as e: + request.uid = old_uid + if e.args == odoo.exceptions.AccessDenied().args: + values['error'] = _("Wrong login/password") + else: + values['error'] = e.args[0] + else: + if 'error' in request.params and request.params.get('error') == 'access': + values['error'] = _('Only employees can access this database. Please contact the administrator.') + + if 'login' not in values and request.session.get('auth_login'): + values['login'] = request.session.get('auth_login') + + if not odoo.tools.config['list_db']: + values['disable_database_manager'] = True + conf_param = request.env['ir.config_parameter'].sudo() + orientation = conf_param.get_param('web_login_styles.orientation') + image = conf_param.get_param('web_login_styles.image') + url = conf_param.get_param('web_login_styles.url') + + background_type = conf_param.get_param('web_login_styles.background') + + if background_type == 'color': + values['bg'] = '' + values['color'] = conf_param.get_param('web_login_styles.color') + elif background_type == 'image': + exist_rec = request.env['ir.attachment'].search([('is_background', '=', True)]) + if exist_rec: + exist_rec.unlink() + attachments = request.env['ir.attachment'].create({ + 'name': 'Background Image', + 'datas': image, + 'type': 'binary', + 'mimetype': 'image/png', + 'public': True, + 'is_background': True + }) + base_url = conf_param.get_param('web.base.url') + url = base_url + '/web/image?' + 'model=ir.attachment&id=' + str(attachments.id) + '&field=datas' + values['bg_img'] = url or '' + elif background_type == 'url': + pre_exist = request.env['ir.attachment'].search([('url', '=', url)]) + if not pre_exist: + attachments = request.env['ir.attachment'].create({ + 'name': 'Background Image URL', + 'url': url, + 'type': 'url', + 'public': True + }) + else: + attachments = pre_exist + encode = hashlib.md5(pycompat.to_text(attachments.url).encode("utf-8")).hexdigest()[0:7] + encode_url = "/web/image/{}-{}".format(attachments.id, encode) + values['bg_img'] = encode_url or '' + + if orientation == 'right': + response = request.render('web_login_styles.login_template_right', values) + elif orientation == 'left': + response = request.render('web_login_styles.login_template_left', values) + elif orientation == 'middle': + response = request.render('web_login_styles.login_template_middle', values) + else: + response = request.render('web.login', values) + response.headers['X-Frame-Options'] = 'DENY' + return response diff --git a/web_login_styles/doc/RELEASE_NOTES.md b/web_login_styles/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..41a886cc3 --- /dev/null +++ b/web_login_styles/doc/RELEASE_NOTES.md @@ -0,0 +1,10 @@ +## Module + +#### 15.12.2022 +#### Version 16.0.1.0.0 +#### ADD +Initial Commit for Login Styles. + + + + diff --git a/web_login_styles/models/__init__.py b/web_login_styles/models/__init__.py new file mode 100644 index 000000000..67db10fb6 --- /dev/null +++ b/web_login_styles/models/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import res_config_settings +from . import ir_attachment + diff --git a/web_login_styles/models/ir_attachment.py b/web_login_styles/models/ir_attachment.py new file mode 100644 index 000000000..e79ad8dd6 --- /dev/null +++ b/web_login_styles/models/ir_attachment.py @@ -0,0 +1,7 @@ +from odoo import models, fields, api + + +class AttachmentFile(models.Model): + _inherit = 'ir.attachment' + + is_background = fields.Boolean(string="Is Background", default=False) diff --git a/web_login_styles/models/res_config_settings.py b/web_login_styles/models/res_config_settings.py new file mode 100644 index 000000000..0743c28a0 --- /dev/null +++ b/web_login_styles/models/res_config_settings.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from odoo import fields, models, api + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + orientation = fields.Selection([('default', 'Default'), ('left', 'Left'), ('middle', 'Middle'), ('right', 'Right')], string="Orientation") + background = fields.Selection([('color', 'Color Picker'), ('image', 'Image'), ('url', 'URL')], string="Background") + image = fields.Binary(string="Image") + url = fields.Char(string="URL") + color = fields.Char(string="Color") + + @api.model + def get_values(self): + res = super(ResConfigSettings, self).get_values() + params = self.env['ir.config_parameter'].sudo() + res.update( + background=params.get_param('web_login_styles.background'), + orientation=params.get_param('web_login_styles.orientation'), + image=params.get_param('web_login_styles.image'), + url=params.get_param('web_login_styles.url'), + color=params.get_param('web_login_styles.color'), + ) + + return res + + def set_values(self): + super(ResConfigSettings, self).set_values() + params = self.env['ir.config_parameter'].sudo() + set_orientation = self.orientation or False + set_image = self.image or False + set_url = self.url or False + set_color = self.color or False + set_background = self.background or False + params.set_param('web_login_styles.background', set_background) + params.set_param('web_login_styles.orientation', set_orientation) + params.set_param('web_login_styles.image', set_image) + params.set_param('web_login_styles.url', set_url) + params.set_param('web_login_styles.color', set_color) + + @api.onchange('orientation') + def onchange_orientation(self): + if self.orientation == 'default': + self.background = False diff --git a/web_login_styles/static/description/assets/icons/check.png b/web_login_styles/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/web_login_styles/static/description/assets/icons/check.png differ diff --git a/web_login_styles/static/description/assets/icons/chevron.png b/web_login_styles/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/web_login_styles/static/description/assets/icons/chevron.png differ diff --git a/web_login_styles/static/description/assets/icons/cogs.png b/web_login_styles/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/web_login_styles/static/description/assets/icons/cogs.png differ diff --git a/web_login_styles/static/description/assets/icons/consultation.png b/web_login_styles/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/web_login_styles/static/description/assets/icons/consultation.png differ diff --git a/web_login_styles/static/description/assets/icons/ecom-black.png b/web_login_styles/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/web_login_styles/static/description/assets/icons/ecom-black.png differ diff --git a/web_login_styles/static/description/assets/icons/education-black.png b/web_login_styles/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/web_login_styles/static/description/assets/icons/education-black.png differ diff --git a/web_login_styles/static/description/assets/icons/hotel-black.png b/web_login_styles/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/web_login_styles/static/description/assets/icons/hotel-black.png differ diff --git a/web_login_styles/static/description/assets/icons/license.png b/web_login_styles/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/web_login_styles/static/description/assets/icons/license.png differ diff --git a/web_login_styles/static/description/assets/icons/lifebuoy.png b/web_login_styles/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/web_login_styles/static/description/assets/icons/lifebuoy.png differ diff --git a/web_login_styles/static/description/assets/icons/manufacturing-black.png b/web_login_styles/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/web_login_styles/static/description/assets/icons/manufacturing-black.png differ diff --git a/web_login_styles/static/description/assets/icons/pos-black.png b/web_login_styles/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/web_login_styles/static/description/assets/icons/pos-black.png differ diff --git a/web_login_styles/static/description/assets/icons/puzzle.png b/web_login_styles/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/web_login_styles/static/description/assets/icons/puzzle.png differ diff --git a/web_login_styles/static/description/assets/icons/restaurant-black.png b/web_login_styles/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/web_login_styles/static/description/assets/icons/restaurant-black.png differ diff --git a/web_login_styles/static/description/assets/icons/service-black.png b/web_login_styles/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/web_login_styles/static/description/assets/icons/service-black.png differ diff --git a/web_login_styles/static/description/assets/icons/trading-black.png b/web_login_styles/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/web_login_styles/static/description/assets/icons/trading-black.png differ diff --git a/web_login_styles/static/description/assets/icons/training.png b/web_login_styles/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/web_login_styles/static/description/assets/icons/training.png differ diff --git a/web_login_styles/static/description/assets/icons/update.png b/web_login_styles/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/web_login_styles/static/description/assets/icons/update.png differ diff --git a/web_login_styles/static/description/assets/icons/user.png b/web_login_styles/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/web_login_styles/static/description/assets/icons/user.png differ diff --git a/web_login_styles/static/description/assets/icons/wrench.png b/web_login_styles/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/web_login_styles/static/description/assets/icons/wrench.png differ diff --git a/web_login_styles/static/description/assets/misc/categories.png b/web_login_styles/static/description/assets/misc/categories.png new file mode 100644 index 000000000..bedf1e0b1 Binary files /dev/null and b/web_login_styles/static/description/assets/misc/categories.png differ diff --git a/web_login_styles/static/description/assets/misc/check-box.png b/web_login_styles/static/description/assets/misc/check-box.png new file mode 100644 index 000000000..42caf24b9 Binary files /dev/null and b/web_login_styles/static/description/assets/misc/check-box.png differ diff --git a/web_login_styles/static/description/assets/misc/compass.png b/web_login_styles/static/description/assets/misc/compass.png new file mode 100644 index 000000000..d5fed8faa Binary files /dev/null and b/web_login_styles/static/description/assets/misc/compass.png differ diff --git a/web_login_styles/static/description/assets/misc/corporate.png b/web_login_styles/static/description/assets/misc/corporate.png new file mode 100644 index 000000000..2eb13edbf Binary files /dev/null and b/web_login_styles/static/description/assets/misc/corporate.png differ diff --git a/web_login_styles/static/description/assets/misc/customer-support.png b/web_login_styles/static/description/assets/misc/customer-support.png new file mode 100644 index 000000000..79efc72ed Binary files /dev/null and b/web_login_styles/static/description/assets/misc/customer-support.png differ diff --git a/web_login_styles/static/description/assets/misc/cybrosys-logo.png b/web_login_styles/static/description/assets/misc/cybrosys-logo.png new file mode 100644 index 000000000..cc3cc0ccf Binary files /dev/null and b/web_login_styles/static/description/assets/misc/cybrosys-logo.png differ diff --git a/web_login_styles/static/description/assets/misc/features.png b/web_login_styles/static/description/assets/misc/features.png new file mode 100644 index 000000000..b41769f77 Binary files /dev/null and b/web_login_styles/static/description/assets/misc/features.png differ diff --git a/web_login_styles/static/description/assets/misc/logo.png b/web_login_styles/static/description/assets/misc/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/web_login_styles/static/description/assets/misc/logo.png differ diff --git a/web_login_styles/static/description/assets/misc/pictures.png b/web_login_styles/static/description/assets/misc/pictures.png new file mode 100644 index 000000000..56d255fe9 Binary files /dev/null and b/web_login_styles/static/description/assets/misc/pictures.png differ diff --git a/web_login_styles/static/description/assets/misc/pie-chart.png b/web_login_styles/static/description/assets/misc/pie-chart.png new file mode 100644 index 000000000..426e05244 Binary files /dev/null and b/web_login_styles/static/description/assets/misc/pie-chart.png differ diff --git a/web_login_styles/static/description/assets/misc/right-arrow.png b/web_login_styles/static/description/assets/misc/right-arrow.png new file mode 100644 index 000000000..730984a06 Binary files /dev/null and b/web_login_styles/static/description/assets/misc/right-arrow.png differ diff --git a/web_login_styles/static/description/assets/misc/star.png b/web_login_styles/static/description/assets/misc/star.png new file mode 100644 index 000000000..2eb9ab29f Binary files /dev/null and b/web_login_styles/static/description/assets/misc/star.png differ diff --git a/web_login_styles/static/description/assets/misc/support.png b/web_login_styles/static/description/assets/misc/support.png new file mode 100644 index 000000000..4f18b8b82 Binary files /dev/null and b/web_login_styles/static/description/assets/misc/support.png differ diff --git a/web_login_styles/static/description/assets/misc/whatsapp.png b/web_login_styles/static/description/assets/misc/whatsapp.png new file mode 100644 index 000000000..d513a5356 Binary files /dev/null and b/web_login_styles/static/description/assets/misc/whatsapp.png differ diff --git a/web_login_styles/static/description/assets/modules/1.png b/web_login_styles/static/description/assets/modules/1.png new file mode 100644 index 000000000..5238bdeab Binary files /dev/null and b/web_login_styles/static/description/assets/modules/1.png differ diff --git a/web_login_styles/static/description/assets/modules/2.png b/web_login_styles/static/description/assets/modules/2.png new file mode 100644 index 000000000..1ae7cfe3b Binary files /dev/null and b/web_login_styles/static/description/assets/modules/2.png differ diff --git a/web_login_styles/static/description/assets/modules/3.png b/web_login_styles/static/description/assets/modules/3.png new file mode 100644 index 000000000..3c3ff1afb Binary files /dev/null and b/web_login_styles/static/description/assets/modules/3.png differ diff --git a/web_login_styles/static/description/assets/modules/4.png b/web_login_styles/static/description/assets/modules/4.png new file mode 100644 index 000000000..3fae4631e Binary files /dev/null and b/web_login_styles/static/description/assets/modules/4.png differ diff --git a/web_login_styles/static/description/assets/modules/5.gif b/web_login_styles/static/description/assets/modules/5.gif new file mode 100644 index 000000000..2a5f8e659 Binary files /dev/null and b/web_login_styles/static/description/assets/modules/5.gif differ diff --git a/web_login_styles/static/description/assets/modules/6.png b/web_login_styles/static/description/assets/modules/6.png new file mode 100644 index 000000000..7f2815273 Binary files /dev/null and b/web_login_styles/static/description/assets/modules/6.png differ diff --git a/web_login_styles/static/description/assets/screenshots/1.png b/web_login_styles/static/description/assets/screenshots/1.png new file mode 100644 index 000000000..6923d21ba Binary files /dev/null and b/web_login_styles/static/description/assets/screenshots/1.png differ diff --git a/web_login_styles/static/description/assets/screenshots/10.png b/web_login_styles/static/description/assets/screenshots/10.png new file mode 100644 index 000000000..8df49ef2a Binary files /dev/null and b/web_login_styles/static/description/assets/screenshots/10.png differ diff --git a/web_login_styles/static/description/assets/screenshots/11.png b/web_login_styles/static/description/assets/screenshots/11.png new file mode 100644 index 000000000..7d5f8e412 Binary files /dev/null and b/web_login_styles/static/description/assets/screenshots/11.png differ diff --git a/web_login_styles/static/description/assets/screenshots/12.png b/web_login_styles/static/description/assets/screenshots/12.png new file mode 100644 index 000000000..faca4f84e Binary files /dev/null and b/web_login_styles/static/description/assets/screenshots/12.png differ diff --git a/web_login_styles/static/description/assets/screenshots/2.png b/web_login_styles/static/description/assets/screenshots/2.png new file mode 100644 index 000000000..c50646e38 Binary files /dev/null and b/web_login_styles/static/description/assets/screenshots/2.png differ diff --git a/web_login_styles/static/description/assets/screenshots/3.png b/web_login_styles/static/description/assets/screenshots/3.png new file mode 100644 index 000000000..aa6de06b7 Binary files /dev/null and b/web_login_styles/static/description/assets/screenshots/3.png differ diff --git a/web_login_styles/static/description/assets/screenshots/4.png b/web_login_styles/static/description/assets/screenshots/4.png new file mode 100644 index 000000000..2167eed73 Binary files /dev/null and b/web_login_styles/static/description/assets/screenshots/4.png differ diff --git a/web_login_styles/static/description/assets/screenshots/5.png b/web_login_styles/static/description/assets/screenshots/5.png new file mode 100644 index 000000000..5b8a7bbf4 Binary files /dev/null and b/web_login_styles/static/description/assets/screenshots/5.png differ diff --git a/web_login_styles/static/description/assets/screenshots/6.png b/web_login_styles/static/description/assets/screenshots/6.png new file mode 100644 index 000000000..26918f06d Binary files /dev/null and b/web_login_styles/static/description/assets/screenshots/6.png differ diff --git a/web_login_styles/static/description/assets/screenshots/7.png b/web_login_styles/static/description/assets/screenshots/7.png new file mode 100644 index 000000000..6ded5f4ef Binary files /dev/null and b/web_login_styles/static/description/assets/screenshots/7.png differ diff --git a/web_login_styles/static/description/assets/screenshots/8.png b/web_login_styles/static/description/assets/screenshots/8.png new file mode 100644 index 000000000..668642827 Binary files /dev/null and b/web_login_styles/static/description/assets/screenshots/8.png differ diff --git a/web_login_styles/static/description/assets/screenshots/9.png b/web_login_styles/static/description/assets/screenshots/9.png new file mode 100644 index 000000000..fa702e36e Binary files /dev/null and b/web_login_styles/static/description/assets/screenshots/9.png differ diff --git a/web_login_styles/static/description/assets/screenshots/hero.gif b/web_login_styles/static/description/assets/screenshots/hero.gif new file mode 100644 index 000000000..0c232e50d Binary files /dev/null and b/web_login_styles/static/description/assets/screenshots/hero.gif differ diff --git a/web_login_styles/static/description/banner.png b/web_login_styles/static/description/banner.png new file mode 100644 index 000000000..d2f60e3d3 Binary files /dev/null and b/web_login_styles/static/description/banner.png differ diff --git a/web_login_styles/static/description/icon.png b/web_login_styles/static/description/icon.png new file mode 100644 index 000000000..316237633 Binary files /dev/null and b/web_login_styles/static/description/icon.png differ diff --git a/web_login_styles/static/description/index.html b/web_login_styles/static/description/index.html new file mode 100644 index 000000000..1e4b2d624 --- /dev/null +++ b/web_login_styles/static/description/index.html @@ -0,0 +1,606 @@ +
+ +
+ +
+
+ Community +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+ +
+
+
+ +

+ All In One Dynamic Fields

+

Create custom fields in as per you need without any coding.

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

Explore This + Module

+
+ + + + +
+
+ +
+

Overview +

+
+
+
+ In the Login Styles App, We can configure the login page according to our needs. + For this a new option is added in the general settings and based on that we can change the alignment of login to the left, right, middle or default. + Also, there is an option to change the background of login page as image, url or color. +
+
+ + + +
+
+ +
+

Features +

+
+
+
+
+ + Change the alignment of the login to right, left, middle or default. +
+
+ + Set up background with color, image, or url of the images. +
+
+ + Can keep the login page as default if there's no need of any customizations. +
+
+
+ + +
+
+ + + +
+
+ +
+

Screenshots +

+
+
+
+ +
+

Login Page Set-Up +

+

From the general settings one can find the login page set up to configure the login option. The alignments can be set to right, left, middle or as default.

+ +
+ +
+

Alignment of the Login +

+

Login Layout set to default.

+ +
+ +
+ + +

Login Layout set to Left.

+ +
+ +
+ + +

Login Layout set to Middle(it can include the changes in background).

+ +
+ +
+ + +

Login Layout set to Right.

+ +
+ +
+

Background Styles +

+

There are 3 types of background setup. They are color picker, image, URL.

+ +
+ +
+

Set a colour for the background. +

+

Using Color picker we can select the color as we need to set up as the background of login page.

+ +
+ +
+ + +

After selecting the color the login page automatically set up its background to the color we selected.

+ +
+ +
+

Set a background Image +

+

Using Image option we can select the background image from our system.

+ +
+ +
+ + +

Then the selected image will be set as the background image of the login layout

+ +
+ +
+

Set a background Image as url in the set up +

+

Using the 'URL' option we can set the background image from the mentioned url of the image.

+ +
+ +
+ + +

Then the image from the url will be set as the background image of the login layout

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

Related + Products +

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

Our Services +

+
+ +
+
+
+
+ +
+
+ Odoo + Customization
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Support
+
+ + +
+
+ +
+
+ Hire + Odoo + Developer
+
+ +
+
+ +
+
+ Odoo + Integration
+
+ +
+
+ +
+
+ Odoo + Migration
+
+ + +
+
+ +
+
+ Odoo + Consultancy
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Licensing Consultancy
+
+
+ +
+ + + + + +
+
+ +
+

Our + Industries +

+
+ +
+
+
+
+ +
+ Trading +
+

+ Easily procure + and + sell your products

+
+
+ +
+
+ +
+ POS +
+

+ Easy + configuration + and convivial experience

+
+
+ +
+
+ +
+ Education +
+

+ A platform for + educational management

+
+
+ +
+
+ +
+ Manufacturing +
+

+ Plan, track and + schedule your operations

+
+
+ +
+
+ +
+ E-commerce & Website +
+

+ Mobile + friendly, + awe-inspiring product pages

+
+
+ +
+
+ +
+ Service Management +
+

+ Keep track of + services and invoice

+
+
+ +
+
+ +
+ Restaurant +
+

+ Run your bar or + restaurant methodically

+
+
+ +
+
+ +
+ Hotel Management +
+

+ An + all-inclusive + hotel management application

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

Support +

+
+
+
+
+
+
+ +
+
+

Need Help?

+

Got questions or need help? Get in touch.

+ +

+ odoo@cybrosys.com

+
+
+
+
+
+
+
+ +
+
+

WhatsApp

+

Say hi to us on WhatsApp!

+ +

+91 86068 + 27707

+
+
+
+
+
+
+
+ +
+
+
+ \ No newline at end of file diff --git a/web_login_styles/views/res_config_settings_views.xml b/web_login_styles/views/res_config_settings_views.xml new file mode 100644 index 000000000..cd6240540 --- /dev/null +++ b/web_login_styles/views/res_config_settings_views.xml @@ -0,0 +1,43 @@ + + + + + + res.config.settings.form + res.config.settings + + + + +