├── .coveragerc ├── .github ├── CODEOWNERS ├── PULL_REQUEST_TEMPLATE.md ├── release-drafter.yml └── workflows │ ├── ci.yml │ ├── publish.yml │ └── release-drafter.yml ├── .gitignore ├── .isort.cfg ├── .travis.yml ├── AUTHORS ├── CHANGELOG.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── VERSION ├── chartjs ├── __init__.py ├── colors.py ├── models.py ├── static │ ├── css │ │ └── Chart.min.css │ └── js │ │ ├── Chart.min.js │ │ ├── excanvas.js │ │ └── highcharts │ │ ├── .fr-amL3gv │ │ ├── annotations.js │ │ ├── annotations.src.js │ │ ├── canvas-tools.js │ │ ├── canvas-tools.src.js │ │ ├── dark-blue.js │ │ ├── dark-green.js │ │ ├── data.js │ │ ├── data.src.js │ │ ├── exporting.js │ │ ├── exporting.src.js │ │ ├── funnel.js │ │ ├── funnel.src.js │ │ ├── gray.js │ │ ├── grid.js │ │ ├── highcharts-more.js │ │ ├── highcharts-more.src.js │ │ ├── highcharts.js │ │ ├── highcharts.src.js │ │ └── skies.js │ │ ├── highcharts-more.js │ │ ├── highcharts-more.src.js │ │ ├── highcharts.js │ │ ├── highcharts.src.js │ │ ├── modules │ │ ├── annotations.js │ │ ├── annotations.src.js │ │ ├── canvas-tools.js │ │ ├── canvas-tools.src.js │ │ ├── data.js │ │ ├── data.src.js │ │ ├── exporting.js │ │ ├── exporting.src.js │ │ ├── funnel.js │ │ └── funnel.src.js │ │ └── themes │ │ ├── dark-blue.js │ │ ├── dark-green.js │ │ ├── gray.js │ │ ├── grid.js │ │ └── skies.js ├── util.py └── views │ ├── __init__.py │ ├── base.py │ ├── columns.py │ ├── lines.py │ └── pie.py ├── demo ├── README.rst ├── demoproject │ ├── __init__.py │ ├── fixtures │ │ └── meter-readings.json │ ├── manage.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── settings.py │ ├── templates │ │ ├── base.html │ │ ├── colors.html │ │ ├── home.html │ │ └── line_chart.html │ ├── tests.py │ ├── urls.py │ ├── views.py │ └── wsgi.py └── setup.py ├── docs ├── Makefile ├── _static │ └── django-chartjs.png ├── about │ ├── authors.txt │ ├── changelog.txt │ ├── index.txt │ └── license.txt ├── conf.py ├── demo.txt ├── dev.txt ├── index.txt └── install.txt ├── setup.py ├── test-requirements.pip ├── tox.ini ├── update-Chart.js.sh └── var └── .gitkeep /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = chartjs 3 | omit = *settings.py*,*manage.py* 4 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @peopledoc/python-community @natim 2 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Closes # 2 | 3 | ### Checklist: 4 | 5 | - [ ] Tests 6 | - [ ] (not applicable?) 7 | - [ ] Documentation 8 | - [ ] (not applicable?) 9 | 10 | 12 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: '$NEXT_MINOR_VERSION' 2 | tag-template: '$NEXT_MINOR_VERSION' 3 | template: | 4 | $CHANGES 5 | 6 | ## Kudos: 7 | 8 | $CONTRIBUTORS 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - 'master' 8 | tags: 9 | - '*' 10 | 11 | jobs: 12 | build: 13 | 14 | strategy: 15 | matrix: 16 | include: 17 | 18 | - name: Python 3.6 / Django 1.10 19 | python_version: "3.6" 20 | tox_env: py36-django110 21 | 22 | - name: Python 3.7 / Django 1.10 23 | python_version: "3.7" 24 | tox_env: py37-django110 25 | 26 | - name: Python 3.7 / Django 2.2 27 | python_version: "3.7" 28 | tox_env: py37-django22 29 | 30 | - name: Python 3.9 / Django 2.2 31 | python_version: "3.9" 32 | tox_env: py39-django22 33 | 34 | - name: Python 3.9 / Django 3.0 35 | python_version: "3.9" 36 | tox_env: py39-django30 37 | 38 | - name: Python 3.9 / Django 3.1 39 | python_version: "3.9" 40 | tox_env: py39-django31 41 | 42 | - name: Python 3.7 / Django 3.2 43 | python_version: "3.7" 44 | tox_env: py37-django32 45 | 46 | - name: Python 3.8 / Django 3.2 47 | python_version: "3.8" 48 | tox_env: py38-django32 49 | 50 | - name: Python 3.9 / Django 3.2 51 | python_version: "3.9" 52 | tox_env: py39-django32 53 | 54 | - name: Python 3.9 / Django 4.0 55 | python_version: "3.9" 56 | tox_env: py39-django40 57 | 58 | - name: Flake8 59 | python_version: "3" 60 | tox_env: flake8 61 | 62 | name: "${{ matrix.name }}" 63 | runs-on: ubuntu-latest 64 | 65 | steps: 66 | - uses: actions/checkout@v2 67 | 68 | - name: Set up Python 69 | id: setup-python 70 | uses: actions/setup-python@v2 71 | with: 72 | python-version: ${{ matrix.python_version }} 73 | 74 | - name: Pip cache 75 | uses: actions/cache@v2 76 | with: 77 | path: | 78 | ~/.cache/ 79 | key: ${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('setup.cfg') }} 80 | 81 | - name: Install Tox 82 | run: pip install tox 83 | 84 | - name: Run ${{ matrix.name }} 85 | run: tox 86 | env: 87 | TOXENV: "${{ matrix.tox_env }}" 88 | 89 | report-status: 90 | name: success 91 | runs-on: ubuntu-latest 92 | needs: build 93 | steps: 94 | 95 | - name: Report success 96 | run: echo 'Success !' 97 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | deploy: 10 | name: Publish package to PyPI 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Set up Python 16 | id: setup-python 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: ${{ matrix.python_version }} 20 | 21 | - name: Install 22 | run: pip install build 23 | 24 | - name: Build package 25 | run: python -m build 26 | 27 | - name: Wait for tests to succeed 28 | uses: fountainhead/action-wait-for-check@v1.0.0 29 | id: wait-for-ci 30 | with: 31 | token: ${{ secrets.GITHUB_TOKEN }} 32 | checkName: success 33 | 34 | - name: Exit if CI did not succeed 35 | if: steps.wait-for-ci.outputs.conclusion != 'success' 36 | run: exit 1 37 | 38 | - name: Publish a Python distribution to PyPI 39 | uses: pypa/gh-action-pypi-publish@release/v1 40 | with: 41 | user: __token__ 42 | password: "${{ secrets.PYPI_TOKEN }}" 43 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | update_release_draft: 10 | runs-on: ubuntu-latest 11 | steps: 12 | # Drafts the next Release notes as Pull Requests are merged into "master" 13 | - uses: release-drafter/release-drafter@v5 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *~ 3 | *.egg-info/ 4 | var/ 5 | .coverage 6 | /bin/ 7 | /lib/ 8 | /include/ 9 | /build/ 10 | /local/ 11 | /.tox/ 12 | /.venv/ 13 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | # # Needed for black compatibility 3 | multi_line_output=3 4 | include_trailing_comma=True 5 | force_grid_wrap=0 6 | line_length=88 7 | combine_as_imports=True 8 | 9 | # If set, imports will be sorted within their section independent to the import_type. 10 | force_sort_within_sections=True 11 | 12 | # List sections with django and 13 | known_django=django 14 | known_chartjs=chartjs 15 | 16 | sections=FUTURE,STDLIB,DJANGO,CHARTJS,THIRDPARTY,FIRSTPARTY,LOCALFOLDER 17 | 18 | # skip 19 | skip_glob=.local 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | dist: bionic 3 | python: 4 | - "3.6" 5 | - "3.7" 6 | - "3.8" 7 | before_install: 8 | - pip install -U pip setuptools 9 | - pip install -U tox tox-travis 10 | install: 11 | - pip install -r test-requirements.pip 12 | script: 13 | - tox 14 | after_success: 15 | # Report coverage results to coveralls.io 16 | - pip install coveralls 17 | - coveralls 18 | matrix: 19 | fast_finish: true 20 | include: 21 | - python: 3.8 22 | env: TOXENV=flake8 23 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | ######################## 2 | Authors and contributors 3 | ######################## 4 | 5 | * Adrien Panhaleux 6 | * aRkadeFR 7 | * Benoît Bryon 8 | * Bruno Bord 9 | * Christian Holler 10 | * Connor Johnson 11 | * dvanderpost 12 | * Edras Pacola 13 | * John Franey 14 | * Kyle Umstatter 15 | * Loren M. Lang 16 | * Matthew Beauregard 17 | * mab 18 | * Mickaël Guérin 19 | * Rémy HUBSCHER 20 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | 2.4.0 (unreleased) 5 | ------------------ 6 | 7 | - Nothing changed yet. 8 | 9 | 2.3.0 (2022-02-07) 10 | ------------------ 11 | 12 | - Remove CI test because Nose is unmaintained and no longer works with Python 3.10. 13 | - Update CI tox envs 14 | - Add github actions 15 | - Rename tox.ini envs 16 | - Django 3 compatibility 17 | - Update tox.ini 18 | - Fix doc filename 19 | 20 | 2.2.1 (2020-07-15) 21 | ------------------ 22 | 23 | - Update to Chart.js 2.9.3 24 | 25 | 26 | 2.1.0 (2020-05-06) 27 | ------------------ 28 | 29 | - Add get_dataset_options method. 30 | 31 | 32 | 2.0.0 (2020-01-17) 33 | ------------------ 34 | 35 | - Drop support for Python 2.7 36 | - Add support for Django 1.10, 2.2 and 3.0 and remove older version support. 37 | - Add black and isort configs. 38 | 39 | 40 | 1.5.0 (2019-07-08) 41 | ------------------ 42 | 43 | - Handle a new ClassBased view with LineChartJS options. (#45) 44 | 45 | 46 | 1.4.0 (2019-06-07) 47 | ------------------ 48 | 49 | - Add utilities to generate and graph discontinuous datestamped data. (#43) 50 | 51 | 52 | 1.3 (unreleased) 53 | ---------------- 54 | 55 | - Allow Chart Classes inheritance. (#37) 56 | 57 | 58 | 1.2 (2017-05-24) 59 | ---------------- 60 | 61 | - Upgrade to Chart.js v.2.5.0 (#28) 62 | 63 | 64 | 1.1 (2016-10-07) 65 | ---------------- 66 | 67 | - Add python 3.4/3.5 support #23 68 | - Update README.rst #20 69 | - Multiple get_data() Calls from API #16 70 | 71 | 72 | 1.0 (2013-12-12) 73 | ---------------- 74 | 75 | - Add documentation 76 | - Add Python 3 support 77 | - Add an option to change / or remove credits 78 | 79 | 80 | 0.3 (2013-10-14) 81 | ---------------- 82 | 83 | - Add Py3 Support 84 | - Add Highchart Pie, Donut and lines 85 | - Add test 86 | 87 | 88 | 0.2 (2013-07-26) 89 | ---------------- 90 | 91 | - Only display integer 92 | - Add highcharts Columns chart 93 | 94 | 0.1 (2013-06-12) 95 | ---------------- 96 | 97 | - Add providers support. 98 | - Add support for IE < 8. 99 | - Add an example to the README. 100 | - Adding the BaseLineChart view as well as an example of how to use it.. 101 | - Add a demo page for next_color. 102 | - Init django-chartjs project. 103 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ####### 2 | License 3 | ####### 4 | 5 | Copyright (c) 2013 Rémy HUBSCHER 6 | 7 | All rights reserved. 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are 10 | met: 11 | 12 | * Redistributions of source code must retain the above copyright 13 | notice, this list of conditions and the following disclaimer. 14 | 15 | * Redistributions in binary form must reproduce the above copyright 16 | notice, this list of conditions and the following disclaimer in the 17 | documentation and/or other materials provided with the distribution. 18 | 19 | * Neither the name of the copyright holder nor the names of its 20 | contributors may be used to endorse or promote products derived from 21 | this software without specific prior written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include chartjs * 2 | global-exclude *.pyc 3 | include *.txt 4 | include VERSION README.rst CHANGELOG.rst LICENSE AUTHORS 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VIRTUALENV = virtualenv --python python3 2 | VENV := $(shell echo $${VIRTUAL_ENV-.venv}) 3 | PYTHON = $(VENV)/bin/python 4 | TOX = $(VENV)/bin/tox 5 | DEV_STAMP = $(VENV)/.dev_env_installed.stamp 6 | INSTALL_STAMP = $(VENV)/.install.stamp 7 | 8 | .PHONY: docs test clean tox install-dev virtualenv 9 | all: install 10 | 11 | virtualenv: $(PYTHON) 12 | $(PYTHON): 13 | $(VIRTUALENV) $(VENV) 14 | 15 | tox: $(TOX) 16 | $(TOX): virtualenv 17 | $(VENV)/bin/pip install tox 18 | 19 | install: $(INSTALL_STAMP) 20 | $(INSTALL_STAMP): $(PYTHON) setup.py 21 | $(VENV)/bin/pip install -U pip 22 | $(VENV)/bin/pip install -Ue . 23 | touch $(INSTALL_STAMP) 24 | 25 | install-dev: $(DEV_STAMP) 26 | $(DEV_STAMP): $(INSTALL_STAMP) 27 | $(VENV)/bin/pip install -r test-requirements.pip 28 | $(VENV)/bin/pip install -e ./ 29 | $(VENV)/bin/pip install -e demo/ 30 | touch $(DEV_STAMP) 31 | 32 | test: install-dev 33 | $(VENV)/bin/coverage run --branch --source=chartjs demo test demoproject 34 | 35 | clean: 36 | rm -rf bin .tox include/ lib/ man/ django_chartjs.egg-info/ build/ 37 | 38 | docs: 39 | (cd docs; make html) 40 | 41 | .PHONY: demo 42 | demo: install-dev 43 | DJANGO_SETTINGS_MODULE=demoproject.settings $(PYTHON) demo/demoproject/manage.py runserver 44 | 45 | demo-with-fixtures: install-dev 46 | DJANGO_SETTINGS_MODULE=demoproject.settings $(PYTHON) demo/demoproject/manage.py makemigrations demoproject 47 | DJANGO_SETTINGS_MODULE=demoproject.settings $(PYTHON) demo/demoproject/manage.py migrate 48 | DJANGO_SETTINGS_MODULE=demoproject.settings $(PYTHON) demo/demoproject/manage.py loaddata demo/demoproject/fixtures/meter-readings.json 49 | DJANGO_SETTINGS_MODULE=demoproject.settings $(PYTHON) demo/demoproject/manage.py runserver 50 | 51 | demo-test: install-dev 52 | DJANGO_SETTINGS_MODULE=demoproject.settings $(PYTHON) demo/demoproject/manage.py test demoproject --verbose 53 | 54 | .PHONY: black 55 | black: install-dev 56 | $(VENV)/bin/black chartjs demo 57 | 58 | .PHONY: isort 59 | isort: install-dev 60 | $(VENV)/bin/isort -rc .isort.cfg chartjs demo 61 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Django Chartjs 2 | ############## 3 | 4 | Django Chartjs lets you manage charts in your Django application. 5 | 6 | .. image:: https://travis-ci.org/peopledoc/django-chartjs.svg?branch=master 7 | :target: https://travis-ci.org/peopledoc/django-chartjs 8 | .. image:: https://coveralls.io/repos/peopledoc/django-chartjs/badge.png?branch=master&style=flat 9 | :target: https://coveralls.io/r/peopledoc/django-chartjs?branch=master 10 | .. image:: https://img.shields.io/pypi/v/django-chartjs.svg 11 | :target: https://pypi.python.org/pypi/django-chartjs/ 12 | 13 | 14 | This is compatible with Chart.js and Highcharts JS libraries. 15 | 16 | Using a set of predefined Class Based Views you are able to get 17 | started after writing just your SQL query. 18 | 19 | * Authors: Rémy Hubscher and `contributors 20 | `_ 21 | * Licence: BSD 22 | * Compatibility: Django 1.10, 2.2 and 3.0, python3.6 up to python3.8 23 | * Project URL: https://github.com/peopledoc/django-chartjs 24 | 25 | 26 | Getting Started 27 | =============== 28 | 29 | Install django-chartjs:: 30 | 31 | pip install django-chartjs 32 | 33 | 34 | Add it to your ``INSTALLED_APPS`` settings:: 35 | 36 | INSTALLED_APPS = ( 37 | '...', 38 | 'chartjs', 39 | ) 40 | 41 | 42 | Using it 43 | ======== 44 | 45 | A simple Line Chart example. 46 | 47 | 1. Create the HTML file 48 | +++++++++++++++++++++++ 49 | 50 | .. code-block:: html 51 | 52 | {% load static %} 53 | 54 | 55 | django-chartjs line chart demo 56 | 59 | 60 | 61 |

Some Line Charts loaded in Ajax!

62 | 63 | 64 | 65 | 66 | 67 | 75 | 76 | 77 | 78 | 79 | 2. Create the view with labels and data definition 80 | ++++++++++++++++++++++++++++++++++++++++++++++++++ 81 | 82 | .. code-block:: python 83 | 84 | from django.views.generic import TemplateView 85 | from chartjs.views.lines import BaseLineChartView 86 | 87 | 88 | class LineChartJSONView(BaseLineChartView): 89 | def get_labels(self): 90 | """Return 7 labels for the x-axis.""" 91 | return ["January", "February", "March", "April", "May", "June", "July"] 92 | 93 | def get_providers(self): 94 | """Return names of datasets.""" 95 | return ["Central", "Eastside", "Westside"] 96 | 97 | def get_data(self): 98 | """Return 3 datasets to plot.""" 99 | 100 | return [[75, 44, 92, 11, 44, 95, 35], 101 | [41, 92, 18, 3, 73, 87, 92], 102 | [87, 21, 94, 3, 90, 13, 65]] 103 | 104 | 105 | line_chart = TemplateView.as_view(template_name='line_chart.html') 106 | line_chart_json = LineChartJSONView.as_view() 107 | 108 | 109 | 3. Update urls.py with the new urls for TemplateView and AJAX call 'line_chart_json' as in chart.html 110 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 111 | 112 | .. code-block:: python 113 | 114 | from .views import line_chart, line_chart_json 115 | 116 | urlpatterns = [ 117 | '...', 118 | path('chart', line_chart, name='line_chart'), 119 | path('chartJSON', line_chart_json, name='line_chart_json'), 120 | ] 121 | 122 | 123 | 4. Get a Chart.js Line Chart 124 | ++++++++++++++++++++++++++++ 125 | 126 | .. image:: https://raw.github.com/peopledoc/django-chartjs/master/docs/_static/django-chartjs.png 127 | 128 | 129 | It is that simple! 130 | 131 | For other examples including a HighCharts line chart, don't hesitate to look at the demo project. 132 | 133 | Also, feel free to contribute your demo! 134 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 2.4.0.dev0 2 | -------------------------------------------------------------------------------- /chartjs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peopledoc/django-chartjs/e3c72c948fe8a905a5ee696c8cca9217941058a6/chartjs/__init__.py -------------------------------------------------------------------------------- /chartjs/colors.py: -------------------------------------------------------------------------------- 1 | COLORS = [ 2 | (202, 201, 197), # Light gray 3 | (171, 9, 0), # Red 4 | (166, 78, 46), # Light orange 5 | (255, 190, 67), # Yellow 6 | (163, 191, 63), # Light green 7 | (122, 159, 191), # Light blue 8 | (140, 5, 84), # Pink 9 | (166, 133, 93), # Light brown 10 | (75, 64, 191), # Red blue 11 | (237, 124, 60), # orange 12 | ] 13 | 14 | 15 | def next_color(color_list=COLORS): 16 | """Create a different color from a base color list. 17 | 18 | >>> color_list = ( 19 | ... (122, 159, 191), # Light blue 20 | ... (202, 201, 197), # Light gray, 21 | ... ) 22 | >>> g = next_color(color_list) 23 | >>> next(g) 24 | [122, 159, 191] 25 | >>> next(g) 26 | [202, 201, 197] 27 | >>> next(g) 28 | [63, 100, 132] 29 | >>> next(g) 30 | [143, 142, 138] 31 | >>> next(g) 32 | [4, 41, 73] 33 | >>> next(g) 34 | [84, 83, 79] 35 | """ 36 | step = 0 37 | while True: 38 | for color in color_list: 39 | yield list(map(lambda base: (base + step) % 256, color)) 40 | step += 197 41 | -------------------------------------------------------------------------------- /chartjs/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peopledoc/django-chartjs/e3c72c948fe8a905a5ee696c8cca9217941058a6/chartjs/models.py -------------------------------------------------------------------------------- /chartjs/static/css/Chart.min.css: -------------------------------------------------------------------------------- 1 | @keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0} -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/.fr-amL3gv/annotations.js: -------------------------------------------------------------------------------- 1 | (function(f,A){function k(a){return typeof a==="number"}function l(a){return a!==B&&a!==null}var B,n,o,w=f.Chart,r=f.extend,x=f.each,y;y={xAxis:0,yAxis:0,title:{style:{},text:"",x:0,y:0},shape:{params:{stroke:"#000000",fill:"transparent",strokeWidth:2}}};o=["path","rect","circle"];n={top:0,left:0,center:0.5,middle:0.5,bottom:1,right:1};var s=A.inArray,C=f.merge,z=function(){this.init.apply(this,arguments)};z.prototype={init:function(a,d){this.chart=a;this.options=C({},y,d)},render:function(a){var d= 2 | this.chart,c=this.chart.renderer,b=this.group,m=this.title,e=this.shape,g=this.options,f=g.title,i=g.shape;if(!b)b=this.group=c.g();if(!m&&f)m=this.title=c.label(f),m.add(b);if(!e&&i&&s(i.type,o)!==-1)e=this.shape=c[g.shape.type](i.params),e.add(b);b.add(d.annotations.group);this.linkObjects();a!==!1&&this.redraw()},redraw:function(){var a=this.options,d=this.chart,c=this.group,b=this.title,m=this.shape,e=this.linkedObject,g=d.xAxis[a.xAxis],o=d.yAxis[a.yAxis],i=a.width,t=a.height,u=n[a.anchorY], 3 | v=n[a.anchorX],h,q,j,p;if(e)h=e instanceof f.Point?"point":e instanceof f.Series?"series":null,h==="point"?(a.xValue=e.x,a.yValue=e.y,q=e.series):h==="series"&&(q=e),c.visibility!==q.group.visibility&&c.attr({visibility:q.group.visibility});e=(l(a.xValue)?g.toPixels(a.xValue+g.minPointOffset):a.x)-g.minPixelPadding;h=l(a.yValue)?o.toPixels(a.yValue):a.y;if(!isNaN(e)&&!isNaN(h)&&k(e)&&k(h)){b&&(b.attr(a.title),b.css(a.title.style));if(m){b=r({},a.shape.params);if(a.units==="values"){for(j in b)s(j, 4 | ["width","x"])>-1?b[j]=g.translate(b[j]):s(j,["height","y"])>-1&&(b[j]=o.translate(b[j]));b.width&&(b.width-=g.toPixels(0)-g.left);b.x&&(b.x+=g.minPixelPadding)}m.attr(b)}c.bBox=null;if(!k(i))p=c.getBBox(),i=p.width;if(!k(t))p||(p=c.getBBox()),t=p.height;if(!k(v))v=n.center;if(!k(u))u=n.center;e-=i*v;h-=t*u;d.animation&&l(c.translateX)&&l(c.translateY)?c.animate({translateX:e,translateY:h}):c.translate(e,h)}},destroy:function(){var a=this,d=this.chart.annotations.allItems,c=d.indexOf(a);c>-1&&d.splice(c, 5 | 1);x(["title","shape","group"],function(b){a[b]&&(a[b].destroy(),a[b]=null)});a.group=a.title=a.shape=a.chart=a.options=null},update:function(a,d){r(this.options,a);this.linkObjects();this.render(d)},linkObjects:function(){var a=this.chart,d=this.linkedObject,c=d&&(d.id||d.options.id),b=this.options.linkedTo;if(l(b)){if(!l(d)||b!==c)this.linkedObject=a.get(b)}else this.linkedObject=null}};r(w.prototype,{annotations:{add:function(a,d){var c=this.allItems,b=this.chart,f,e;Object.prototype.toString.call(a)=== 6 | "[object Array]"||(a=[a]);for(e=a.length;e--;)f=new z(b,a[e]),c.push(f),f.render(d)},redraw:function(){x(this.allItems,function(a){a.redraw()})}}});w.prototype.callbacks.push(function(a){var d=a.options.annotations,c;c=a.renderer.g("annotations");c.attr({zIndex:7});c.add();a.annotations.allItems=[];a.annotations.chart=a;a.annotations.group=c;Object.prototype.toString.call(d)==="[object Array]"&&d.length>0&&a.annotations.add(a.options.annotations);f.addEvent(a,"redraw",function(){a.annotations.redraw()})})})(Highcharts, 7 | HighchartsAdapter); 8 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/.fr-amL3gv/annotations.src.js: -------------------------------------------------------------------------------- 1 | (function (Highcharts, HighchartsAdapter) { 2 | 3 | var UNDEFINED, 4 | ALIGN_FACTOR, 5 | ALLOWED_SHAPES, 6 | Chart = Highcharts.Chart, 7 | extend = Highcharts.extend, 8 | each = Highcharts.each, 9 | defaultOptions; 10 | 11 | defaultOptions = { 12 | xAxis: 0, 13 | yAxis: 0, 14 | title: { 15 | style: {}, 16 | text: "", 17 | x: 0, 18 | y: 0 19 | }, 20 | shape: { 21 | params: { 22 | stroke: "#000000", 23 | fill: "transparent", 24 | strokeWidth: 2 25 | } 26 | } 27 | }; 28 | 29 | ALLOWED_SHAPES = ["path", "rect", "circle"]; 30 | 31 | ALIGN_FACTOR = { 32 | top: 0, 33 | left: 0, 34 | center: 0.5, 35 | middle: 0.5, 36 | bottom: 1, 37 | right: 1 38 | }; 39 | 40 | 41 | // Highcharts helper methods 42 | var inArray = HighchartsAdapter.inArray, 43 | merge = Highcharts.merge; 44 | 45 | function isArray(obj) { 46 | return Object.prototype.toString.call(obj) === '[object Array]'; 47 | } 48 | 49 | function isNumber(n) { 50 | return typeof n === 'number'; 51 | } 52 | 53 | function defined(obj) { 54 | return obj !== UNDEFINED && obj !== null; 55 | } 56 | 57 | 58 | // Define annotation prototype 59 | var Annotation = function () { 60 | this.init.apply(this, arguments); 61 | }; 62 | Annotation.prototype = { 63 | /* 64 | * Initialize the annotation 65 | */ 66 | init: function (chart, options) { 67 | this.chart = chart; 68 | this.options = merge({}, defaultOptions, options); 69 | }, 70 | 71 | /* 72 | * Render the annotation 73 | */ 74 | render: function (redraw) { 75 | var annotation = this, 76 | chart = this.chart, 77 | renderer = annotation.chart.renderer, 78 | group = annotation.group, 79 | title = annotation.title, 80 | shape = annotation.shape, 81 | options = annotation.options, 82 | titleOptions = options.title, 83 | shapeOptions = options.shape; 84 | 85 | if (!group) { 86 | group = annotation.group = renderer.g(); 87 | } 88 | 89 | if (!title && titleOptions) { 90 | title = annotation.title = renderer.label(titleOptions); 91 | title.add(group); 92 | } 93 | 94 | if (!shape && shapeOptions && inArray(shapeOptions.type, ALLOWED_SHAPES) !== -1) { 95 | shape = annotation.shape = renderer[options.shape.type](shapeOptions.params); 96 | shape.add(group); 97 | } 98 | 99 | group.add(chart.annotations.group); 100 | 101 | // link annotations to point or series 102 | annotation.linkObjects(); 103 | 104 | if (redraw !== false) { 105 | annotation.redraw(); 106 | } 107 | }, 108 | 109 | /* 110 | * Redraw the annotation title or shape after options update 111 | */ 112 | redraw: function () { 113 | var options = this.options, 114 | chart = this.chart, 115 | group = this.group, 116 | title = this.title, 117 | shape = this.shape, 118 | linkedTo = this.linkedObject, 119 | xAxis = chart.xAxis[options.xAxis], 120 | yAxis = chart.yAxis[options.yAxis], 121 | width = options.width, 122 | height = options.height, 123 | anchorY = ALIGN_FACTOR[options.anchorY], 124 | anchorX = ALIGN_FACTOR[options.anchorX], 125 | resetBBox = false, 126 | shapeParams, 127 | linkType, 128 | series, 129 | param, 130 | bbox, 131 | x, 132 | y; 133 | 134 | if (linkedTo) { 135 | linkType = (linkedTo instanceof Highcharts.Point) ? 'point' : 136 | (linkedTo instanceof Highcharts.Series) ? 'series' : null; 137 | 138 | if (linkType === 'point') { 139 | options.xValue = linkedTo.x; 140 | options.yValue = linkedTo.y; 141 | series = linkedTo.series; 142 | } else if (linkType === 'series') { 143 | series = linkedTo; 144 | } 145 | 146 | if (group.visibility !== series.group.visibility) { 147 | group.attr({ 148 | visibility: series.group.visibility 149 | }); 150 | } 151 | } 152 | 153 | 154 | // Based on given options find annotation pixel position 155 | x = (defined(options.xValue) ? xAxis.toPixels(options.xValue + xAxis.minPointOffset) : options.x) - xAxis.minPixelPadding; 156 | y = defined(options.yValue) ? yAxis.toPixels(options.yValue) : options.y; 157 | 158 | 159 | if (isNaN(x) || isNaN(y) || !isNumber(x) || !isNumber(y)) { 160 | return; 161 | } 162 | 163 | 164 | if (title) { 165 | title.attr(options.title); 166 | title.css(options.title.style); 167 | resetBBox = true; 168 | } 169 | 170 | if (shape) { 171 | shapeParams = extend({}, options.shape.params); 172 | 173 | if (options.units === 'values') { 174 | for (param in shapeParams) { 175 | if (inArray(param, ['width', 'x']) > -1) { 176 | shapeParams[param] = xAxis.translate(shapeParams[param]); 177 | } else if (inArray(param, ['height', 'y']) > -1) { 178 | shapeParams[param] = yAxis.translate(shapeParams[param]); 179 | } 180 | } 181 | 182 | if (shapeParams.width) { 183 | shapeParams.width -= xAxis.toPixels(0) - xAxis.left; 184 | } 185 | 186 | if (shapeParams.x) { 187 | shapeParams.x += xAxis.minPixelPadding; 188 | } 189 | 190 | } 191 | 192 | resetBBox = true; 193 | shape.attr(shapeParams); 194 | } 195 | 196 | group.bBox = null; 197 | 198 | // If annotation width or height is not defined in options use bounding box size 199 | if (!isNumber(width)) { 200 | bbox = group.getBBox(); 201 | width = bbox.width; 202 | } 203 | 204 | if (!isNumber(height)) { 205 | // get bbox only if it wasn't set before 206 | if (!bbox) { 207 | bbox = group.getBBox(); 208 | } 209 | 210 | height = bbox.height; 211 | } 212 | 213 | // Calculate anchor point 214 | if (!isNumber(anchorX)) { 215 | anchorX = ALIGN_FACTOR.center; 216 | } 217 | 218 | if (!isNumber(anchorY)) { 219 | anchorY = ALIGN_FACTOR.center; 220 | } 221 | 222 | // Translate group according to its dimension and anchor point 223 | x = x - width * anchorX; 224 | y = y - height * anchorY; 225 | 226 | if (chart.animation && defined(group.translateX) && defined(group.translateY)) { 227 | group.animate({ 228 | translateX: x, 229 | translateY: y 230 | }); 231 | } else { 232 | group.translate(x, y); 233 | } 234 | }, 235 | 236 | /* 237 | * Destroy the annotation 238 | */ 239 | destroy: function () { 240 | var annotation = this, 241 | chart = this.chart, 242 | allItems = chart.annotations.allItems, 243 | index = allItems.indexOf(annotation); 244 | 245 | if (index > -1) { 246 | allItems.splice(index, 1); 247 | } 248 | 249 | each(['title', 'shape', 'group'], function (element) { 250 | if (annotation[element]) { 251 | annotation[element].destroy(); 252 | annotation[element] = null; 253 | } 254 | }); 255 | 256 | annotation.group = annotation.title = annotation.shape = annotation.chart = annotation.options = null; 257 | }, 258 | 259 | /* 260 | * Update the annotation with a given options 261 | */ 262 | update: function (options, redraw) { 263 | extend(this.options, options); 264 | 265 | // update link to point or series 266 | this.linkObjects(); 267 | 268 | this.render(redraw); 269 | }, 270 | 271 | linkObjects: function () { 272 | var annotation = this, 273 | chart = annotation.chart, 274 | linkedTo = annotation.linkedObject, 275 | linkedId = linkedTo && (linkedTo.id || linkedTo.options.id), 276 | options = annotation.options, 277 | id = options.linkedTo; 278 | 279 | if (!defined(id)) { 280 | annotation.linkedObject = null; 281 | } else if (!defined(linkedTo) || id !== linkedId) { 282 | annotation.linkedObject = chart.get(id); 283 | } 284 | } 285 | }; 286 | 287 | 288 | // Add annotations methods to chart prototype 289 | extend(Chart.prototype, { 290 | annotations: { 291 | /* 292 | * Unified method for adding annotations to the chart 293 | */ 294 | add: function (options, redraw) { 295 | var annotations = this.allItems, 296 | chart = this.chart, 297 | item, 298 | len; 299 | 300 | if (!isArray(options)) { 301 | options = [options]; 302 | } 303 | 304 | len = options.length; 305 | 306 | while (len--) { 307 | item = new Annotation(chart, options[len]); 308 | annotations.push(item); 309 | item.render(redraw); 310 | } 311 | }, 312 | 313 | /** 314 | * Redraw all annotations, method used in chart events 315 | */ 316 | redraw: function () { 317 | each(this.allItems, function (annotation) { 318 | annotation.redraw(); 319 | }); 320 | } 321 | } 322 | }); 323 | 324 | 325 | // Initialize on chart load 326 | Chart.prototype.callbacks.push(function (chart) { 327 | var options = chart.options.annotations, 328 | group; 329 | 330 | group = chart.renderer.g("annotations"); 331 | group.attr({ 332 | zIndex: 7 333 | }); 334 | group.add(); 335 | 336 | // initialize empty array for annotations 337 | chart.annotations.allItems = []; 338 | 339 | // link chart object to annotations 340 | chart.annotations.chart = chart; 341 | 342 | // link annotations group element to the chart 343 | chart.annotations.group = group; 344 | 345 | if (isArray(options) && options.length > 0) { 346 | chart.annotations.add(chart.options.annotations); 347 | } 348 | 349 | // update annotations after chart redraw 350 | Highcharts.addEvent(chart, 'redraw', function () { 351 | chart.annotations.redraw(); 352 | }); 353 | }); 354 | }(Highcharts, HighchartsAdapter)); 355 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/.fr-amL3gv/dark-blue.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Dark blue theme for Highcharts JS 3 | * @author Torstein Hønsi 4 | */ 5 | 6 | Highcharts.theme = { 7 | colors: ["#DDDF0D", "#55BF3B", "#DF5353", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee", 8 | "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], 9 | chart: { 10 | backgroundColor: { 11 | linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 }, 12 | stops: [ 13 | [0, 'rgb(48, 48, 96)'], 14 | [1, 'rgb(0, 0, 0)'] 15 | ] 16 | }, 17 | borderColor: '#000000', 18 | borderWidth: 2, 19 | className: 'dark-container', 20 | plotBackgroundColor: 'rgba(255, 255, 255, .1)', 21 | plotBorderColor: '#CCCCCC', 22 | plotBorderWidth: 1 23 | }, 24 | title: { 25 | style: { 26 | color: '#C0C0C0', 27 | font: 'bold 16px "Trebuchet MS", Verdana, sans-serif' 28 | } 29 | }, 30 | subtitle: { 31 | style: { 32 | color: '#666666', 33 | font: 'bold 12px "Trebuchet MS", Verdana, sans-serif' 34 | } 35 | }, 36 | xAxis: { 37 | gridLineColor: '#333333', 38 | gridLineWidth: 1, 39 | labels: { 40 | style: { 41 | color: '#A0A0A0' 42 | } 43 | }, 44 | lineColor: '#A0A0A0', 45 | tickColor: '#A0A0A0', 46 | title: { 47 | style: { 48 | color: '#CCC', 49 | fontWeight: 'bold', 50 | fontSize: '12px', 51 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 52 | 53 | } 54 | } 55 | }, 56 | yAxis: { 57 | gridLineColor: '#333333', 58 | labels: { 59 | style: { 60 | color: '#A0A0A0' 61 | } 62 | }, 63 | lineColor: '#A0A0A0', 64 | minorTickInterval: null, 65 | tickColor: '#A0A0A0', 66 | tickWidth: 1, 67 | title: { 68 | style: { 69 | color: '#CCC', 70 | fontWeight: 'bold', 71 | fontSize: '12px', 72 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 73 | } 74 | } 75 | }, 76 | tooltip: { 77 | backgroundColor: 'rgba(0, 0, 0, 0.75)', 78 | style: { 79 | color: '#F0F0F0' 80 | } 81 | }, 82 | toolbar: { 83 | itemStyle: { 84 | color: 'silver' 85 | } 86 | }, 87 | plotOptions: { 88 | line: { 89 | dataLabels: { 90 | color: '#CCC' 91 | }, 92 | marker: { 93 | lineColor: '#333' 94 | } 95 | }, 96 | spline: { 97 | marker: { 98 | lineColor: '#333' 99 | } 100 | }, 101 | scatter: { 102 | marker: { 103 | lineColor: '#333' 104 | } 105 | }, 106 | candlestick: { 107 | lineColor: 'white' 108 | } 109 | }, 110 | legend: { 111 | itemStyle: { 112 | font: '9pt Trebuchet MS, Verdana, sans-serif', 113 | color: '#A0A0A0' 114 | }, 115 | itemHoverStyle: { 116 | color: '#FFF' 117 | }, 118 | itemHiddenStyle: { 119 | color: '#444' 120 | } 121 | }, 122 | credits: { 123 | style: { 124 | color: '#666' 125 | } 126 | }, 127 | labels: { 128 | style: { 129 | color: '#CCC' 130 | } 131 | }, 132 | 133 | navigation: { 134 | buttonOptions: { 135 | symbolStroke: '#DDDDDD', 136 | hoverSymbolStroke: '#FFFFFF', 137 | theme: { 138 | fill: { 139 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 140 | stops: [ 141 | [0.4, '#606060'], 142 | [0.6, '#333333'] 143 | ] 144 | }, 145 | stroke: '#000000' 146 | } 147 | } 148 | }, 149 | 150 | // scroll charts 151 | rangeSelector: { 152 | buttonTheme: { 153 | fill: { 154 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 155 | stops: [ 156 | [0.4, '#888'], 157 | [0.6, '#555'] 158 | ] 159 | }, 160 | stroke: '#000000', 161 | style: { 162 | color: '#CCC', 163 | fontWeight: 'bold' 164 | }, 165 | states: { 166 | hover: { 167 | fill: { 168 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 169 | stops: [ 170 | [0.4, '#BBB'], 171 | [0.6, '#888'] 172 | ] 173 | }, 174 | stroke: '#000000', 175 | style: { 176 | color: 'white' 177 | } 178 | }, 179 | select: { 180 | fill: { 181 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 182 | stops: [ 183 | [0.1, '#000'], 184 | [0.3, '#333'] 185 | ] 186 | }, 187 | stroke: '#000000', 188 | style: { 189 | color: 'yellow' 190 | } 191 | } 192 | } 193 | }, 194 | inputStyle: { 195 | backgroundColor: '#333', 196 | color: 'silver' 197 | }, 198 | labelStyle: { 199 | color: 'silver' 200 | } 201 | }, 202 | 203 | navigator: { 204 | handles: { 205 | backgroundColor: '#666', 206 | borderColor: '#AAA' 207 | }, 208 | outlineColor: '#CCC', 209 | maskFill: 'rgba(16, 16, 16, 0.5)', 210 | series: { 211 | color: '#7798BF', 212 | lineColor: '#A6C7ED' 213 | } 214 | }, 215 | 216 | scrollbar: { 217 | barBackgroundColor: { 218 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 219 | stops: [ 220 | [0.4, '#888'], 221 | [0.6, '#555'] 222 | ] 223 | }, 224 | barBorderColor: '#CCC', 225 | buttonArrowColor: '#CCC', 226 | buttonBackgroundColor: { 227 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 228 | stops: [ 229 | [0.4, '#888'], 230 | [0.6, '#555'] 231 | ] 232 | }, 233 | buttonBorderColor: '#CCC', 234 | rifleColor: '#FFF', 235 | trackBackgroundColor: { 236 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 237 | stops: [ 238 | [0, '#000'], 239 | [1, '#333'] 240 | ] 241 | }, 242 | trackBorderColor: '#666' 243 | }, 244 | 245 | // special colors for some of the 246 | legendBackgroundColor: 'rgba(0, 0, 0, 0.5)', 247 | legendBackgroundColorSolid: 'rgb(35, 35, 70)', 248 | dataLabelsColor: '#444', 249 | textColor: '#C0C0C0', 250 | maskColor: 'rgba(255,255,255,0.3)' 251 | }; 252 | 253 | // Apply the theme 254 | var highchartsOptions = Highcharts.setOptions(Highcharts.theme); 255 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/.fr-amL3gv/dark-green.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Dark blue theme for Highcharts JS 3 | * @author Torstein Hønsi 4 | */ 5 | 6 | Highcharts.theme = { 7 | colors: ["#DDDF0D", "#55BF3B", "#DF5353", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee", 8 | "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], 9 | chart: { 10 | backgroundColor: { 11 | linearGradient: [0, 0, 250, 500], 12 | stops: [ 13 | [0, 'rgb(48, 96, 48)'], 14 | [1, 'rgb(0, 0, 0)'] 15 | ] 16 | }, 17 | borderColor: '#000000', 18 | borderWidth: 2, 19 | className: 'dark-container', 20 | plotBackgroundColor: 'rgba(255, 255, 255, .1)', 21 | plotBorderColor: '#CCCCCC', 22 | plotBorderWidth: 1 23 | }, 24 | title: { 25 | style: { 26 | color: '#C0C0C0', 27 | font: 'bold 16px "Trebuchet MS", Verdana, sans-serif' 28 | } 29 | }, 30 | subtitle: { 31 | style: { 32 | color: '#666666', 33 | font: 'bold 12px "Trebuchet MS", Verdana, sans-serif' 34 | } 35 | }, 36 | xAxis: { 37 | gridLineColor: '#333333', 38 | gridLineWidth: 1, 39 | labels: { 40 | style: { 41 | color: '#A0A0A0' 42 | } 43 | }, 44 | lineColor: '#A0A0A0', 45 | tickColor: '#A0A0A0', 46 | title: { 47 | style: { 48 | color: '#CCC', 49 | fontWeight: 'bold', 50 | fontSize: '12px', 51 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 52 | 53 | } 54 | } 55 | }, 56 | yAxis: { 57 | gridLineColor: '#333333', 58 | labels: { 59 | style: { 60 | color: '#A0A0A0' 61 | } 62 | }, 63 | lineColor: '#A0A0A0', 64 | minorTickInterval: null, 65 | tickColor: '#A0A0A0', 66 | tickWidth: 1, 67 | title: { 68 | style: { 69 | color: '#CCC', 70 | fontWeight: 'bold', 71 | fontSize: '12px', 72 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 73 | } 74 | } 75 | }, 76 | tooltip: { 77 | backgroundColor: 'rgba(0, 0, 0, 0.75)', 78 | style: { 79 | color: '#F0F0F0' 80 | } 81 | }, 82 | toolbar: { 83 | itemStyle: { 84 | color: 'silver' 85 | } 86 | }, 87 | plotOptions: { 88 | line: { 89 | dataLabels: { 90 | color: '#CCC' 91 | }, 92 | marker: { 93 | lineColor: '#333' 94 | } 95 | }, 96 | spline: { 97 | marker: { 98 | lineColor: '#333' 99 | } 100 | }, 101 | scatter: { 102 | marker: { 103 | lineColor: '#333' 104 | } 105 | }, 106 | candlestick: { 107 | lineColor: 'white' 108 | } 109 | }, 110 | legend: { 111 | itemStyle: { 112 | font: '9pt Trebuchet MS, Verdana, sans-serif', 113 | color: '#A0A0A0' 114 | }, 115 | itemHoverStyle: { 116 | color: '#FFF' 117 | }, 118 | itemHiddenStyle: { 119 | color: '#444' 120 | } 121 | }, 122 | credits: { 123 | style: { 124 | color: '#666' 125 | } 126 | }, 127 | labels: { 128 | style: { 129 | color: '#CCC' 130 | } 131 | }, 132 | 133 | 134 | navigation: { 135 | buttonOptions: { 136 | symbolStroke: '#DDDDDD', 137 | hoverSymbolStroke: '#FFFFFF', 138 | theme: { 139 | fill: { 140 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 141 | stops: [ 142 | [0.4, '#606060'], 143 | [0.6, '#333333'] 144 | ] 145 | }, 146 | stroke: '#000000' 147 | } 148 | } 149 | }, 150 | 151 | // scroll charts 152 | rangeSelector: { 153 | buttonTheme: { 154 | fill: { 155 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 156 | stops: [ 157 | [0.4, '#888'], 158 | [0.6, '#555'] 159 | ] 160 | }, 161 | stroke: '#000000', 162 | style: { 163 | color: '#CCC', 164 | fontWeight: 'bold' 165 | }, 166 | states: { 167 | hover: { 168 | fill: { 169 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 170 | stops: [ 171 | [0.4, '#BBB'], 172 | [0.6, '#888'] 173 | ] 174 | }, 175 | stroke: '#000000', 176 | style: { 177 | color: 'white' 178 | } 179 | }, 180 | select: { 181 | fill: { 182 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 183 | stops: [ 184 | [0.1, '#000'], 185 | [0.3, '#333'] 186 | ] 187 | }, 188 | stroke: '#000000', 189 | style: { 190 | color: 'yellow' 191 | } 192 | } 193 | } 194 | }, 195 | inputStyle: { 196 | backgroundColor: '#333', 197 | color: 'silver' 198 | }, 199 | labelStyle: { 200 | color: 'silver' 201 | } 202 | }, 203 | 204 | navigator: { 205 | handles: { 206 | backgroundColor: '#666', 207 | borderColor: '#AAA' 208 | }, 209 | outlineColor: '#CCC', 210 | maskFill: 'rgba(16, 16, 16, 0.5)', 211 | series: { 212 | color: '#7798BF', 213 | lineColor: '#A6C7ED' 214 | } 215 | }, 216 | 217 | scrollbar: { 218 | barBackgroundColor: { 219 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 220 | stops: [ 221 | [0.4, '#888'], 222 | [0.6, '#555'] 223 | ] 224 | }, 225 | barBorderColor: '#CCC', 226 | buttonArrowColor: '#CCC', 227 | buttonBackgroundColor: { 228 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 229 | stops: [ 230 | [0.4, '#888'], 231 | [0.6, '#555'] 232 | ] 233 | }, 234 | buttonBorderColor: '#CCC', 235 | rifleColor: '#FFF', 236 | trackBackgroundColor: { 237 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 238 | stops: [ 239 | [0, '#000'], 240 | [1, '#333'] 241 | ] 242 | }, 243 | trackBorderColor: '#666' 244 | }, 245 | 246 | // special colors for some of the 247 | legendBackgroundColor: 'rgba(0, 0, 0, 0.5)', 248 | legendBackgroundColorSolid: 'rgb(35, 35, 70)', 249 | dataLabelsColor: '#444', 250 | textColor: '#C0C0C0', 251 | maskColor: 'rgba(255,255,255,0.3)' 252 | }; 253 | 254 | // Apply the theme 255 | var highchartsOptions = Highcharts.setOptions(Highcharts.theme); 256 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/.fr-amL3gv/data.js: -------------------------------------------------------------------------------- 1 | /* 2 | Data plugin for Highcharts 3 | 4 | (c) 2012-2013 Torstein Hønsi 5 | Last revision 2012-11-27 6 | 7 | License: www.highcharts.com/license 8 | */ 9 | (function(f){var k=f.each,n=function(a){this.init(a)};f.extend(n.prototype,{init:function(a){this.options=a;this.columns=a.columns||this.rowsToColumns(a.rows)||[];this.columns.length?this.dataFound():(this.parseCSV(),this.parseTable(),this.parseGoogleSpreadsheet())},dataFound:function(){this.parseTypes();this.findHeaderRow();this.parsed();this.complete()},parseCSV:function(){var a=this,b=this.options,c=b.csv,d=this.columns,e=b.startRow||0,g=b.endRow||Number.MAX_VALUE,h=b.startColumn||0,l=b.endColumn|| 10 | Number.MAX_VALUE,q=0;c&&(c=c.replace(/\r\n/g,"\n").replace(/\r/g,"\n").split(b.lineDelimiter||"\n"),k(c,function(c,m){var o=a.trim(c),f=o.indexOf("#")===0;m>=e&&m<=g&&!f&&o!==""&&(o=c.split(b.itemDelimiter||","),k(o,function(b,a){a>=h&&a<=l&&(d[a-h]||(d[a-h]=[]),d[a-h][q]=b)}),q+=1)}),this.dataFound())},parseTable:function(){var a=this.options,b=a.table,c=this.columns,d=a.startRow||0,e=a.endRow||Number.MAX_VALUE,g=a.startColumn||0,h=a.endColumn||Number.MAX_VALUE,l;b&&(typeof b==="string"&&(b=document.getElementById(b)), 11 | k(b.getElementsByTagName("tr"),function(a,b){l=0;b>=d&&b<=e&&k(a.childNodes,function(a){if((a.tagName==="TD"||a.tagName==="TH")&&l>=g&&l<=h)c[l]||(c[l]=[]),c[l][b-d]=a.innerHTML,l+=1})}),this.dataFound())},parseGoogleSpreadsheet:function(){var a=this,b=this.options,c=b.googleSpreadsheetKey,d=this.columns,e=b.startRow||0,g=b.endRow||Number.MAX_VALUE,h=b.startColumn||0,l=b.endColumn||Number.MAX_VALUE,f,j;c&&jQuery.getJSON("https://spreadsheets.google.com/feeds/cells/"+c+"/"+(b.googleSpreadsheetWorksheet|| 12 | "od6")+"/public/values?alt=json-in-script&callback=?",function(b){var b=b.feed.entry,c,k=b.length,n=0,p=0,i;for(i=0;i=h&&i<=l)d[i-h]=[],d[i-h].length=Math.min(p,g-e);for(i=0;i=h&&j<=l&&f>=e&&f<=g)d[j-h][f-e]=c.content.$t;a.dataFound()})},findHeaderRow:function(){k(this.columns,function(){});this.headerRow=0},trim:function(a){return typeof a==="string"?a.replace(/^\s+|\s+$/g, 13 | ""):a},parseTypes:function(){for(var a=this.columns,b=a.length,c,d,e,g;b--;)for(c=a[b].length;c--;)d=a[b][c],e=parseFloat(d),g=this.trim(d),g==e?(a[b][c]=e,e>31536E6?a[b].isDatetime=!0:a[b].isNumeric=!0):(d=this.parseDate(d),b===0&&typeof d==="number"&&!isNaN(d)?(a[b][c]=d,a[b].isDatetime=!0):a[b][c]=g===""?null:g)},dateFormats:{"YYYY-mm-dd":{regex:"^([0-9]{4})-([0-9]{2})-([0-9]{2})$",parser:function(a){return Date.UTC(+a[1],a[2]-1,+a[3])}}},parseDate:function(a){var b=this.options.parseDate,c,d, 14 | e;b&&(c=b(a));if(typeof a==="string")for(d in this.dateFormats)b=this.dateFormats[d],(e=a.match(b.regex))&&(c=b.parser(e));return c},rowsToColumns:function(a){var b,c,d,e,g;if(a){g=[];c=a.length;for(b=0;b1&&(d=a.shift(),this.headerRow===0&& 15 | d.shift(),(b=d.isNumeric||d.isDatetime)||(c=d),d.isDatetime&&(e="datetime"));h=[];for(j=0;j.*?$/,"").replace(/ /g," ").replace(/­/g,"­").replace(//g, 15 | 'xlink:href="$1"/>').replace(/id=([^" >]+)/g,'id="$1"').replace(/class=([^" >]+)/g,'class="$1"').replace(/ transform /g," ").replace(/:(path|rect)/g,"$1").replace(/style="([^"]+)"/g,function(a){return a.toLowerCase()});d=d.replace(/(url\(#highcharts-[0-9]+)"/g,"$1").replace(/"/g,"'");d.match(/ xmlns="/g).length===2&&(d=d.replace(/xmlns="[^"]+"/,""));return d},exportChart:function(a,b){var a=a||{},c=this.options.exporting,c=this.getSVG(k({chart:{borderRadius:0}},c.chartOptions,b,{exporting:{sourceWidth:a.sourceWidth|| 16 | c.sourceWidth,sourceHeight:a.sourceHeight||c.sourceHeight}})),a=k(this.options.exporting,a);e.post(a.url,{filename:a.filename||"chart",type:a.type,width:a.width||0,scale:a.scale||2,svg:c})},print:function(){var a=this,b=a.container,c=[],d=b.parentNode,e=i.body,g=e.childNodes;if(!a.isPrinting)a.isPrinting=!0,r(g,function(a,b){if(a.nodeType===1)c[b]=a.style.display,a.style.display="none"}),e.appendChild(b),z.focus(),z.print(),setTimeout(function(){d.appendChild(b);r(g,function(a,b){if(a.nodeType=== 17 | 1)a.style.display=c[b]});a.isPrinting=!1},1E3)},contextMenu:function(a,b,c,d,e,g,f){var h=this,q=h.options.navigation,n=q.menuItemStyle,o=h.chartWidth,i=h.chartHeight,A="cache-"+a,l=h[A],k=C(e,g),u,j,s;if(!l)h[A]=l=m("div",{className:"highcharts-"+a},{position:"absolute",zIndex:1E3,padding:k+"px"},h.container),u=m("div",null,p({MozBoxShadow:"3px 3px 10px #888",WebkitBoxShadow:"3px 3px 10px #888",boxShadow:"3px 3px 10px #888"},q.menuStyle),l),j=function(){t(l,{display:"none"});f&&f.setState(0);h.openMenu= 18 | !1},v(l,"mouseleave",function(){s=setTimeout(j,500)}),v(l,"mouseenter",function(){clearTimeout(s)}),r(b,function(a){if(a){var b=a.separator?m("hr",null,null,u):m("div",{onmouseover:function(){t(this,q.menuItemHoverStyle)},onmouseout:function(){t(this,n)},onclick:function(){j();a.onclick.apply(h,arguments)},innerHTML:a.text||h.options.lang[a.textKey]},p({cursor:"pointer"},n),u);h.exportDivElements.push(b)}}),h.exportDivElements.push(u,l),h.exportMenuWidth=l.offsetWidth,h.exportMenuHeight=l.offsetHeight; 19 | a={display:"block"};c+h.exportMenuWidth>o?a.right=o-c-e-k+"px":a.left=c-k+"px";d+g+h.exportMenuHeight>i?a.bottom=i-d-k+"px":a.top=d+g-k+"px";t(l,a);h.openMenu=!0},addButton:function(a){var b=this,c=b.renderer,a=k(b.options.navigation.buttonOptions,a),d=a.onclick,i=a.menuItems,g,f,h={stroke:a.symbolStroke,fill:a.symbolFill},q=a.symbolSize||12;if(!b.btnCount)b.btnCount=0;b.btnCount++;if(!b.exportDivElements)b.exportDivElements=[],b.exportSVGElements=[];if(a.enabled!==!1){var n=a.theme,o=n.states,m= 20 | o&&o.hover,o=o&&o.select,j;delete n.states;d?j=function(){d.apply(b,arguments)}:i&&(j=function(){b.contextMenu("contextmenu",i,f.translateX,f.translateY,f.width,f.height,f);f.setState(2)});a.text&&a.symbol?n.paddingLeft=e.pick(n.paddingLeft,25):a.text||p(n,{width:a.width,height:a.height,padding:0});f=c.button(a.text,0,0,j,n,m,o).attr({title:b.options.lang[a._titleKey],"stroke-linecap":"round"});a.symbol&&(g=c.symbol(a.symbol,a.symbolX-q/2,a.symbolY-q/2,q,q).attr(p(h,{"stroke-width":a.symbolStrokeWidth|| 21 | 1,zIndex:1})).add(f));f.add().align(p(a,{width:f.width,x:e.pick(a.x,w)}),!0,"spacingBox");w+=(f.width+a.buttonSpacing)*(a.align==="right"?-1:1);b.exportSVGElements.push(f,g)}},destroyExport:function(a){var a=a.target,b,c;for(b=0;bm-u?s:s+(l-s)*((m-u-i)/(m-u))};this.getX=function(i,a){return b+(a?-1:1)*(r(i)/2+c.dataLabels.distance)};this.center=[b,j,m];this.centerX=b;z(a,function(a){f+=a.y});z(a,function(a){p=null;x=f?a.y/f:0;n=g*m;k=n+x*m;h=r(n);y=b-h/2;A=y+h;h=r(k);o=b- 11 | h/2;t=o+h;n>v?(y=o=b-s/2,A=t=b+s/2):k>v&&(p=k,h=r(v),o=b-h/2,t=o+h,k=v);w=["M",y,n,"L",A,n,t,k];p&&w.push(t,p,o,p);w.push(o,k,"Z");a.shapeType="path";a.shapeArgs={d:w};a.percentage=x*100;a.plotX=b;a.plotY=(n+(p||k))/2;a.tooltipPos=[b,a.plotY];a.slice=C;a.half=q;g+=x});this.setTooltipPoints()},drawPoints:function(){var a=this,f=a.options,d=a.chart.renderer;z(a.data,function(e){var g=e.graphic,c=e.shapeArgs;g?g.animate(c):e.graphic=d.path(c).attr({fill:e.color,stroke:f.borderColor,"stroke-width":f.borderWidth}).add(a.group)})}, 12 | drawDataLabels:function(){var a=this.data,f=this.options.dataLabels.distance,d,e,g,c=a.length,j,b;for(this.center[2]-=2*f;c--;)g=a[c],e=(d=g.half)?1:-1,b=g.plotY,j=this.getX(b,d),g.labelPos=[0,b,j+(f-5)*e,b,j+f*e,b,d?"right":"left",0];q.pie.prototype.drawDataLabels.call(this)}})})(Highcharts); 13 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/.fr-amL3gv/funnel.src.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Highcharts funnel module, Beta 4 | * 5 | * (c) 2010-2012 Torstein Hønsi 6 | * 7 | * License: www.highcharts.com/license 8 | */ 9 | 10 | /*global Highcharts */ 11 | (function (Highcharts) { 12 | 13 | 'use strict'; 14 | 15 | // create shortcuts 16 | var defaultOptions = Highcharts.getOptions(), 17 | defaultPlotOptions = defaultOptions.plotOptions, 18 | seriesTypes = Highcharts.seriesTypes, 19 | merge = Highcharts.merge, 20 | noop = function () {}, 21 | each = Highcharts.each; 22 | 23 | // set default options 24 | defaultPlotOptions.funnel = merge(defaultPlotOptions.pie, { 25 | center: ['50%', '50%'], 26 | width: '90%', 27 | neckWidth: '30%', 28 | height: '100%', 29 | neckHeight: '25%', 30 | 31 | dataLabels: { 32 | //position: 'right', 33 | connectorWidth: 1, 34 | connectorColor: '#606060' 35 | }, 36 | size: true, // to avoid adapting to data label size in Pie.drawDataLabels 37 | states: { 38 | select: { 39 | color: '#C0C0C0', 40 | borderColor: '#000000', 41 | shadow: false 42 | } 43 | } 44 | }); 45 | 46 | 47 | seriesTypes.funnel = Highcharts.extendClass(seriesTypes.pie, { 48 | 49 | type: 'funnel', 50 | animate: noop, 51 | 52 | /** 53 | * Overrides the pie translate method 54 | */ 55 | translate: function () { 56 | 57 | var 58 | // Get positions - either an integer or a percentage string must be given 59 | getLength = function (length, relativeTo) { 60 | return (/%$/).test(length) ? 61 | relativeTo * parseInt(length, 10) / 100 : 62 | parseInt(length, 10); 63 | }, 64 | 65 | sum = 0, 66 | series = this, 67 | chart = series.chart, 68 | plotWidth = chart.plotWidth, 69 | plotHeight = chart.plotHeight, 70 | cumulative = 0, // start at top 71 | options = series.options, 72 | center = options.center, 73 | centerX = getLength(center[0], plotWidth), 74 | centerY = getLength(center[0], plotHeight), 75 | width = getLength(options.width, plotWidth), 76 | tempWidth, 77 | getWidthAt, 78 | height = getLength(options.height, plotHeight), 79 | neckWidth = getLength(options.neckWidth, plotWidth), 80 | neckHeight = getLength(options.neckHeight, plotHeight), 81 | neckY = height - neckHeight, 82 | data = series.data, 83 | path, 84 | fraction, 85 | half = options.dataLabels.position === 'left' ? 1 : 0, 86 | 87 | x1, 88 | y1, 89 | x2, 90 | x3, 91 | y3, 92 | x4, 93 | y5; 94 | 95 | // Return the width at a specific y coordinate 96 | series.getWidthAt = getWidthAt = function (y) { 97 | return y > height - neckHeight ? 98 | neckWidth : 99 | neckWidth + (width - neckWidth) * ((height - neckHeight - y) / (height - neckHeight)); 100 | }; 101 | series.getX = function (y, half) { 102 | return centerX + (half ? -1 : 1) * ((getWidthAt(y) / 2) + options.dataLabels.distance); 103 | }; 104 | 105 | // Expose 106 | series.center = [centerX, centerY, height]; 107 | series.centerX = centerX; 108 | 109 | /* 110 | * Individual point coordinate naming: 111 | * 112 | * x1,y1 _________________ x2,y1 113 | * \ / 114 | * \ / 115 | * \ / 116 | * \ / 117 | * \ / 118 | * x3,y3 _________ x4,y3 119 | * 120 | * Additional for the base of the neck: 121 | * 122 | * | | 123 | * | | 124 | * | | 125 | * x3,y5 _________ x4,y5 126 | */ 127 | 128 | 129 | 130 | 131 | // get the total sum 132 | each(data, function (point) { 133 | sum += point.y; 134 | }); 135 | 136 | each(data, function (point) { 137 | // set start and end positions 138 | y5 = null; 139 | fraction = sum ? point.y / sum : 0; 140 | y1 = cumulative * height; 141 | y3 = y1 + fraction * height; 142 | //tempWidth = neckWidth + (width - neckWidth) * ((height - neckHeight - y1) / (height - neckHeight)); 143 | tempWidth = getWidthAt(y1); 144 | x1 = centerX - tempWidth / 2; 145 | x2 = x1 + tempWidth; 146 | tempWidth = getWidthAt(y3); 147 | x3 = centerX - tempWidth / 2; 148 | x4 = x3 + tempWidth; 149 | 150 | // the entire point is within the neck 151 | if (y1 > neckY) { 152 | x1 = x3 = centerX - neckWidth / 2; 153 | x2 = x4 = centerX + neckWidth / 2; 154 | 155 | // the base of the neck 156 | } else if (y3 > neckY) { 157 | y5 = y3; 158 | 159 | tempWidth = getWidthAt(neckY); 160 | x3 = centerX - tempWidth / 2; 161 | x4 = x3 + tempWidth; 162 | 163 | y3 = neckY; 164 | } 165 | 166 | // save the path 167 | path = [ 168 | 'M', 169 | x1, y1, 170 | 'L', 171 | x2, y1, 172 | x4, y3 173 | ]; 174 | if (y5) { 175 | path.push(x4, y5, x3, y5); 176 | } 177 | path.push(x3, y3, 'Z'); 178 | 179 | // prepare for using shared dr 180 | point.shapeType = 'path'; 181 | point.shapeArgs = { d: path }; 182 | 183 | 184 | // for tooltips and data labels 185 | point.percentage = fraction * 100; 186 | point.plotX = centerX; 187 | point.plotY = (y1 + (y5 || y3)) / 2; 188 | 189 | // Placement of tooltips and data labels 190 | point.tooltipPos = [ 191 | centerX, 192 | point.plotY 193 | ]; 194 | 195 | // Slice is a noop on funnel points 196 | point.slice = noop; 197 | 198 | // Mimicking pie data label placement logic 199 | point.half = half; 200 | 201 | cumulative += fraction; 202 | }); 203 | 204 | 205 | series.setTooltipPoints(); 206 | }, 207 | /** 208 | * Draw a single point (wedge) 209 | * @param {Object} point The point object 210 | * @param {Object} color The color of the point 211 | * @param {Number} brightness The brightness relative to the color 212 | */ 213 | drawPoints: function () { 214 | var series = this, 215 | options = series.options, 216 | chart = series.chart, 217 | renderer = chart.renderer; 218 | 219 | each(series.data, function (point) { 220 | 221 | var graphic = point.graphic, 222 | shapeArgs = point.shapeArgs; 223 | 224 | if (!graphic) { // Create the shapes 225 | point.graphic = renderer.path(shapeArgs). 226 | attr({ 227 | fill: point.color, 228 | stroke: options.borderColor, 229 | 'stroke-width': options.borderWidth 230 | }). 231 | add(series.group); 232 | 233 | } else { // Update the shapes 234 | graphic.animate(shapeArgs); 235 | } 236 | }); 237 | }, 238 | 239 | /** 240 | * Extend the pie data label method 241 | */ 242 | drawDataLabels: function () { 243 | var data = this.data, 244 | labelDistance = this.options.dataLabels.distance, 245 | leftSide, 246 | sign, 247 | point, 248 | i = data.length, 249 | x, 250 | y; 251 | 252 | // In the original pie label anticollision logic, the slots are distributed 253 | // from one labelDistance above to one labelDistance below the pie. In funnels 254 | // we don't want this. 255 | this.center[2] -= 2 * labelDistance; 256 | 257 | // Set the label position array for each point. 258 | while (i--) { 259 | point = data[i]; 260 | leftSide = point.half; 261 | sign = leftSide ? 1 : -1; 262 | y = point.plotY; 263 | x = this.getX(y, leftSide); 264 | 265 | // set the anchor point for data labels 266 | point.labelPos = [ 267 | 0, // first break of connector 268 | y, // a/a 269 | x + (labelDistance - 5) * sign, // second break, right outside point shape 270 | y, // a/a 271 | x + labelDistance * sign, // landing point for connector 272 | y, // a/a 273 | leftSide ? 'right' : 'left', // alignment 274 | 0 // center angle 275 | ]; 276 | } 277 | 278 | seriesTypes.pie.prototype.drawDataLabels.call(this); 279 | } 280 | 281 | }); 282 | 283 | 284 | }(Highcharts)); 285 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/.fr-amL3gv/gray.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gray theme for Highcharts JS 3 | * @author Torstein Hønsi 4 | */ 5 | 6 | Highcharts.theme = { 7 | colors: ["#DDDF0D", "#7798BF", "#55BF3B", "#DF5353", "#aaeeee", "#ff0066", "#eeaaee", 8 | "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], 9 | chart: { 10 | backgroundColor: { 11 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 12 | stops: [ 13 | [0, 'rgb(96, 96, 96)'], 14 | [1, 'rgb(16, 16, 16)'] 15 | ] 16 | }, 17 | borderWidth: 0, 18 | borderRadius: 15, 19 | plotBackgroundColor: null, 20 | plotShadow: false, 21 | plotBorderWidth: 0 22 | }, 23 | title: { 24 | style: { 25 | color: '#FFF', 26 | font: '16px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 27 | } 28 | }, 29 | subtitle: { 30 | style: { 31 | color: '#DDD', 32 | font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 33 | } 34 | }, 35 | xAxis: { 36 | gridLineWidth: 0, 37 | lineColor: '#999', 38 | tickColor: '#999', 39 | labels: { 40 | style: { 41 | color: '#999', 42 | fontWeight: 'bold' 43 | } 44 | }, 45 | title: { 46 | style: { 47 | color: '#AAA', 48 | font: 'bold 12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 49 | } 50 | } 51 | }, 52 | yAxis: { 53 | alternateGridColor: null, 54 | minorTickInterval: null, 55 | gridLineColor: 'rgba(255, 255, 255, .1)', 56 | minorGridLineColor: 'rgba(255,255,255,0.07)', 57 | lineWidth: 0, 58 | tickWidth: 0, 59 | labels: { 60 | style: { 61 | color: '#999', 62 | fontWeight: 'bold' 63 | } 64 | }, 65 | title: { 66 | style: { 67 | color: '#AAA', 68 | font: 'bold 12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 69 | } 70 | } 71 | }, 72 | legend: { 73 | itemStyle: { 74 | color: '#CCC' 75 | }, 76 | itemHoverStyle: { 77 | color: '#FFF' 78 | }, 79 | itemHiddenStyle: { 80 | color: '#333' 81 | } 82 | }, 83 | labels: { 84 | style: { 85 | color: '#CCC' 86 | } 87 | }, 88 | tooltip: { 89 | backgroundColor: { 90 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 91 | stops: [ 92 | [0, 'rgba(96, 96, 96, .8)'], 93 | [1, 'rgba(16, 16, 16, .8)'] 94 | ] 95 | }, 96 | borderWidth: 0, 97 | style: { 98 | color: '#FFF' 99 | } 100 | }, 101 | 102 | 103 | plotOptions: { 104 | series: { 105 | shadow: true 106 | }, 107 | line: { 108 | dataLabels: { 109 | color: '#CCC' 110 | }, 111 | marker: { 112 | lineColor: '#333' 113 | } 114 | }, 115 | spline: { 116 | marker: { 117 | lineColor: '#333' 118 | } 119 | }, 120 | scatter: { 121 | marker: { 122 | lineColor: '#333' 123 | } 124 | }, 125 | candlestick: { 126 | lineColor: 'white' 127 | } 128 | }, 129 | 130 | toolbar: { 131 | itemStyle: { 132 | color: '#CCC' 133 | } 134 | }, 135 | 136 | navigation: { 137 | buttonOptions: { 138 | symbolStroke: '#DDDDDD', 139 | hoverSymbolStroke: '#FFFFFF', 140 | theme: { 141 | fill: { 142 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 143 | stops: [ 144 | [0.4, '#606060'], 145 | [0.6, '#333333'] 146 | ] 147 | }, 148 | stroke: '#000000' 149 | } 150 | } 151 | }, 152 | 153 | // scroll charts 154 | rangeSelector: { 155 | buttonTheme: { 156 | fill: { 157 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 158 | stops: [ 159 | [0.4, '#888'], 160 | [0.6, '#555'] 161 | ] 162 | }, 163 | stroke: '#000000', 164 | style: { 165 | color: '#CCC', 166 | fontWeight: 'bold' 167 | }, 168 | states: { 169 | hover: { 170 | fill: { 171 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 172 | stops: [ 173 | [0.4, '#BBB'], 174 | [0.6, '#888'] 175 | ] 176 | }, 177 | stroke: '#000000', 178 | style: { 179 | color: 'white' 180 | } 181 | }, 182 | select: { 183 | fill: { 184 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 185 | stops: [ 186 | [0.1, '#000'], 187 | [0.3, '#333'] 188 | ] 189 | }, 190 | stroke: '#000000', 191 | style: { 192 | color: 'yellow' 193 | } 194 | } 195 | } 196 | }, 197 | inputStyle: { 198 | backgroundColor: '#333', 199 | color: 'silver' 200 | }, 201 | labelStyle: { 202 | color: 'silver' 203 | } 204 | }, 205 | 206 | navigator: { 207 | handles: { 208 | backgroundColor: '#666', 209 | borderColor: '#AAA' 210 | }, 211 | outlineColor: '#CCC', 212 | maskFill: 'rgba(16, 16, 16, 0.5)', 213 | series: { 214 | color: '#7798BF', 215 | lineColor: '#A6C7ED' 216 | } 217 | }, 218 | 219 | scrollbar: { 220 | barBackgroundColor: { 221 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 222 | stops: [ 223 | [0.4, '#888'], 224 | [0.6, '#555'] 225 | ] 226 | }, 227 | barBorderColor: '#CCC', 228 | buttonArrowColor: '#CCC', 229 | buttonBackgroundColor: { 230 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 231 | stops: [ 232 | [0.4, '#888'], 233 | [0.6, '#555'] 234 | ] 235 | }, 236 | buttonBorderColor: '#CCC', 237 | rifleColor: '#FFF', 238 | trackBackgroundColor: { 239 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 240 | stops: [ 241 | [0, '#000'], 242 | [1, '#333'] 243 | ] 244 | }, 245 | trackBorderColor: '#666' 246 | }, 247 | 248 | // special colors for some of the demo examples 249 | legendBackgroundColor: 'rgba(48, 48, 48, 0.8)', 250 | legendBackgroundColorSolid: 'rgb(70, 70, 70)', 251 | dataLabelsColor: '#444', 252 | textColor: '#E0E0E0', 253 | maskColor: 'rgba(255,255,255,0.3)' 254 | }; 255 | 256 | // Apply the theme 257 | var highchartsOptions = Highcharts.setOptions(Highcharts.theme); 258 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/.fr-amL3gv/grid.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Grid theme for Highcharts JS 3 | * @author Torstein Hønsi 4 | */ 5 | 6 | Highcharts.theme = { 7 | colors: ['#058DC7', '#50B432', '#ED561B', '#DDDF00', '#24CBE5', '#64E572', '#FF9655', '#FFF263', '#6AF9C4'], 8 | chart: { 9 | backgroundColor: { 10 | linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 }, 11 | stops: [ 12 | [0, 'rgb(255, 255, 255)'], 13 | [1, 'rgb(240, 240, 255)'] 14 | ] 15 | }, 16 | borderWidth: 2, 17 | plotBackgroundColor: 'rgba(255, 255, 255, .9)', 18 | plotShadow: true, 19 | plotBorderWidth: 1 20 | }, 21 | title: { 22 | style: { 23 | color: '#000', 24 | font: 'bold 16px "Trebuchet MS", Verdana, sans-serif' 25 | } 26 | }, 27 | subtitle: { 28 | style: { 29 | color: '#666666', 30 | font: 'bold 12px "Trebuchet MS", Verdana, sans-serif' 31 | } 32 | }, 33 | xAxis: { 34 | gridLineWidth: 1, 35 | lineColor: '#000', 36 | tickColor: '#000', 37 | labels: { 38 | style: { 39 | color: '#000', 40 | font: '11px Trebuchet MS, Verdana, sans-serif' 41 | } 42 | }, 43 | title: { 44 | style: { 45 | color: '#333', 46 | fontWeight: 'bold', 47 | fontSize: '12px', 48 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 49 | 50 | } 51 | } 52 | }, 53 | yAxis: { 54 | minorTickInterval: 'auto', 55 | lineColor: '#000', 56 | lineWidth: 1, 57 | tickWidth: 1, 58 | tickColor: '#000', 59 | labels: { 60 | style: { 61 | color: '#000', 62 | font: '11px Trebuchet MS, Verdana, sans-serif' 63 | } 64 | }, 65 | title: { 66 | style: { 67 | color: '#333', 68 | fontWeight: 'bold', 69 | fontSize: '12px', 70 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 71 | } 72 | } 73 | }, 74 | legend: { 75 | itemStyle: { 76 | font: '9pt Trebuchet MS, Verdana, sans-serif', 77 | color: 'black' 78 | 79 | }, 80 | itemHoverStyle: { 81 | color: '#039' 82 | }, 83 | itemHiddenStyle: { 84 | color: 'gray' 85 | } 86 | }, 87 | labels: { 88 | style: { 89 | color: '#99b' 90 | } 91 | }, 92 | 93 | navigation: { 94 | buttonOptions: { 95 | theme: { 96 | stroke: '#CCCCCC' 97 | } 98 | } 99 | } 100 | }; 101 | 102 | // Apply the theme 103 | var highchartsOptions = Highcharts.setOptions(Highcharts.theme); 104 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/.fr-amL3gv/skies.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Skies theme for Highcharts JS 3 | * @author Torstein Hønsi 4 | */ 5 | 6 | Highcharts.theme = { 7 | colors: ["#514F78", "#42A07B", "#9B5E4A", "#72727F", "#1F949A", "#82914E", "#86777F", "#42A07B"], 8 | chart: { 9 | className: 'skies', 10 | borderWidth: 0, 11 | plotShadow: true, 12 | plotBackgroundImage: '/demo/gfx/skies.jpg', 13 | plotBackgroundColor: { 14 | linearGradient: [0, 0, 250, 500], 15 | stops: [ 16 | [0, 'rgba(255, 255, 255, 1)'], 17 | [1, 'rgba(255, 255, 255, 0)'] 18 | ] 19 | }, 20 | plotBorderWidth: 1 21 | }, 22 | title: { 23 | style: { 24 | color: '#3E576F', 25 | font: '16px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 26 | } 27 | }, 28 | subtitle: { 29 | style: { 30 | color: '#6D869F', 31 | font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 32 | } 33 | }, 34 | xAxis: { 35 | gridLineWidth: 0, 36 | lineColor: '#C0D0E0', 37 | tickColor: '#C0D0E0', 38 | labels: { 39 | style: { 40 | color: '#666', 41 | fontWeight: 'bold' 42 | } 43 | }, 44 | title: { 45 | style: { 46 | color: '#666', 47 | font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 48 | } 49 | } 50 | }, 51 | yAxis: { 52 | alternateGridColor: 'rgba(255, 255, 255, .5)', 53 | lineColor: '#C0D0E0', 54 | tickColor: '#C0D0E0', 55 | tickWidth: 1, 56 | labels: { 57 | style: { 58 | color: '#666', 59 | fontWeight: 'bold' 60 | } 61 | }, 62 | title: { 63 | style: { 64 | color: '#666', 65 | font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 66 | } 67 | } 68 | }, 69 | legend: { 70 | itemStyle: { 71 | font: '9pt Trebuchet MS, Verdana, sans-serif', 72 | color: '#3E576F' 73 | }, 74 | itemHoverStyle: { 75 | color: 'black' 76 | }, 77 | itemHiddenStyle: { 78 | color: 'silver' 79 | } 80 | }, 81 | labels: { 82 | style: { 83 | color: '#3E576F' 84 | } 85 | } 86 | }; 87 | 88 | // Apply the theme 89 | var highchartsOptions = Highcharts.setOptions(Highcharts.theme); 90 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/modules/annotations.js: -------------------------------------------------------------------------------- 1 | (function(f,A){function k(a){return typeof a==="number"}function l(a){return a!==B&&a!==null}var B,n,o,w=f.Chart,r=f.extend,x=f.each,y;y={xAxis:0,yAxis:0,title:{style:{},text:"",x:0,y:0},shape:{params:{stroke:"#000000",fill:"transparent",strokeWidth:2}}};o=["path","rect","circle"];n={top:0,left:0,center:0.5,middle:0.5,bottom:1,right:1};var s=A.inArray,C=f.merge,z=function(){this.init.apply(this,arguments)};z.prototype={init:function(a,d){this.chart=a;this.options=C({},y,d)},render:function(a){var d= 2 | this.chart,c=this.chart.renderer,b=this.group,m=this.title,e=this.shape,g=this.options,f=g.title,i=g.shape;if(!b)b=this.group=c.g();if(!m&&f)m=this.title=c.label(f),m.add(b);if(!e&&i&&s(i.type,o)!==-1)e=this.shape=c[g.shape.type](i.params),e.add(b);b.add(d.annotations.group);this.linkObjects();a!==!1&&this.redraw()},redraw:function(){var a=this.options,d=this.chart,c=this.group,b=this.title,m=this.shape,e=this.linkedObject,g=d.xAxis[a.xAxis],o=d.yAxis[a.yAxis],i=a.width,t=a.height,u=n[a.anchorY], 3 | v=n[a.anchorX],h,q,j,p;if(e)h=e instanceof f.Point?"point":e instanceof f.Series?"series":null,h==="point"?(a.xValue=e.x,a.yValue=e.y,q=e.series):h==="series"&&(q=e),c.visibility!==q.group.visibility&&c.attr({visibility:q.group.visibility});e=(l(a.xValue)?g.toPixels(a.xValue+g.minPointOffset):a.x)-g.minPixelPadding;h=l(a.yValue)?o.toPixels(a.yValue):a.y;if(!isNaN(e)&&!isNaN(h)&&k(e)&&k(h)){b&&(b.attr(a.title),b.css(a.title.style));if(m){b=r({},a.shape.params);if(a.units==="values"){for(j in b)s(j, 4 | ["width","x"])>-1?b[j]=g.translate(b[j]):s(j,["height","y"])>-1&&(b[j]=o.translate(b[j]));b.width&&(b.width-=g.toPixels(0)-g.left);b.x&&(b.x+=g.minPixelPadding)}m.attr(b)}c.bBox=null;if(!k(i))p=c.getBBox(),i=p.width;if(!k(t))p||(p=c.getBBox()),t=p.height;if(!k(v))v=n.center;if(!k(u))u=n.center;e-=i*v;h-=t*u;d.animation&&l(c.translateX)&&l(c.translateY)?c.animate({translateX:e,translateY:h}):c.translate(e,h)}},destroy:function(){var a=this,d=this.chart.annotations.allItems,c=d.indexOf(a);c>-1&&d.splice(c, 5 | 1);x(["title","shape","group"],function(b){a[b]&&(a[b].destroy(),a[b]=null)});a.group=a.title=a.shape=a.chart=a.options=null},update:function(a,d){r(this.options,a);this.linkObjects();this.render(d)},linkObjects:function(){var a=this.chart,d=this.linkedObject,c=d&&(d.id||d.options.id),b=this.options.linkedTo;if(l(b)){if(!l(d)||b!==c)this.linkedObject=a.get(b)}else this.linkedObject=null}};r(w.prototype,{annotations:{add:function(a,d){var c=this.allItems,b=this.chart,f,e;Object.prototype.toString.call(a)=== 6 | "[object Array]"||(a=[a]);for(e=a.length;e--;)f=new z(b,a[e]),c.push(f),f.render(d)},redraw:function(){x(this.allItems,function(a){a.redraw()})}}});w.prototype.callbacks.push(function(a){var d=a.options.annotations,c;c=a.renderer.g("annotations");c.attr({zIndex:7});c.add();a.annotations.allItems=[];a.annotations.chart=a;a.annotations.group=c;Object.prototype.toString.call(d)==="[object Array]"&&d.length>0&&a.annotations.add(a.options.annotations);f.addEvent(a,"redraw",function(){a.annotations.redraw()})})})(Highcharts, 7 | HighchartsAdapter); 8 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/modules/annotations.src.js: -------------------------------------------------------------------------------- 1 | (function (Highcharts, HighchartsAdapter) { 2 | 3 | var UNDEFINED, 4 | ALIGN_FACTOR, 5 | ALLOWED_SHAPES, 6 | Chart = Highcharts.Chart, 7 | extend = Highcharts.extend, 8 | each = Highcharts.each, 9 | defaultOptions; 10 | 11 | defaultOptions = { 12 | xAxis: 0, 13 | yAxis: 0, 14 | title: { 15 | style: {}, 16 | text: "", 17 | x: 0, 18 | y: 0 19 | }, 20 | shape: { 21 | params: { 22 | stroke: "#000000", 23 | fill: "transparent", 24 | strokeWidth: 2 25 | } 26 | } 27 | }; 28 | 29 | ALLOWED_SHAPES = ["path", "rect", "circle"]; 30 | 31 | ALIGN_FACTOR = { 32 | top: 0, 33 | left: 0, 34 | center: 0.5, 35 | middle: 0.5, 36 | bottom: 1, 37 | right: 1 38 | }; 39 | 40 | 41 | // Highcharts helper methods 42 | var inArray = HighchartsAdapter.inArray, 43 | merge = Highcharts.merge; 44 | 45 | function isArray(obj) { 46 | return Object.prototype.toString.call(obj) === '[object Array]'; 47 | } 48 | 49 | function isNumber(n) { 50 | return typeof n === 'number'; 51 | } 52 | 53 | function defined(obj) { 54 | return obj !== UNDEFINED && obj !== null; 55 | } 56 | 57 | 58 | // Define annotation prototype 59 | var Annotation = function () { 60 | this.init.apply(this, arguments); 61 | }; 62 | Annotation.prototype = { 63 | /* 64 | * Initialize the annotation 65 | */ 66 | init: function (chart, options) { 67 | this.chart = chart; 68 | this.options = merge({}, defaultOptions, options); 69 | }, 70 | 71 | /* 72 | * Render the annotation 73 | */ 74 | render: function (redraw) { 75 | var annotation = this, 76 | chart = this.chart, 77 | renderer = annotation.chart.renderer, 78 | group = annotation.group, 79 | title = annotation.title, 80 | shape = annotation.shape, 81 | options = annotation.options, 82 | titleOptions = options.title, 83 | shapeOptions = options.shape; 84 | 85 | if (!group) { 86 | group = annotation.group = renderer.g(); 87 | } 88 | 89 | if (!title && titleOptions) { 90 | title = annotation.title = renderer.label(titleOptions); 91 | title.add(group); 92 | } 93 | 94 | if (!shape && shapeOptions && inArray(shapeOptions.type, ALLOWED_SHAPES) !== -1) { 95 | shape = annotation.shape = renderer[options.shape.type](shapeOptions.params); 96 | shape.add(group); 97 | } 98 | 99 | group.add(chart.annotations.group); 100 | 101 | // link annotations to point or series 102 | annotation.linkObjects(); 103 | 104 | if (redraw !== false) { 105 | annotation.redraw(); 106 | } 107 | }, 108 | 109 | /* 110 | * Redraw the annotation title or shape after options update 111 | */ 112 | redraw: function () { 113 | var options = this.options, 114 | chart = this.chart, 115 | group = this.group, 116 | title = this.title, 117 | shape = this.shape, 118 | linkedTo = this.linkedObject, 119 | xAxis = chart.xAxis[options.xAxis], 120 | yAxis = chart.yAxis[options.yAxis], 121 | width = options.width, 122 | height = options.height, 123 | anchorY = ALIGN_FACTOR[options.anchorY], 124 | anchorX = ALIGN_FACTOR[options.anchorX], 125 | resetBBox = false, 126 | shapeParams, 127 | linkType, 128 | series, 129 | param, 130 | bbox, 131 | x, 132 | y; 133 | 134 | if (linkedTo) { 135 | linkType = (linkedTo instanceof Highcharts.Point) ? 'point' : 136 | (linkedTo instanceof Highcharts.Series) ? 'series' : null; 137 | 138 | if (linkType === 'point') { 139 | options.xValue = linkedTo.x; 140 | options.yValue = linkedTo.y; 141 | series = linkedTo.series; 142 | } else if (linkType === 'series') { 143 | series = linkedTo; 144 | } 145 | 146 | if (group.visibility !== series.group.visibility) { 147 | group.attr({ 148 | visibility: series.group.visibility 149 | }); 150 | } 151 | } 152 | 153 | 154 | // Based on given options find annotation pixel position 155 | x = (defined(options.xValue) ? xAxis.toPixels(options.xValue + xAxis.minPointOffset) : options.x) - xAxis.minPixelPadding; 156 | y = defined(options.yValue) ? yAxis.toPixels(options.yValue) : options.y; 157 | 158 | 159 | if (isNaN(x) || isNaN(y) || !isNumber(x) || !isNumber(y)) { 160 | return; 161 | } 162 | 163 | 164 | if (title) { 165 | title.attr(options.title); 166 | title.css(options.title.style); 167 | resetBBox = true; 168 | } 169 | 170 | if (shape) { 171 | shapeParams = extend({}, options.shape.params); 172 | 173 | if (options.units === 'values') { 174 | for (param in shapeParams) { 175 | if (inArray(param, ['width', 'x']) > -1) { 176 | shapeParams[param] = xAxis.translate(shapeParams[param]); 177 | } else if (inArray(param, ['height', 'y']) > -1) { 178 | shapeParams[param] = yAxis.translate(shapeParams[param]); 179 | } 180 | } 181 | 182 | if (shapeParams.width) { 183 | shapeParams.width -= xAxis.toPixels(0) - xAxis.left; 184 | } 185 | 186 | if (shapeParams.x) { 187 | shapeParams.x += xAxis.minPixelPadding; 188 | } 189 | 190 | } 191 | 192 | resetBBox = true; 193 | shape.attr(shapeParams); 194 | } 195 | 196 | group.bBox = null; 197 | 198 | // If annotation width or height is not defined in options use bounding box size 199 | if (!isNumber(width)) { 200 | bbox = group.getBBox(); 201 | width = bbox.width; 202 | } 203 | 204 | if (!isNumber(height)) { 205 | // get bbox only if it wasn't set before 206 | if (!bbox) { 207 | bbox = group.getBBox(); 208 | } 209 | 210 | height = bbox.height; 211 | } 212 | 213 | // Calculate anchor point 214 | if (!isNumber(anchorX)) { 215 | anchorX = ALIGN_FACTOR.center; 216 | } 217 | 218 | if (!isNumber(anchorY)) { 219 | anchorY = ALIGN_FACTOR.center; 220 | } 221 | 222 | // Translate group according to its dimension and anchor point 223 | x = x - width * anchorX; 224 | y = y - height * anchorY; 225 | 226 | if (chart.animation && defined(group.translateX) && defined(group.translateY)) { 227 | group.animate({ 228 | translateX: x, 229 | translateY: y 230 | }); 231 | } else { 232 | group.translate(x, y); 233 | } 234 | }, 235 | 236 | /* 237 | * Destroy the annotation 238 | */ 239 | destroy: function () { 240 | var annotation = this, 241 | chart = this.chart, 242 | allItems = chart.annotations.allItems, 243 | index = allItems.indexOf(annotation); 244 | 245 | if (index > -1) { 246 | allItems.splice(index, 1); 247 | } 248 | 249 | each(['title', 'shape', 'group'], function (element) { 250 | if (annotation[element]) { 251 | annotation[element].destroy(); 252 | annotation[element] = null; 253 | } 254 | }); 255 | 256 | annotation.group = annotation.title = annotation.shape = annotation.chart = annotation.options = null; 257 | }, 258 | 259 | /* 260 | * Update the annotation with a given options 261 | */ 262 | update: function (options, redraw) { 263 | extend(this.options, options); 264 | 265 | // update link to point or series 266 | this.linkObjects(); 267 | 268 | this.render(redraw); 269 | }, 270 | 271 | linkObjects: function () { 272 | var annotation = this, 273 | chart = annotation.chart, 274 | linkedTo = annotation.linkedObject, 275 | linkedId = linkedTo && (linkedTo.id || linkedTo.options.id), 276 | options = annotation.options, 277 | id = options.linkedTo; 278 | 279 | if (!defined(id)) { 280 | annotation.linkedObject = null; 281 | } else if (!defined(linkedTo) || id !== linkedId) { 282 | annotation.linkedObject = chart.get(id); 283 | } 284 | } 285 | }; 286 | 287 | 288 | // Add annotations methods to chart prototype 289 | extend(Chart.prototype, { 290 | annotations: { 291 | /* 292 | * Unified method for adding annotations to the chart 293 | */ 294 | add: function (options, redraw) { 295 | var annotations = this.allItems, 296 | chart = this.chart, 297 | item, 298 | len; 299 | 300 | if (!isArray(options)) { 301 | options = [options]; 302 | } 303 | 304 | len = options.length; 305 | 306 | while (len--) { 307 | item = new Annotation(chart, options[len]); 308 | annotations.push(item); 309 | item.render(redraw); 310 | } 311 | }, 312 | 313 | /** 314 | * Redraw all annotations, method used in chart events 315 | */ 316 | redraw: function () { 317 | each(this.allItems, function (annotation) { 318 | annotation.redraw(); 319 | }); 320 | } 321 | } 322 | }); 323 | 324 | 325 | // Initialize on chart load 326 | Chart.prototype.callbacks.push(function (chart) { 327 | var options = chart.options.annotations, 328 | group; 329 | 330 | group = chart.renderer.g("annotations"); 331 | group.attr({ 332 | zIndex: 7 333 | }); 334 | group.add(); 335 | 336 | // initialize empty array for annotations 337 | chart.annotations.allItems = []; 338 | 339 | // link chart object to annotations 340 | chart.annotations.chart = chart; 341 | 342 | // link annotations group element to the chart 343 | chart.annotations.group = group; 344 | 345 | if (isArray(options) && options.length > 0) { 346 | chart.annotations.add(chart.options.annotations); 347 | } 348 | 349 | // update annotations after chart redraw 350 | Highcharts.addEvent(chart, 'redraw', function () { 351 | chart.annotations.redraw(); 352 | }); 353 | }); 354 | }(Highcharts, HighchartsAdapter)); 355 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/modules/data.js: -------------------------------------------------------------------------------- 1 | /* 2 | Data plugin for Highcharts 3 | 4 | (c) 2012-2013 Torstein Hønsi 5 | Last revision 2012-11-27 6 | 7 | License: www.highcharts.com/license 8 | */ 9 | (function(f){var k=f.each,n=function(a){this.init(a)};f.extend(n.prototype,{init:function(a){this.options=a;this.columns=a.columns||this.rowsToColumns(a.rows)||[];this.columns.length?this.dataFound():(this.parseCSV(),this.parseTable(),this.parseGoogleSpreadsheet())},dataFound:function(){this.parseTypes();this.findHeaderRow();this.parsed();this.complete()},parseCSV:function(){var a=this,b=this.options,c=b.csv,d=this.columns,e=b.startRow||0,g=b.endRow||Number.MAX_VALUE,h=b.startColumn||0,l=b.endColumn|| 10 | Number.MAX_VALUE,q=0;c&&(c=c.replace(/\r\n/g,"\n").replace(/\r/g,"\n").split(b.lineDelimiter||"\n"),k(c,function(c,m){var o=a.trim(c),f=o.indexOf("#")===0;m>=e&&m<=g&&!f&&o!==""&&(o=c.split(b.itemDelimiter||","),k(o,function(b,a){a>=h&&a<=l&&(d[a-h]||(d[a-h]=[]),d[a-h][q]=b)}),q+=1)}),this.dataFound())},parseTable:function(){var a=this.options,b=a.table,c=this.columns,d=a.startRow||0,e=a.endRow||Number.MAX_VALUE,g=a.startColumn||0,h=a.endColumn||Number.MAX_VALUE,l;b&&(typeof b==="string"&&(b=document.getElementById(b)), 11 | k(b.getElementsByTagName("tr"),function(a,b){l=0;b>=d&&b<=e&&k(a.childNodes,function(a){if((a.tagName==="TD"||a.tagName==="TH")&&l>=g&&l<=h)c[l]||(c[l]=[]),c[l][b-d]=a.innerHTML,l+=1})}),this.dataFound())},parseGoogleSpreadsheet:function(){var a=this,b=this.options,c=b.googleSpreadsheetKey,d=this.columns,e=b.startRow||0,g=b.endRow||Number.MAX_VALUE,h=b.startColumn||0,l=b.endColumn||Number.MAX_VALUE,f,j;c&&jQuery.getJSON("https://spreadsheets.google.com/feeds/cells/"+c+"/"+(b.googleSpreadsheetWorksheet|| 12 | "od6")+"/public/values?alt=json-in-script&callback=?",function(b){var b=b.feed.entry,c,k=b.length,n=0,p=0,i;for(i=0;i=h&&i<=l)d[i-h]=[],d[i-h].length=Math.min(p,g-e);for(i=0;i=h&&j<=l&&f>=e&&f<=g)d[j-h][f-e]=c.content.$t;a.dataFound()})},findHeaderRow:function(){k(this.columns,function(){});this.headerRow=0},trim:function(a){return typeof a==="string"?a.replace(/^\s+|\s+$/g, 13 | ""):a},parseTypes:function(){for(var a=this.columns,b=a.length,c,d,e,g;b--;)for(c=a[b].length;c--;)d=a[b][c],e=parseFloat(d),g=this.trim(d),g==e?(a[b][c]=e,e>31536E6?a[b].isDatetime=!0:a[b].isNumeric=!0):(d=this.parseDate(d),b===0&&typeof d==="number"&&!isNaN(d)?(a[b][c]=d,a[b].isDatetime=!0):a[b][c]=g===""?null:g)},dateFormats:{"YYYY-mm-dd":{regex:"^([0-9]{4})-([0-9]{2})-([0-9]{2})$",parser:function(a){return Date.UTC(+a[1],a[2]-1,+a[3])}}},parseDate:function(a){var b=this.options.parseDate,c,d, 14 | e;b&&(c=b(a));if(typeof a==="string")for(d in this.dateFormats)b=this.dateFormats[d],(e=a.match(b.regex))&&(c=b.parser(e));return c},rowsToColumns:function(a){var b,c,d,e,g;if(a){g=[];c=a.length;for(b=0;b1&&(d=a.shift(),this.headerRow===0&& 15 | d.shift(),(b=d.isNumeric||d.isDatetime)||(c=d),d.isDatetime&&(e="datetime"));h=[];for(j=0;j> 20 | * A two-dimensional array representing the input data on tabular form. This input can 21 | * be used when the data is already parsed, for example from a grid view component. 22 | * Each cell can be a string or number. If not switchRowsAndColumns is set, the columns 23 | * are interpreted as series. See also the rows option. 24 | * 25 | * - complete : Function(chartOptions) 26 | * The callback that is evaluated when the data is finished loading, optionally from an 27 | * external source, and parsed. The first argument passed is a finished chart options 28 | * object, containing series and an xAxis with categories if applicable. Thise options 29 | * can be extended with additional options and passed directly to the chart constructor. 30 | * 31 | * - csv : String 32 | * A comma delimited string to be parsed. Related options are startRow, endRow, startColumn 33 | * and endColumn to delimit what part of the table is used. The lineDelimiter and 34 | * itemDelimiter options define the CSV delimiter formats. 35 | * 36 | * - endColumn : Integer 37 | * In tabular input data, the first row (indexed by 0) to use. Defaults to the last 38 | * column containing data. 39 | * 40 | * - endRow : Integer 41 | * In tabular input data, the last row (indexed by 0) to use. Defaults to the last row 42 | * containing data. 43 | * 44 | * - googleSpreadsheetKey : String 45 | * A Google Spreadsheet key. See https://developers.google.com/gdata/samples/spreadsheet_sample 46 | * for general information on GS. 47 | * 48 | * - googleSpreadsheetWorksheet : String 49 | * The Google Spreadsheet worksheet. The available id's can be read from 50 | * https://spreadsheets.google.com/feeds/worksheets/{key}/public/basic 51 | * 52 | * - itemDelimiter : String 53 | * Item or cell delimiter for parsing CSV. Defaults to ",". 54 | * 55 | * - lineDelimiter : String 56 | * Line delimiter for parsing CSV. Defaults to "\n". 57 | * 58 | * - parsed : Function 59 | * A callback function to access the parsed columns, the two-dimentional input data 60 | * array directly, before they are interpreted into series data and categories. 61 | * 62 | * - parseDate : Function 63 | * A callback function to parse string representations of dates into JavaScript timestamps. 64 | * Return an integer on success. 65 | * 66 | * - rows : Array> 67 | * The same as the columns input option, but defining rows intead of columns. 68 | * 69 | * - startColumn : Integer 70 | * In tabular input data, the first column (indexed by 0) to use. 71 | * 72 | * - startRow : Integer 73 | * In tabular input data, the first row (indexed by 0) to use. 74 | * 75 | * - table : String|HTMLElement 76 | * A HTML table or the id of such to be parsed as input data. Related options ara startRow, 77 | * endRow, startColumn and endColumn to delimit what part of the table is used. 78 | */ 79 | 80 | // JSLint options: 81 | /*global jQuery */ 82 | 83 | (function (Highcharts) { 84 | 85 | // Utilities 86 | var each = Highcharts.each; 87 | 88 | 89 | // The Data constructor 90 | var Data = function (options) { 91 | this.init(options); 92 | }; 93 | 94 | // Set the prototype properties 95 | Highcharts.extend(Data.prototype, { 96 | 97 | /** 98 | * Initialize the Data object with the given options 99 | */ 100 | init: function (options) { 101 | this.options = options; 102 | this.columns = options.columns || this.rowsToColumns(options.rows) || []; 103 | 104 | // No need to parse or interpret anything 105 | if (this.columns.length) { 106 | this.dataFound(); 107 | 108 | // Parse and interpret 109 | } else { 110 | 111 | // Parse a CSV string if options.csv is given 112 | this.parseCSV(); 113 | 114 | // Parse a HTML table if options.table is given 115 | this.parseTable(); 116 | 117 | // Parse a Google Spreadsheet 118 | this.parseGoogleSpreadsheet(); 119 | } 120 | 121 | }, 122 | 123 | dataFound: function () { 124 | // Interpret the values into right types 125 | this.parseTypes(); 126 | 127 | // Use first row for series names? 128 | this.findHeaderRow(); 129 | 130 | // Handle columns if a handleColumns callback is given 131 | this.parsed(); 132 | 133 | // Complete if a complete callback is given 134 | this.complete(); 135 | 136 | }, 137 | 138 | /** 139 | * Parse a CSV input string 140 | */ 141 | parseCSV: function () { 142 | var self = this, 143 | options = this.options, 144 | csv = options.csv, 145 | columns = this.columns, 146 | startRow = options.startRow || 0, 147 | endRow = options.endRow || Number.MAX_VALUE, 148 | startColumn = options.startColumn || 0, 149 | endColumn = options.endColumn || Number.MAX_VALUE, 150 | lines, 151 | activeRowNo = 0; 152 | 153 | if (csv) { 154 | 155 | lines = csv 156 | .replace(/\r\n/g, "\n") // Unix 157 | .replace(/\r/g, "\n") // Mac 158 | .split(options.lineDelimiter || "\n"); 159 | 160 | each(lines, function (line, rowNo) { 161 | var trimmed = self.trim(line), 162 | isComment = trimmed.indexOf('#') === 0, 163 | isBlank = trimmed === '', 164 | items; 165 | 166 | if (rowNo >= startRow && rowNo <= endRow && !isComment && !isBlank) { 167 | items = line.split(options.itemDelimiter || ','); 168 | each(items, function (item, colNo) { 169 | if (colNo >= startColumn && colNo <= endColumn) { 170 | if (!columns[colNo - startColumn]) { 171 | columns[colNo - startColumn] = []; 172 | } 173 | 174 | columns[colNo - startColumn][activeRowNo] = item; 175 | } 176 | }); 177 | activeRowNo += 1; 178 | } 179 | }); 180 | 181 | this.dataFound(); 182 | } 183 | }, 184 | 185 | /** 186 | * Parse a HTML table 187 | */ 188 | parseTable: function () { 189 | var options = this.options, 190 | table = options.table, 191 | columns = this.columns, 192 | startRow = options.startRow || 0, 193 | endRow = options.endRow || Number.MAX_VALUE, 194 | startColumn = options.startColumn || 0, 195 | endColumn = options.endColumn || Number.MAX_VALUE, 196 | colNo; 197 | 198 | if (table) { 199 | 200 | if (typeof table === 'string') { 201 | table = document.getElementById(table); 202 | } 203 | 204 | each(table.getElementsByTagName('tr'), function (tr, rowNo) { 205 | colNo = 0; 206 | if (rowNo >= startRow && rowNo <= endRow) { 207 | each(tr.childNodes, function (item) { 208 | if ((item.tagName === 'TD' || item.tagName === 'TH') && colNo >= startColumn && colNo <= endColumn) { 209 | if (!columns[colNo]) { 210 | columns[colNo] = []; 211 | } 212 | columns[colNo][rowNo - startRow] = item.innerHTML; 213 | 214 | colNo += 1; 215 | } 216 | }); 217 | } 218 | }); 219 | 220 | this.dataFound(); // continue 221 | } 222 | }, 223 | 224 | /** 225 | * TODO: 226 | * - switchRowsAndColumns 227 | */ 228 | parseGoogleSpreadsheet: function () { 229 | var self = this, 230 | options = this.options, 231 | googleSpreadsheetKey = options.googleSpreadsheetKey, 232 | columns = this.columns, 233 | startRow = options.startRow || 0, 234 | endRow = options.endRow || Number.MAX_VALUE, 235 | startColumn = options.startColumn || 0, 236 | endColumn = options.endColumn || Number.MAX_VALUE, 237 | gr, // google row 238 | gc; // google column 239 | 240 | if (googleSpreadsheetKey) { 241 | jQuery.getJSON('https://spreadsheets.google.com/feeds/cells/' + 242 | googleSpreadsheetKey + '/' + (options.googleSpreadsheetWorksheet || 'od6') + 243 | '/public/values?alt=json-in-script&callback=?', 244 | function (json) { 245 | 246 | // Prepare the data from the spreadsheat 247 | var cells = json.feed.entry, 248 | cell, 249 | cellCount = cells.length, 250 | colCount = 0, 251 | rowCount = 0, 252 | i; 253 | 254 | // First, find the total number of columns and rows that 255 | // are actually filled with data 256 | for (i = 0; i < cellCount; i++) { 257 | cell = cells[i]; 258 | colCount = Math.max(colCount, cell.gs$cell.col); 259 | rowCount = Math.max(rowCount, cell.gs$cell.row); 260 | } 261 | 262 | // Set up arrays containing the column data 263 | for (i = 0; i < colCount; i++) { 264 | if (i >= startColumn && i <= endColumn) { 265 | // Create new columns with the length of either end-start or rowCount 266 | columns[i - startColumn] = []; 267 | 268 | // Setting the length to avoid jslint warning 269 | columns[i - startColumn].length = Math.min(rowCount, endRow - startRow); 270 | } 271 | } 272 | 273 | // Loop over the cells and assign the value to the right 274 | // place in the column arrays 275 | for (i = 0; i < cellCount; i++) { 276 | cell = cells[i]; 277 | gr = cell.gs$cell.row - 1; // rows start at 1 278 | gc = cell.gs$cell.col - 1; // columns start at 1 279 | 280 | // If both row and col falls inside start and end 281 | // set the transposed cell value in the newly created columns 282 | if (gc >= startColumn && gc <= endColumn && 283 | gr >= startRow && gr <= endRow) { 284 | columns[gc - startColumn][gr - startRow] = cell.content.$t; 285 | } 286 | } 287 | self.dataFound(); 288 | }); 289 | } 290 | }, 291 | 292 | /** 293 | * Find the header row. For now, we just check whether the first row contains 294 | * numbers or strings. Later we could loop down and find the first row with 295 | * numbers. 296 | */ 297 | findHeaderRow: function () { 298 | var headerRow = 0; 299 | each(this.columns, function (column) { 300 | if (typeof column[0] !== 'string') { 301 | headerRow = null; 302 | } 303 | }); 304 | this.headerRow = 0; 305 | }, 306 | 307 | /** 308 | * Trim a string from whitespace 309 | */ 310 | trim: function (str) { 311 | return typeof str === 'string' ? str.replace(/^\s+|\s+$/g, '') : str; 312 | }, 313 | 314 | /** 315 | * Parse numeric cells in to number types and date types in to true dates. 316 | * @param {Object} columns 317 | */ 318 | parseTypes: function () { 319 | var columns = this.columns, 320 | col = columns.length, 321 | row, 322 | val, 323 | floatVal, 324 | trimVal, 325 | dateVal; 326 | 327 | while (col--) { 328 | row = columns[col].length; 329 | while (row--) { 330 | val = columns[col][row]; 331 | floatVal = parseFloat(val); 332 | trimVal = this.trim(val); 333 | 334 | /*jslint eqeq: true*/ 335 | if (trimVal == floatVal) { // is numeric 336 | /*jslint eqeq: false*/ 337 | columns[col][row] = floatVal; 338 | 339 | // If the number is greater than milliseconds in a year, assume datetime 340 | if (floatVal > 365 * 24 * 3600 * 1000) { 341 | columns[col].isDatetime = true; 342 | } else { 343 | columns[col].isNumeric = true; 344 | } 345 | 346 | } else { // string, continue to determine if it is a date string or really a string 347 | dateVal = this.parseDate(val); 348 | 349 | if (col === 0 && typeof dateVal === 'number' && !isNaN(dateVal)) { // is date 350 | columns[col][row] = dateVal; 351 | columns[col].isDatetime = true; 352 | 353 | } else { // string 354 | columns[col][row] = trimVal === '' ? null : trimVal; 355 | } 356 | } 357 | 358 | } 359 | } 360 | }, 361 | //* 362 | dateFormats: { 363 | 'YYYY-mm-dd': { 364 | regex: '^([0-9]{4})-([0-9]{2})-([0-9]{2})$', 365 | parser: function (match) { 366 | return Date.UTC(+match[1], match[2] - 1, +match[3]); 367 | } 368 | } 369 | }, 370 | // */ 371 | /** 372 | * Parse a date and return it as a number. Overridable through options.parseDate. 373 | */ 374 | parseDate: function (val) { 375 | var parseDate = this.options.parseDate, 376 | ret, 377 | key, 378 | format, 379 | match; 380 | 381 | if (parseDate) { 382 | ret = parseDate(val); 383 | } 384 | 385 | if (typeof val === 'string') { 386 | for (key in this.dateFormats) { 387 | format = this.dateFormats[key]; 388 | match = val.match(format.regex); 389 | if (match) { 390 | ret = format.parser(match); 391 | } 392 | } 393 | } 394 | return ret; 395 | }, 396 | 397 | /** 398 | * Reorganize rows into columns 399 | */ 400 | rowsToColumns: function (rows) { 401 | var row, 402 | rowsLength, 403 | col, 404 | colsLength, 405 | columns; 406 | 407 | if (rows) { 408 | columns = []; 409 | rowsLength = rows.length; 410 | for (row = 0; row < rowsLength; row++) { 411 | colsLength = rows[row].length; 412 | for (col = 0; col < colsLength; col++) { 413 | if (!columns[col]) { 414 | columns[col] = []; 415 | } 416 | columns[col][row] = rows[row][col]; 417 | } 418 | } 419 | } 420 | return columns; 421 | }, 422 | 423 | /** 424 | * A hook for working directly on the parsed columns 425 | */ 426 | parsed: function () { 427 | if (this.options.parsed) { 428 | this.options.parsed.call(this, this.columns); 429 | } 430 | }, 431 | 432 | /** 433 | * If a complete callback function is provided in the options, interpret the 434 | * columns into a Highcharts options object. 435 | */ 436 | complete: function () { 437 | 438 | var columns = this.columns, 439 | hasXData, 440 | categories, 441 | firstCol, 442 | type, 443 | options = this.options, 444 | series, 445 | data, 446 | name, 447 | i, 448 | j; 449 | 450 | 451 | if (options.complete) { 452 | 453 | // Use first column for X data or categories? 454 | if (columns.length > 1) { 455 | firstCol = columns.shift(); 456 | if (this.headerRow === 0) { 457 | firstCol.shift(); // remove the first cell 458 | } 459 | 460 | // Use the first column for categories or X values 461 | hasXData = firstCol.isNumeric || firstCol.isDatetime; 462 | if (!hasXData) { // means type is neither datetime nor linear 463 | categories = firstCol; 464 | } 465 | 466 | if (firstCol.isDatetime) { 467 | type = 'datetime'; 468 | } 469 | } 470 | 471 | // Use the next columns for series 472 | series = []; 473 | for (i = 0; i < columns.length; i++) { 474 | if (this.headerRow === 0) { 475 | name = columns[i].shift(); 476 | } 477 | data = []; 478 | for (j = 0; j < columns[i].length; j++) { 479 | data[j] = columns[i][j] !== undefined ? 480 | (hasXData ? 481 | [firstCol[j], columns[i][j]] : 482 | columns[i][j] 483 | ) : 484 | null; 485 | } 486 | series[i] = { 487 | name: name, 488 | data: data 489 | }; 490 | } 491 | 492 | // Do the callback 493 | options.complete({ 494 | xAxis: { 495 | categories: categories, 496 | type: type 497 | }, 498 | series: series 499 | }); 500 | } 501 | } 502 | }); 503 | 504 | // Register the Data prototype and data function on Highcharts 505 | Highcharts.Data = Data; 506 | Highcharts.data = function (options) { 507 | return new Data(options); 508 | }; 509 | 510 | // Extend Chart.init so that the Chart constructor accepts a new configuration 511 | // option group, data. 512 | Highcharts.wrap(Highcharts.Chart.prototype, 'init', function (proceed, userOptions, callback) { 513 | var chart = this; 514 | 515 | if (userOptions && userOptions.data) { 516 | Highcharts.data(Highcharts.extend(userOptions.data, { 517 | complete: function (dataOptions) { 518 | 519 | // Merge series configs 520 | if (userOptions.series) { 521 | each(userOptions.series, function (series, i) { 522 | userOptions.series[i] = Highcharts.merge(series, dataOptions.series[i]); 523 | }); 524 | } 525 | 526 | // Do the merge 527 | userOptions = Highcharts.merge(dataOptions, userOptions); 528 | 529 | proceed.call(chart, userOptions, callback); 530 | } 531 | })); 532 | } else { 533 | proceed.call(chart, userOptions, callback); 534 | } 535 | }); 536 | 537 | }(Highcharts)); 538 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/modules/exporting.js: -------------------------------------------------------------------------------- 1 | /* 2 | Highcharts JS v3.0.2 (2013-06-05) 3 | Exporting module 4 | 5 | (c) 2010-2013 Torstein Hønsi 6 | 7 | License: www.highcharts.com/license 8 | */ 9 | (function(e){var y=e.Chart,v=e.addEvent,B=e.removeEvent,m=e.createElement,j=e.discardElement,t=e.css,k=e.merge,r=e.each,p=e.extend,C=Math.max,i=document,z=window,D=e.isTouchDevice,E=e.Renderer.prototype.symbols,s=e.getOptions(),w;p(s.lang,{printChart:"Print chart",downloadPNG:"Download PNG image",downloadJPEG:"Download JPEG image",downloadPDF:"Download PDF document",downloadSVG:"Download SVG vector image",contextButtonTitle:"Chart context menu"});s.navigation={menuStyle:{border:"1px solid #A0A0A0", 10 | background:"#FFFFFF",padding:"5px 0"},menuItemStyle:{padding:"0 10px",background:"none",color:"#303030",fontSize:D?"14px":"11px"},menuItemHoverStyle:{background:"#4572A5",color:"#FFFFFF"},buttonOptions:{symbolFill:"#E0E0E0",symbolSize:14,symbolStroke:"#666",symbolStrokeWidth:3,symbolX:12.5,symbolY:10.5,align:"right",buttonSpacing:3,height:22,theme:{fill:"white",stroke:"none"},verticalAlign:"top",width:24}};s.exporting={type:"image/png",url:"http://export.highcharts.com/",buttons:{contextButton:{symbol:"menu", 11 | _titleKey:"contextButtonTitle",menuItems:[{textKey:"printChart",onclick:function(){this.print()}},{separator:!0},{textKey:"downloadPNG",onclick:function(){this.exportChart()}},{textKey:"downloadJPEG",onclick:function(){this.exportChart({type:"image/jpeg"})}},{textKey:"downloadPDF",onclick:function(){this.exportChart({type:"application/pdf"})}},{textKey:"downloadSVG",onclick:function(){this.exportChart({type:"image/svg+xml"})}}]}}};e.post=function(a,b){var c,d;d=m("form",{method:"post",action:a,enctype:"multipart/form-data"}, 12 | {display:"none"},i.body);for(c in b)m("input",{type:"hidden",name:c,value:b[c]},null,d);d.submit();j(d)};p(y.prototype,{getSVG:function(a){var b=this,c,d,x,g,f=k(b.options,a);if(!i.createElementNS)i.createElementNS=function(a,b){return i.createElement(b)};a=m("div",null,{position:"absolute",top:"-9999em",width:b.chartWidth+"px",height:b.chartHeight+"px"},i.body);d=b.renderTo.style.width;g=b.renderTo.style.height;d=f.exporting.sourceWidth||f.chart.width||/px$/.test(d)&&parseInt(d,10)||600;g=f.exporting.sourceHeight|| 13 | f.chart.height||/px$/.test(g)&&parseInt(g,10)||400;p(f.chart,{animation:!1,renderTo:a,forExport:!0,width:d,height:g});f.exporting.enabled=!1;f.series=[];r(b.series,function(a){x=k(a.options,{animation:!1,showCheckbox:!1,visible:a.visible});x.isInternal||f.series.push(x)});c=new e.Chart(f,b.callback);r(["xAxis","yAxis"],function(a){r(b[a],function(b,d){var f=c[a][d],e=b.getExtremes(),g=e.userMin,e=e.userMax;(g!==void 0||e!==void 0)&&f.setExtremes(g,e,!0,!1)})});d=c.container.innerHTML;f=null;c.destroy(); 14 | j(a);d=d.replace(/zIndex="[^"]+"/g,"").replace(/isShadow="[^"]+"/g,"").replace(/symbolName="[^"]+"/g,"").replace(/jQuery[0-9]+="[^"]+"/g,"").replace(/url\([^#]+#/g,"url(#").replace(/.*?$/,"").replace(/ /g," ").replace(/­/g,"­").replace(//g, 15 | 'xlink:href="$1"/>').replace(/id=([^" >]+)/g,'id="$1"').replace(/class=([^" >]+)/g,'class="$1"').replace(/ transform /g," ").replace(/:(path|rect)/g,"$1").replace(/style="([^"]+)"/g,function(a){return a.toLowerCase()});d=d.replace(/(url\(#highcharts-[0-9]+)"/g,"$1").replace(/"/g,"'");d.match(/ xmlns="/g).length===2&&(d=d.replace(/xmlns="[^"]+"/,""));return d},exportChart:function(a,b){var a=a||{},c=this.options.exporting,c=this.getSVG(k({chart:{borderRadius:0}},c.chartOptions,b,{exporting:{sourceWidth:a.sourceWidth|| 16 | c.sourceWidth,sourceHeight:a.sourceHeight||c.sourceHeight}})),a=k(this.options.exporting,a);e.post(a.url,{filename:a.filename||"chart",type:a.type,width:a.width||0,scale:a.scale||2,svg:c})},print:function(){var a=this,b=a.container,c=[],d=b.parentNode,e=i.body,g=e.childNodes;if(!a.isPrinting)a.isPrinting=!0,r(g,function(a,b){if(a.nodeType===1)c[b]=a.style.display,a.style.display="none"}),e.appendChild(b),z.focus(),z.print(),setTimeout(function(){d.appendChild(b);r(g,function(a,b){if(a.nodeType=== 17 | 1)a.style.display=c[b]});a.isPrinting=!1},1E3)},contextMenu:function(a,b,c,d,e,g,f){var h=this,q=h.options.navigation,n=q.menuItemStyle,o=h.chartWidth,i=h.chartHeight,A="cache-"+a,l=h[A],k=C(e,g),u,j,s;if(!l)h[A]=l=m("div",{className:"highcharts-"+a},{position:"absolute",zIndex:1E3,padding:k+"px"},h.container),u=m("div",null,p({MozBoxShadow:"3px 3px 10px #888",WebkitBoxShadow:"3px 3px 10px #888",boxShadow:"3px 3px 10px #888"},q.menuStyle),l),j=function(){t(l,{display:"none"});f&&f.setState(0);h.openMenu= 18 | !1},v(l,"mouseleave",function(){s=setTimeout(j,500)}),v(l,"mouseenter",function(){clearTimeout(s)}),r(b,function(a){if(a){var b=a.separator?m("hr",null,null,u):m("div",{onmouseover:function(){t(this,q.menuItemHoverStyle)},onmouseout:function(){t(this,n)},onclick:function(){j();a.onclick.apply(h,arguments)},innerHTML:a.text||h.options.lang[a.textKey]},p({cursor:"pointer"},n),u);h.exportDivElements.push(b)}}),h.exportDivElements.push(u,l),h.exportMenuWidth=l.offsetWidth,h.exportMenuHeight=l.offsetHeight; 19 | a={display:"block"};c+h.exportMenuWidth>o?a.right=o-c-e-k+"px":a.left=c-k+"px";d+g+h.exportMenuHeight>i?a.bottom=i-d-k+"px":a.top=d+g-k+"px";t(l,a);h.openMenu=!0},addButton:function(a){var b=this,c=b.renderer,a=k(b.options.navigation.buttonOptions,a),d=a.onclick,i=a.menuItems,g,f,h={stroke:a.symbolStroke,fill:a.symbolFill},q=a.symbolSize||12;if(!b.btnCount)b.btnCount=0;b.btnCount++;if(!b.exportDivElements)b.exportDivElements=[],b.exportSVGElements=[];if(a.enabled!==!1){var n=a.theme,o=n.states,m= 20 | o&&o.hover,o=o&&o.select,j;delete n.states;d?j=function(){d.apply(b,arguments)}:i&&(j=function(){b.contextMenu("contextmenu",i,f.translateX,f.translateY,f.width,f.height,f);f.setState(2)});a.text&&a.symbol?n.paddingLeft=e.pick(n.paddingLeft,25):a.text||p(n,{width:a.width,height:a.height,padding:0});f=c.button(a.text,0,0,j,n,m,o).attr({title:b.options.lang[a._titleKey],"stroke-linecap":"round"});a.symbol&&(g=c.symbol(a.symbol,a.symbolX-q/2,a.symbolY-q/2,q,q).attr(p(h,{"stroke-width":a.symbolStrokeWidth|| 21 | 1,zIndex:1})).add(f));f.add().align(p(a,{width:f.width,x:e.pick(a.x,w)}),!0,"spacingBox");w+=(f.width+a.buttonSpacing)*(a.align==="right"?-1:1);b.exportSVGElements.push(f,g)}},destroyExport:function(a){var a=a.target,b,c;for(b=0;bm-u?s:s+(l-s)*((m-u-i)/(m-u))};this.getX=function(i,a){return b+(a?-1:1)*(r(i)/2+c.dataLabels.distance)};this.center=[b,j,m];this.centerX=b;z(a,function(a){f+=a.y});z(a,function(a){p=null;x=f?a.y/f:0;n=g*m;k=n+x*m;h=r(n);y=b-h/2;A=y+h;h=r(k);o=b- 11 | h/2;t=o+h;n>v?(y=o=b-s/2,A=t=b+s/2):k>v&&(p=k,h=r(v),o=b-h/2,t=o+h,k=v);w=["M",y,n,"L",A,n,t,k];p&&w.push(t,p,o,p);w.push(o,k,"Z");a.shapeType="path";a.shapeArgs={d:w};a.percentage=x*100;a.plotX=b;a.plotY=(n+(p||k))/2;a.tooltipPos=[b,a.plotY];a.slice=C;a.half=q;g+=x});this.setTooltipPoints()},drawPoints:function(){var a=this,f=a.options,d=a.chart.renderer;z(a.data,function(e){var g=e.graphic,c=e.shapeArgs;g?g.animate(c):e.graphic=d.path(c).attr({fill:e.color,stroke:f.borderColor,"stroke-width":f.borderWidth}).add(a.group)})}, 12 | drawDataLabels:function(){var a=this.data,f=this.options.dataLabels.distance,d,e,g,c=a.length,j,b;for(this.center[2]-=2*f;c--;)g=a[c],e=(d=g.half)?1:-1,b=g.plotY,j=this.getX(b,d),g.labelPos=[0,b,j+(f-5)*e,b,j+f*e,b,d?"right":"left",0];q.pie.prototype.drawDataLabels.call(this)}})})(Highcharts); 13 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/modules/funnel.src.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Highcharts funnel module, Beta 4 | * 5 | * (c) 2010-2012 Torstein Hønsi 6 | * 7 | * License: www.highcharts.com/license 8 | */ 9 | 10 | /*global Highcharts */ 11 | (function (Highcharts) { 12 | 13 | 'use strict'; 14 | 15 | // create shortcuts 16 | var defaultOptions = Highcharts.getOptions(), 17 | defaultPlotOptions = defaultOptions.plotOptions, 18 | seriesTypes = Highcharts.seriesTypes, 19 | merge = Highcharts.merge, 20 | noop = function () {}, 21 | each = Highcharts.each; 22 | 23 | // set default options 24 | defaultPlotOptions.funnel = merge(defaultPlotOptions.pie, { 25 | center: ['50%', '50%'], 26 | width: '90%', 27 | neckWidth: '30%', 28 | height: '100%', 29 | neckHeight: '25%', 30 | 31 | dataLabels: { 32 | //position: 'right', 33 | connectorWidth: 1, 34 | connectorColor: '#606060' 35 | }, 36 | size: true, // to avoid adapting to data label size in Pie.drawDataLabels 37 | states: { 38 | select: { 39 | color: '#C0C0C0', 40 | borderColor: '#000000', 41 | shadow: false 42 | } 43 | } 44 | }); 45 | 46 | 47 | seriesTypes.funnel = Highcharts.extendClass(seriesTypes.pie, { 48 | 49 | type: 'funnel', 50 | animate: noop, 51 | 52 | /** 53 | * Overrides the pie translate method 54 | */ 55 | translate: function () { 56 | 57 | var 58 | // Get positions - either an integer or a percentage string must be given 59 | getLength = function (length, relativeTo) { 60 | return (/%$/).test(length) ? 61 | relativeTo * parseInt(length, 10) / 100 : 62 | parseInt(length, 10); 63 | }, 64 | 65 | sum = 0, 66 | series = this, 67 | chart = series.chart, 68 | plotWidth = chart.plotWidth, 69 | plotHeight = chart.plotHeight, 70 | cumulative = 0, // start at top 71 | options = series.options, 72 | center = options.center, 73 | centerX = getLength(center[0], plotWidth), 74 | centerY = getLength(center[0], plotHeight), 75 | width = getLength(options.width, plotWidth), 76 | tempWidth, 77 | getWidthAt, 78 | height = getLength(options.height, plotHeight), 79 | neckWidth = getLength(options.neckWidth, plotWidth), 80 | neckHeight = getLength(options.neckHeight, plotHeight), 81 | neckY = height - neckHeight, 82 | data = series.data, 83 | path, 84 | fraction, 85 | half = options.dataLabels.position === 'left' ? 1 : 0, 86 | 87 | x1, 88 | y1, 89 | x2, 90 | x3, 91 | y3, 92 | x4, 93 | y5; 94 | 95 | // Return the width at a specific y coordinate 96 | series.getWidthAt = getWidthAt = function (y) { 97 | return y > height - neckHeight ? 98 | neckWidth : 99 | neckWidth + (width - neckWidth) * ((height - neckHeight - y) / (height - neckHeight)); 100 | }; 101 | series.getX = function (y, half) { 102 | return centerX + (half ? -1 : 1) * ((getWidthAt(y) / 2) + options.dataLabels.distance); 103 | }; 104 | 105 | // Expose 106 | series.center = [centerX, centerY, height]; 107 | series.centerX = centerX; 108 | 109 | /* 110 | * Individual point coordinate naming: 111 | * 112 | * x1,y1 _________________ x2,y1 113 | * \ / 114 | * \ / 115 | * \ / 116 | * \ / 117 | * \ / 118 | * x3,y3 _________ x4,y3 119 | * 120 | * Additional for the base of the neck: 121 | * 122 | * | | 123 | * | | 124 | * | | 125 | * x3,y5 _________ x4,y5 126 | */ 127 | 128 | 129 | 130 | 131 | // get the total sum 132 | each(data, function (point) { 133 | sum += point.y; 134 | }); 135 | 136 | each(data, function (point) { 137 | // set start and end positions 138 | y5 = null; 139 | fraction = sum ? point.y / sum : 0; 140 | y1 = cumulative * height; 141 | y3 = y1 + fraction * height; 142 | //tempWidth = neckWidth + (width - neckWidth) * ((height - neckHeight - y1) / (height - neckHeight)); 143 | tempWidth = getWidthAt(y1); 144 | x1 = centerX - tempWidth / 2; 145 | x2 = x1 + tempWidth; 146 | tempWidth = getWidthAt(y3); 147 | x3 = centerX - tempWidth / 2; 148 | x4 = x3 + tempWidth; 149 | 150 | // the entire point is within the neck 151 | if (y1 > neckY) { 152 | x1 = x3 = centerX - neckWidth / 2; 153 | x2 = x4 = centerX + neckWidth / 2; 154 | 155 | // the base of the neck 156 | } else if (y3 > neckY) { 157 | y5 = y3; 158 | 159 | tempWidth = getWidthAt(neckY); 160 | x3 = centerX - tempWidth / 2; 161 | x4 = x3 + tempWidth; 162 | 163 | y3 = neckY; 164 | } 165 | 166 | // save the path 167 | path = [ 168 | 'M', 169 | x1, y1, 170 | 'L', 171 | x2, y1, 172 | x4, y3 173 | ]; 174 | if (y5) { 175 | path.push(x4, y5, x3, y5); 176 | } 177 | path.push(x3, y3, 'Z'); 178 | 179 | // prepare for using shared dr 180 | point.shapeType = 'path'; 181 | point.shapeArgs = { d: path }; 182 | 183 | 184 | // for tooltips and data labels 185 | point.percentage = fraction * 100; 186 | point.plotX = centerX; 187 | point.plotY = (y1 + (y5 || y3)) / 2; 188 | 189 | // Placement of tooltips and data labels 190 | point.tooltipPos = [ 191 | centerX, 192 | point.plotY 193 | ]; 194 | 195 | // Slice is a noop on funnel points 196 | point.slice = noop; 197 | 198 | // Mimicking pie data label placement logic 199 | point.half = half; 200 | 201 | cumulative += fraction; 202 | }); 203 | 204 | 205 | series.setTooltipPoints(); 206 | }, 207 | /** 208 | * Draw a single point (wedge) 209 | * @param {Object} point The point object 210 | * @param {Object} color The color of the point 211 | * @param {Number} brightness The brightness relative to the color 212 | */ 213 | drawPoints: function () { 214 | var series = this, 215 | options = series.options, 216 | chart = series.chart, 217 | renderer = chart.renderer; 218 | 219 | each(series.data, function (point) { 220 | 221 | var graphic = point.graphic, 222 | shapeArgs = point.shapeArgs; 223 | 224 | if (!graphic) { // Create the shapes 225 | point.graphic = renderer.path(shapeArgs). 226 | attr({ 227 | fill: point.color, 228 | stroke: options.borderColor, 229 | 'stroke-width': options.borderWidth 230 | }). 231 | add(series.group); 232 | 233 | } else { // Update the shapes 234 | graphic.animate(shapeArgs); 235 | } 236 | }); 237 | }, 238 | 239 | /** 240 | * Extend the pie data label method 241 | */ 242 | drawDataLabels: function () { 243 | var data = this.data, 244 | labelDistance = this.options.dataLabels.distance, 245 | leftSide, 246 | sign, 247 | point, 248 | i = data.length, 249 | x, 250 | y; 251 | 252 | // In the original pie label anticollision logic, the slots are distributed 253 | // from one labelDistance above to one labelDistance below the pie. In funnels 254 | // we don't want this. 255 | this.center[2] -= 2 * labelDistance; 256 | 257 | // Set the label position array for each point. 258 | while (i--) { 259 | point = data[i]; 260 | leftSide = point.half; 261 | sign = leftSide ? 1 : -1; 262 | y = point.plotY; 263 | x = this.getX(y, leftSide); 264 | 265 | // set the anchor point for data labels 266 | point.labelPos = [ 267 | 0, // first break of connector 268 | y, // a/a 269 | x + (labelDistance - 5) * sign, // second break, right outside point shape 270 | y, // a/a 271 | x + labelDistance * sign, // landing point for connector 272 | y, // a/a 273 | leftSide ? 'right' : 'left', // alignment 274 | 0 // center angle 275 | ]; 276 | } 277 | 278 | seriesTypes.pie.prototype.drawDataLabels.call(this); 279 | } 280 | 281 | }); 282 | 283 | 284 | }(Highcharts)); 285 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/themes/dark-blue.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Dark blue theme for Highcharts JS 3 | * @author Torstein Hønsi 4 | */ 5 | 6 | Highcharts.theme = { 7 | colors: ["#DDDF0D", "#55BF3B", "#DF5353", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee", 8 | "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], 9 | chart: { 10 | backgroundColor: { 11 | linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 }, 12 | stops: [ 13 | [0, 'rgb(48, 48, 96)'], 14 | [1, 'rgb(0, 0, 0)'] 15 | ] 16 | }, 17 | borderColor: '#000000', 18 | borderWidth: 2, 19 | className: 'dark-container', 20 | plotBackgroundColor: 'rgba(255, 255, 255, .1)', 21 | plotBorderColor: '#CCCCCC', 22 | plotBorderWidth: 1 23 | }, 24 | title: { 25 | style: { 26 | color: '#C0C0C0', 27 | font: 'bold 16px "Trebuchet MS", Verdana, sans-serif' 28 | } 29 | }, 30 | subtitle: { 31 | style: { 32 | color: '#666666', 33 | font: 'bold 12px "Trebuchet MS", Verdana, sans-serif' 34 | } 35 | }, 36 | xAxis: { 37 | gridLineColor: '#333333', 38 | gridLineWidth: 1, 39 | labels: { 40 | style: { 41 | color: '#A0A0A0' 42 | } 43 | }, 44 | lineColor: '#A0A0A0', 45 | tickColor: '#A0A0A0', 46 | title: { 47 | style: { 48 | color: '#CCC', 49 | fontWeight: 'bold', 50 | fontSize: '12px', 51 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 52 | 53 | } 54 | } 55 | }, 56 | yAxis: { 57 | gridLineColor: '#333333', 58 | labels: { 59 | style: { 60 | color: '#A0A0A0' 61 | } 62 | }, 63 | lineColor: '#A0A0A0', 64 | minorTickInterval: null, 65 | tickColor: '#A0A0A0', 66 | tickWidth: 1, 67 | title: { 68 | style: { 69 | color: '#CCC', 70 | fontWeight: 'bold', 71 | fontSize: '12px', 72 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 73 | } 74 | } 75 | }, 76 | tooltip: { 77 | backgroundColor: 'rgba(0, 0, 0, 0.75)', 78 | style: { 79 | color: '#F0F0F0' 80 | } 81 | }, 82 | toolbar: { 83 | itemStyle: { 84 | color: 'silver' 85 | } 86 | }, 87 | plotOptions: { 88 | line: { 89 | dataLabels: { 90 | color: '#CCC' 91 | }, 92 | marker: { 93 | lineColor: '#333' 94 | } 95 | }, 96 | spline: { 97 | marker: { 98 | lineColor: '#333' 99 | } 100 | }, 101 | scatter: { 102 | marker: { 103 | lineColor: '#333' 104 | } 105 | }, 106 | candlestick: { 107 | lineColor: 'white' 108 | } 109 | }, 110 | legend: { 111 | itemStyle: { 112 | font: '9pt Trebuchet MS, Verdana, sans-serif', 113 | color: '#A0A0A0' 114 | }, 115 | itemHoverStyle: { 116 | color: '#FFF' 117 | }, 118 | itemHiddenStyle: { 119 | color: '#444' 120 | } 121 | }, 122 | credits: { 123 | style: { 124 | color: '#666' 125 | } 126 | }, 127 | labels: { 128 | style: { 129 | color: '#CCC' 130 | } 131 | }, 132 | 133 | navigation: { 134 | buttonOptions: { 135 | symbolStroke: '#DDDDDD', 136 | hoverSymbolStroke: '#FFFFFF', 137 | theme: { 138 | fill: { 139 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 140 | stops: [ 141 | [0.4, '#606060'], 142 | [0.6, '#333333'] 143 | ] 144 | }, 145 | stroke: '#000000' 146 | } 147 | } 148 | }, 149 | 150 | // scroll charts 151 | rangeSelector: { 152 | buttonTheme: { 153 | fill: { 154 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 155 | stops: [ 156 | [0.4, '#888'], 157 | [0.6, '#555'] 158 | ] 159 | }, 160 | stroke: '#000000', 161 | style: { 162 | color: '#CCC', 163 | fontWeight: 'bold' 164 | }, 165 | states: { 166 | hover: { 167 | fill: { 168 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 169 | stops: [ 170 | [0.4, '#BBB'], 171 | [0.6, '#888'] 172 | ] 173 | }, 174 | stroke: '#000000', 175 | style: { 176 | color: 'white' 177 | } 178 | }, 179 | select: { 180 | fill: { 181 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 182 | stops: [ 183 | [0.1, '#000'], 184 | [0.3, '#333'] 185 | ] 186 | }, 187 | stroke: '#000000', 188 | style: { 189 | color: 'yellow' 190 | } 191 | } 192 | } 193 | }, 194 | inputStyle: { 195 | backgroundColor: '#333', 196 | color: 'silver' 197 | }, 198 | labelStyle: { 199 | color: 'silver' 200 | } 201 | }, 202 | 203 | navigator: { 204 | handles: { 205 | backgroundColor: '#666', 206 | borderColor: '#AAA' 207 | }, 208 | outlineColor: '#CCC', 209 | maskFill: 'rgba(16, 16, 16, 0.5)', 210 | series: { 211 | color: '#7798BF', 212 | lineColor: '#A6C7ED' 213 | } 214 | }, 215 | 216 | scrollbar: { 217 | barBackgroundColor: { 218 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 219 | stops: [ 220 | [0.4, '#888'], 221 | [0.6, '#555'] 222 | ] 223 | }, 224 | barBorderColor: '#CCC', 225 | buttonArrowColor: '#CCC', 226 | buttonBackgroundColor: { 227 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 228 | stops: [ 229 | [0.4, '#888'], 230 | [0.6, '#555'] 231 | ] 232 | }, 233 | buttonBorderColor: '#CCC', 234 | rifleColor: '#FFF', 235 | trackBackgroundColor: { 236 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 237 | stops: [ 238 | [0, '#000'], 239 | [1, '#333'] 240 | ] 241 | }, 242 | trackBorderColor: '#666' 243 | }, 244 | 245 | // special colors for some of the 246 | legendBackgroundColor: 'rgba(0, 0, 0, 0.5)', 247 | legendBackgroundColorSolid: 'rgb(35, 35, 70)', 248 | dataLabelsColor: '#444', 249 | textColor: '#C0C0C0', 250 | maskColor: 'rgba(255,255,255,0.3)' 251 | }; 252 | 253 | // Apply the theme 254 | var highchartsOptions = Highcharts.setOptions(Highcharts.theme); 255 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/themes/dark-green.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Dark blue theme for Highcharts JS 3 | * @author Torstein Hønsi 4 | */ 5 | 6 | Highcharts.theme = { 7 | colors: ["#DDDF0D", "#55BF3B", "#DF5353", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee", 8 | "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], 9 | chart: { 10 | backgroundColor: { 11 | linearGradient: [0, 0, 250, 500], 12 | stops: [ 13 | [0, 'rgb(48, 96, 48)'], 14 | [1, 'rgb(0, 0, 0)'] 15 | ] 16 | }, 17 | borderColor: '#000000', 18 | borderWidth: 2, 19 | className: 'dark-container', 20 | plotBackgroundColor: 'rgba(255, 255, 255, .1)', 21 | plotBorderColor: '#CCCCCC', 22 | plotBorderWidth: 1 23 | }, 24 | title: { 25 | style: { 26 | color: '#C0C0C0', 27 | font: 'bold 16px "Trebuchet MS", Verdana, sans-serif' 28 | } 29 | }, 30 | subtitle: { 31 | style: { 32 | color: '#666666', 33 | font: 'bold 12px "Trebuchet MS", Verdana, sans-serif' 34 | } 35 | }, 36 | xAxis: { 37 | gridLineColor: '#333333', 38 | gridLineWidth: 1, 39 | labels: { 40 | style: { 41 | color: '#A0A0A0' 42 | } 43 | }, 44 | lineColor: '#A0A0A0', 45 | tickColor: '#A0A0A0', 46 | title: { 47 | style: { 48 | color: '#CCC', 49 | fontWeight: 'bold', 50 | fontSize: '12px', 51 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 52 | 53 | } 54 | } 55 | }, 56 | yAxis: { 57 | gridLineColor: '#333333', 58 | labels: { 59 | style: { 60 | color: '#A0A0A0' 61 | } 62 | }, 63 | lineColor: '#A0A0A0', 64 | minorTickInterval: null, 65 | tickColor: '#A0A0A0', 66 | tickWidth: 1, 67 | title: { 68 | style: { 69 | color: '#CCC', 70 | fontWeight: 'bold', 71 | fontSize: '12px', 72 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 73 | } 74 | } 75 | }, 76 | tooltip: { 77 | backgroundColor: 'rgba(0, 0, 0, 0.75)', 78 | style: { 79 | color: '#F0F0F0' 80 | } 81 | }, 82 | toolbar: { 83 | itemStyle: { 84 | color: 'silver' 85 | } 86 | }, 87 | plotOptions: { 88 | line: { 89 | dataLabels: { 90 | color: '#CCC' 91 | }, 92 | marker: { 93 | lineColor: '#333' 94 | } 95 | }, 96 | spline: { 97 | marker: { 98 | lineColor: '#333' 99 | } 100 | }, 101 | scatter: { 102 | marker: { 103 | lineColor: '#333' 104 | } 105 | }, 106 | candlestick: { 107 | lineColor: 'white' 108 | } 109 | }, 110 | legend: { 111 | itemStyle: { 112 | font: '9pt Trebuchet MS, Verdana, sans-serif', 113 | color: '#A0A0A0' 114 | }, 115 | itemHoverStyle: { 116 | color: '#FFF' 117 | }, 118 | itemHiddenStyle: { 119 | color: '#444' 120 | } 121 | }, 122 | credits: { 123 | style: { 124 | color: '#666' 125 | } 126 | }, 127 | labels: { 128 | style: { 129 | color: '#CCC' 130 | } 131 | }, 132 | 133 | 134 | navigation: { 135 | buttonOptions: { 136 | symbolStroke: '#DDDDDD', 137 | hoverSymbolStroke: '#FFFFFF', 138 | theme: { 139 | fill: { 140 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 141 | stops: [ 142 | [0.4, '#606060'], 143 | [0.6, '#333333'] 144 | ] 145 | }, 146 | stroke: '#000000' 147 | } 148 | } 149 | }, 150 | 151 | // scroll charts 152 | rangeSelector: { 153 | buttonTheme: { 154 | fill: { 155 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 156 | stops: [ 157 | [0.4, '#888'], 158 | [0.6, '#555'] 159 | ] 160 | }, 161 | stroke: '#000000', 162 | style: { 163 | color: '#CCC', 164 | fontWeight: 'bold' 165 | }, 166 | states: { 167 | hover: { 168 | fill: { 169 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 170 | stops: [ 171 | [0.4, '#BBB'], 172 | [0.6, '#888'] 173 | ] 174 | }, 175 | stroke: '#000000', 176 | style: { 177 | color: 'white' 178 | } 179 | }, 180 | select: { 181 | fill: { 182 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 183 | stops: [ 184 | [0.1, '#000'], 185 | [0.3, '#333'] 186 | ] 187 | }, 188 | stroke: '#000000', 189 | style: { 190 | color: 'yellow' 191 | } 192 | } 193 | } 194 | }, 195 | inputStyle: { 196 | backgroundColor: '#333', 197 | color: 'silver' 198 | }, 199 | labelStyle: { 200 | color: 'silver' 201 | } 202 | }, 203 | 204 | navigator: { 205 | handles: { 206 | backgroundColor: '#666', 207 | borderColor: '#AAA' 208 | }, 209 | outlineColor: '#CCC', 210 | maskFill: 'rgba(16, 16, 16, 0.5)', 211 | series: { 212 | color: '#7798BF', 213 | lineColor: '#A6C7ED' 214 | } 215 | }, 216 | 217 | scrollbar: { 218 | barBackgroundColor: { 219 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 220 | stops: [ 221 | [0.4, '#888'], 222 | [0.6, '#555'] 223 | ] 224 | }, 225 | barBorderColor: '#CCC', 226 | buttonArrowColor: '#CCC', 227 | buttonBackgroundColor: { 228 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 229 | stops: [ 230 | [0.4, '#888'], 231 | [0.6, '#555'] 232 | ] 233 | }, 234 | buttonBorderColor: '#CCC', 235 | rifleColor: '#FFF', 236 | trackBackgroundColor: { 237 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 238 | stops: [ 239 | [0, '#000'], 240 | [1, '#333'] 241 | ] 242 | }, 243 | trackBorderColor: '#666' 244 | }, 245 | 246 | // special colors for some of the 247 | legendBackgroundColor: 'rgba(0, 0, 0, 0.5)', 248 | legendBackgroundColorSolid: 'rgb(35, 35, 70)', 249 | dataLabelsColor: '#444', 250 | textColor: '#C0C0C0', 251 | maskColor: 'rgba(255,255,255,0.3)' 252 | }; 253 | 254 | // Apply the theme 255 | var highchartsOptions = Highcharts.setOptions(Highcharts.theme); 256 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/themes/gray.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gray theme for Highcharts JS 3 | * @author Torstein Hønsi 4 | */ 5 | 6 | Highcharts.theme = { 7 | colors: ["#DDDF0D", "#7798BF", "#55BF3B", "#DF5353", "#aaeeee", "#ff0066", "#eeaaee", 8 | "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], 9 | chart: { 10 | backgroundColor: { 11 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 12 | stops: [ 13 | [0, 'rgb(96, 96, 96)'], 14 | [1, 'rgb(16, 16, 16)'] 15 | ] 16 | }, 17 | borderWidth: 0, 18 | borderRadius: 15, 19 | plotBackgroundColor: null, 20 | plotShadow: false, 21 | plotBorderWidth: 0 22 | }, 23 | title: { 24 | style: { 25 | color: '#FFF', 26 | font: '16px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 27 | } 28 | }, 29 | subtitle: { 30 | style: { 31 | color: '#DDD', 32 | font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 33 | } 34 | }, 35 | xAxis: { 36 | gridLineWidth: 0, 37 | lineColor: '#999', 38 | tickColor: '#999', 39 | labels: { 40 | style: { 41 | color: '#999', 42 | fontWeight: 'bold' 43 | } 44 | }, 45 | title: { 46 | style: { 47 | color: '#AAA', 48 | font: 'bold 12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 49 | } 50 | } 51 | }, 52 | yAxis: { 53 | alternateGridColor: null, 54 | minorTickInterval: null, 55 | gridLineColor: 'rgba(255, 255, 255, .1)', 56 | minorGridLineColor: 'rgba(255,255,255,0.07)', 57 | lineWidth: 0, 58 | tickWidth: 0, 59 | labels: { 60 | style: { 61 | color: '#999', 62 | fontWeight: 'bold' 63 | } 64 | }, 65 | title: { 66 | style: { 67 | color: '#AAA', 68 | font: 'bold 12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 69 | } 70 | } 71 | }, 72 | legend: { 73 | itemStyle: { 74 | color: '#CCC' 75 | }, 76 | itemHoverStyle: { 77 | color: '#FFF' 78 | }, 79 | itemHiddenStyle: { 80 | color: '#333' 81 | } 82 | }, 83 | labels: { 84 | style: { 85 | color: '#CCC' 86 | } 87 | }, 88 | tooltip: { 89 | backgroundColor: { 90 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 91 | stops: [ 92 | [0, 'rgba(96, 96, 96, .8)'], 93 | [1, 'rgba(16, 16, 16, .8)'] 94 | ] 95 | }, 96 | borderWidth: 0, 97 | style: { 98 | color: '#FFF' 99 | } 100 | }, 101 | 102 | 103 | plotOptions: { 104 | series: { 105 | shadow: true 106 | }, 107 | line: { 108 | dataLabels: { 109 | color: '#CCC' 110 | }, 111 | marker: { 112 | lineColor: '#333' 113 | } 114 | }, 115 | spline: { 116 | marker: { 117 | lineColor: '#333' 118 | } 119 | }, 120 | scatter: { 121 | marker: { 122 | lineColor: '#333' 123 | } 124 | }, 125 | candlestick: { 126 | lineColor: 'white' 127 | } 128 | }, 129 | 130 | toolbar: { 131 | itemStyle: { 132 | color: '#CCC' 133 | } 134 | }, 135 | 136 | navigation: { 137 | buttonOptions: { 138 | symbolStroke: '#DDDDDD', 139 | hoverSymbolStroke: '#FFFFFF', 140 | theme: { 141 | fill: { 142 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 143 | stops: [ 144 | [0.4, '#606060'], 145 | [0.6, '#333333'] 146 | ] 147 | }, 148 | stroke: '#000000' 149 | } 150 | } 151 | }, 152 | 153 | // scroll charts 154 | rangeSelector: { 155 | buttonTheme: { 156 | fill: { 157 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 158 | stops: [ 159 | [0.4, '#888'], 160 | [0.6, '#555'] 161 | ] 162 | }, 163 | stroke: '#000000', 164 | style: { 165 | color: '#CCC', 166 | fontWeight: 'bold' 167 | }, 168 | states: { 169 | hover: { 170 | fill: { 171 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 172 | stops: [ 173 | [0.4, '#BBB'], 174 | [0.6, '#888'] 175 | ] 176 | }, 177 | stroke: '#000000', 178 | style: { 179 | color: 'white' 180 | } 181 | }, 182 | select: { 183 | fill: { 184 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 185 | stops: [ 186 | [0.1, '#000'], 187 | [0.3, '#333'] 188 | ] 189 | }, 190 | stroke: '#000000', 191 | style: { 192 | color: 'yellow' 193 | } 194 | } 195 | } 196 | }, 197 | inputStyle: { 198 | backgroundColor: '#333', 199 | color: 'silver' 200 | }, 201 | labelStyle: { 202 | color: 'silver' 203 | } 204 | }, 205 | 206 | navigator: { 207 | handles: { 208 | backgroundColor: '#666', 209 | borderColor: '#AAA' 210 | }, 211 | outlineColor: '#CCC', 212 | maskFill: 'rgba(16, 16, 16, 0.5)', 213 | series: { 214 | color: '#7798BF', 215 | lineColor: '#A6C7ED' 216 | } 217 | }, 218 | 219 | scrollbar: { 220 | barBackgroundColor: { 221 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 222 | stops: [ 223 | [0.4, '#888'], 224 | [0.6, '#555'] 225 | ] 226 | }, 227 | barBorderColor: '#CCC', 228 | buttonArrowColor: '#CCC', 229 | buttonBackgroundColor: { 230 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 231 | stops: [ 232 | [0.4, '#888'], 233 | [0.6, '#555'] 234 | ] 235 | }, 236 | buttonBorderColor: '#CCC', 237 | rifleColor: '#FFF', 238 | trackBackgroundColor: { 239 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 240 | stops: [ 241 | [0, '#000'], 242 | [1, '#333'] 243 | ] 244 | }, 245 | trackBorderColor: '#666' 246 | }, 247 | 248 | // special colors for some of the demo examples 249 | legendBackgroundColor: 'rgba(48, 48, 48, 0.8)', 250 | legendBackgroundColorSolid: 'rgb(70, 70, 70)', 251 | dataLabelsColor: '#444', 252 | textColor: '#E0E0E0', 253 | maskColor: 'rgba(255,255,255,0.3)' 254 | }; 255 | 256 | // Apply the theme 257 | var highchartsOptions = Highcharts.setOptions(Highcharts.theme); 258 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/themes/grid.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Grid theme for Highcharts JS 3 | * @author Torstein Hønsi 4 | */ 5 | 6 | Highcharts.theme = { 7 | colors: ['#058DC7', '#50B432', '#ED561B', '#DDDF00', '#24CBE5', '#64E572', '#FF9655', '#FFF263', '#6AF9C4'], 8 | chart: { 9 | backgroundColor: { 10 | linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 }, 11 | stops: [ 12 | [0, 'rgb(255, 255, 255)'], 13 | [1, 'rgb(240, 240, 255)'] 14 | ] 15 | }, 16 | borderWidth: 2, 17 | plotBackgroundColor: 'rgba(255, 255, 255, .9)', 18 | plotShadow: true, 19 | plotBorderWidth: 1 20 | }, 21 | title: { 22 | style: { 23 | color: '#000', 24 | font: 'bold 16px "Trebuchet MS", Verdana, sans-serif' 25 | } 26 | }, 27 | subtitle: { 28 | style: { 29 | color: '#666666', 30 | font: 'bold 12px "Trebuchet MS", Verdana, sans-serif' 31 | } 32 | }, 33 | xAxis: { 34 | gridLineWidth: 1, 35 | lineColor: '#000', 36 | tickColor: '#000', 37 | labels: { 38 | style: { 39 | color: '#000', 40 | font: '11px Trebuchet MS, Verdana, sans-serif' 41 | } 42 | }, 43 | title: { 44 | style: { 45 | color: '#333', 46 | fontWeight: 'bold', 47 | fontSize: '12px', 48 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 49 | 50 | } 51 | } 52 | }, 53 | yAxis: { 54 | minorTickInterval: 'auto', 55 | lineColor: '#000', 56 | lineWidth: 1, 57 | tickWidth: 1, 58 | tickColor: '#000', 59 | labels: { 60 | style: { 61 | color: '#000', 62 | font: '11px Trebuchet MS, Verdana, sans-serif' 63 | } 64 | }, 65 | title: { 66 | style: { 67 | color: '#333', 68 | fontWeight: 'bold', 69 | fontSize: '12px', 70 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 71 | } 72 | } 73 | }, 74 | legend: { 75 | itemStyle: { 76 | font: '9pt Trebuchet MS, Verdana, sans-serif', 77 | color: 'black' 78 | 79 | }, 80 | itemHoverStyle: { 81 | color: '#039' 82 | }, 83 | itemHiddenStyle: { 84 | color: 'gray' 85 | } 86 | }, 87 | labels: { 88 | style: { 89 | color: '#99b' 90 | } 91 | }, 92 | 93 | navigation: { 94 | buttonOptions: { 95 | theme: { 96 | stroke: '#CCCCCC' 97 | } 98 | } 99 | } 100 | }; 101 | 102 | // Apply the theme 103 | var highchartsOptions = Highcharts.setOptions(Highcharts.theme); 104 | -------------------------------------------------------------------------------- /chartjs/static/js/highcharts/themes/skies.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Skies theme for Highcharts JS 3 | * @author Torstein Hønsi 4 | */ 5 | 6 | Highcharts.theme = { 7 | colors: ["#514F78", "#42A07B", "#9B5E4A", "#72727F", "#1F949A", "#82914E", "#86777F", "#42A07B"], 8 | chart: { 9 | className: 'skies', 10 | borderWidth: 0, 11 | plotShadow: true, 12 | plotBackgroundImage: '/demo/gfx/skies.jpg', 13 | plotBackgroundColor: { 14 | linearGradient: [0, 0, 250, 500], 15 | stops: [ 16 | [0, 'rgba(255, 255, 255, 1)'], 17 | [1, 'rgba(255, 255, 255, 0)'] 18 | ] 19 | }, 20 | plotBorderWidth: 1 21 | }, 22 | title: { 23 | style: { 24 | color: '#3E576F', 25 | font: '16px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 26 | } 27 | }, 28 | subtitle: { 29 | style: { 30 | color: '#6D869F', 31 | font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 32 | } 33 | }, 34 | xAxis: { 35 | gridLineWidth: 0, 36 | lineColor: '#C0D0E0', 37 | tickColor: '#C0D0E0', 38 | labels: { 39 | style: { 40 | color: '#666', 41 | fontWeight: 'bold' 42 | } 43 | }, 44 | title: { 45 | style: { 46 | color: '#666', 47 | font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 48 | } 49 | } 50 | }, 51 | yAxis: { 52 | alternateGridColor: 'rgba(255, 255, 255, .5)', 53 | lineColor: '#C0D0E0', 54 | tickColor: '#C0D0E0', 55 | tickWidth: 1, 56 | labels: { 57 | style: { 58 | color: '#666', 59 | fontWeight: 'bold' 60 | } 61 | }, 62 | title: { 63 | style: { 64 | color: '#666', 65 | font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 66 | } 67 | } 68 | }, 69 | legend: { 70 | itemStyle: { 71 | font: '9pt Trebuchet MS, Verdana, sans-serif', 72 | color: '#3E576F' 73 | }, 74 | itemHoverStyle: { 75 | color: 'black' 76 | }, 77 | itemHiddenStyle: { 78 | color: 'silver' 79 | } 80 | }, 81 | labels: { 82 | style: { 83 | color: '#3E576F' 84 | } 85 | } 86 | }; 87 | 88 | // Apply the theme 89 | var highchartsOptions = Highcharts.setOptions(Highcharts.theme); 90 | -------------------------------------------------------------------------------- /chartjs/util.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | NULL = "null" 4 | 5 | 6 | def str_to_datetime_date(datestr): 7 | """Convert a 'yyyy-mm-dd' string to a datetime.date object. 8 | """ 9 | return datetime.datetime.strptime(datestr, "%Y-%m-%d").date() 10 | 11 | 12 | def date_range(start_date, end_date, inclusive=True): 13 | """Generate a sequence of datetime.date objects. 14 | 15 | Arguments: 16 | start_date (datetime.date object or `yyyy-mm-dd'): Start date. 17 | end_date (datetime.date object or 'yyyy-mm-dd'): End date. 18 | inclusive (boolean): Whether or not to include `end_date` in the range. 19 | 20 | Yields: 21 | datetime.date object 22 | """ 23 | if type(start_date) == str: 24 | start_date = str_to_datetime_date(start_date) 25 | if type(end_date) == str: 26 | end_date = str_to_datetime_date(end_date) 27 | number_of_days = int((end_date - start_date).days) 28 | if inclusive: 29 | number_of_days += 1 30 | for days in range(number_of_days): 31 | yield start_date + datetime.timedelta(days) 32 | 33 | 34 | def value_or_null(start_date, end_date, queryset, date_attr, value_attr): 35 | """Generate a sequence of numbers or 'null' strings. 36 | 37 | This generator is used when you want to plot non-continuous datestamped data. For 38 | example, you might have data spanning three weeks, but you're missing data for the 39 | weekends, and you want to express that those data points are missing. If a queryset 40 | doesn't have a value for a particular date, then this generator yields a 'null'. 41 | 42 | Arguments: 43 | start_date (datetime.date object or 'yyyy-mm-dd'): Start date. 44 | end_date: (datetime.date object or 'yyyy-mm-dd'): End date. 45 | queryset (QuerySet): Django queryset that we're interested in. 46 | date_attr (str): Name of date attribute of `queryset`. 47 | value_attr (str): Name of numerical value of interest of `queryset`. 48 | 49 | Yields: 50 | numeric value or 'null' string 51 | """ 52 | if type(start_date) == str: 53 | start_date = str_to_datetime_date(start_date) 54 | if type(end_date) == str: 55 | end_date = str_to_datetime_date(end_date) 56 | for new_date in date_range(start_date, end_date): 57 | query = { 58 | date_attr + "__year": new_date.year, 59 | date_attr + "__month": new_date.month, 60 | date_attr + "__day": new_date.day, 61 | } 62 | items = queryset.filter(**query) 63 | if items.count() == 1: 64 | yield vars(items.first())[value_attr] 65 | else: 66 | yield NULL 67 | -------------------------------------------------------------------------------- /chartjs/views/__init__.py: -------------------------------------------------------------------------------- 1 | from ..colors import next_color 2 | from .base import JSONView 3 | 4 | 5 | class HighChartsView(JSONView): 6 | title = None 7 | y_axis_title = None 8 | credits = {"enabled": True} 9 | 10 | def get_colors(self): 11 | return next_color() 12 | 13 | def get_legend(self): 14 | return {} 15 | 16 | def get_plot_options(self): 17 | return {} 18 | 19 | def get_context_data(self, **kwargs): 20 | context = super(HighChartsView, self).get_context_data(**kwargs) 21 | context.update( 22 | { 23 | "title": {"text": str(self.title)}, 24 | "plotOptions": self.get_plot_options(), 25 | "legend": self.get_legend(), 26 | "credits": self.credits, 27 | } 28 | ) 29 | return context 30 | 31 | def get_data(self): 32 | raise NotImplementedError( # pragma: no cover 33 | "You should return a data list list. " "(i.e: [[25, 34, 0, 1, 50], ...])." 34 | ) 35 | 36 | def get_series(self): 37 | series = [] 38 | color_generator = self.get_colors() 39 | data = self.get_data() 40 | providers = self.get_providers() 41 | num = len(providers) 42 | for i, data in enumerate(self.get_data()): 43 | color = tuple(next(color_generator)) 44 | serie = {"color": "rgba(%d, %d, %d, 1)" % color, "data": data} 45 | if i < num: 46 | serie["name"] = providers[i] 47 | series.append(serie) 48 | return series 49 | -------------------------------------------------------------------------------- /chartjs/views/base.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django import http 4 | from django.views.generic import TemplateView 5 | 6 | 7 | class ComplexEncoder(json.JSONEncoder): 8 | """Always return JSON primitive.""" 9 | 10 | def default(self, obj): 11 | try: 12 | return super(ComplexEncoder, obj).default(obj) 13 | except TypeError: 14 | if hasattr(obj, "pk"): 15 | return obj.pk 16 | return str(obj) 17 | 18 | 19 | class JSONResponseMixin(object): 20 | def render_to_response(self, context): 21 | "Returns a JSON response containing 'context' as payload" 22 | return self.get_json_response(self.convert_context_to_json(context)) 23 | 24 | def get_json_response(self, content, **httpresponse_kwargs): 25 | "Construct an `HttpResponse` object." 26 | return http.HttpResponse( 27 | content, content_type="application/json", **httpresponse_kwargs 28 | ) 29 | 30 | def convert_context_to_json(self, context): 31 | "Convert the context dictionary into a JSON object" 32 | return json.dumps(context, cls=ComplexEncoder) 33 | 34 | 35 | class JSONView(JSONResponseMixin, TemplateView): 36 | """A Base JSON View using the Mixin.""" 37 | 38 | pass 39 | -------------------------------------------------------------------------------- /chartjs/views/columns.py: -------------------------------------------------------------------------------- 1 | """Tools to build Columns HighCharts parameters.""" 2 | from .base import JSONView 3 | 4 | 5 | class BaseColumnsHighChartsView(JSONView): 6 | """Base Class to generate Column HighCharts configuration. 7 | 8 | Define at least title, yUnit, providers, get_labels() and 9 | get_data() to get started. 10 | """ 11 | 12 | providers = {} 13 | credits = {"enabled": True} 14 | 15 | def get_context_data(self, **kwargs): 16 | """Return graph configuration.""" 17 | context = super(BaseColumnsHighChartsView, self).get_context_data(**kwargs) 18 | context.update( 19 | { 20 | "chart": self.get_type(), 21 | "title": self.get_title(), 22 | "subtitle": self.get_subtitle(), 23 | "xAxis": self.get_xAxis(), 24 | "yAxis": self.get_yAxis(), 25 | "tooltip": self.get_tooltip(), 26 | "plotOptions": self.get_plotOptions(), 27 | "series": self.get_series(), 28 | "credits": self.credits, 29 | } 30 | ) 31 | return context 32 | 33 | def get_type(self): 34 | """Return graph type.""" 35 | return {"type": "column"} 36 | 37 | def get_title(self): 38 | """Return graph title.""" 39 | try: 40 | return {"text": u"%s" % self.title} 41 | except AttributeError: # pragma: no cover 42 | raise NotImplementedError( # pragma: no cover 43 | "You should define self.title or self.get_title" 44 | ) 45 | 46 | def get_subtitle(self): 47 | """Return graph subtitle.""" 48 | subtitle = u"%s" % getattr(self, "subtitle", "") 49 | return subtitle 50 | 51 | def get_xAxis(self): 52 | return {"categories": self.get_labels()} 53 | 54 | def get_labels(self): 55 | raise NotImplementedError( # pragma: no cover 56 | "You should return a labels list. " '(i.e: ["January", ...])' 57 | ) 58 | 59 | def get_yAxis(self): 60 | return {"min": getattr(self, "yMin", 0), "title": self.get_yTitle()} 61 | 62 | def get_yTitle(self): 63 | """Return yAxis title.""" 64 | subtitle = u"%s" % getattr(self, "subtitle", "") 65 | return subtitle 66 | 67 | def get_yUnit(self): 68 | try: 69 | return self.yUnit 70 | except AttributeError: # pragma: no cover 71 | raise NotImplementedError( # pragma: no cover 72 | "Please define the yAxis unit (self.yUnit)." 73 | ) 74 | 75 | def get_tooltip(self): 76 | """Return tooltip configuration.""" 77 | return { 78 | "headerFormat": """ 79 | 80 | {point.key} 81 | 82 | """, 83 | "pointFormat": """ 84 | 85 | 88 | 91 | """ 92 | % self.get_yUnit(), 93 | "footerFormat": "
86 | {series.name}: 87 | 89 | {point.y:.0f} %s 90 |
", 94 | "shared": True, 95 | "useHTML": True, 96 | } 97 | 98 | def get_plotOptions(self): 99 | """Return plotOptions configuration.""" 100 | return {"column": {"pointPadding": 0.2, "borderWidth": 0}} 101 | 102 | def get_series(self): 103 | """Generate HighCharts series from providers and data.""" 104 | series = [] 105 | data = self.get_data() 106 | providers = self.get_providers() 107 | for i, d in enumerate(data): 108 | series.append({"name": providers[i], "data": d}) 109 | return series 110 | 111 | def get_data(self): 112 | """Return a list of series [[25, 34, 0, 1, 50], ...]). 113 | 114 | In the same order as providers and with the same serie length 115 | of xAxis. 116 | """ 117 | raise NotImplementedError( # pragma: no cover 118 | "You should return a data list list. " "(i.e: [[25, 34, 0, 1, 50], ...])." 119 | ) 120 | 121 | def get_providers(self): 122 | """Return the list of data series names. 123 | 124 | Providers numbers should be equal to series numbers. 125 | """ 126 | try: 127 | return self.providers 128 | except AttributeError: # pragma: no cover 129 | raise NotImplementedError( # pragma: no cover 130 | "You should define self.providers of self.get_providers." 131 | ) 132 | -------------------------------------------------------------------------------- /chartjs/views/lines.py: -------------------------------------------------------------------------------- 1 | """Tools to build Line charts parameters.""" 2 | from . import HighChartsView 3 | from ..colors import next_color 4 | from .base import JSONView 5 | 6 | 7 | class BaseLineChartView(JSONView): 8 | providers = {} 9 | 10 | def get_context_data(self, **kwargs): 11 | context = super(BaseLineChartView, self).get_context_data(**kwargs) 12 | context.update({"labels": self.get_labels(), "datasets": self.get_datasets()}) 13 | return context 14 | 15 | def get_colors(self): 16 | return next_color() 17 | 18 | def get_dataset_options(self, index, color): 19 | default_opt = { 20 | "backgroundColor": "rgba(%d, %d, %d, 0.5)" % color, 21 | "borderColor": "rgba(%d, %d, %d, 1)" % color, 22 | "pointBackgroundColor": "rgba(%d, %d, %d, 1)" % color, 23 | "pointBorderColor": "#fff", 24 | } 25 | return default_opt 26 | 27 | def get_datasets(self): 28 | datasets = [] 29 | color_generator = self.get_colors() 30 | data = self.get_data() 31 | providers = self.get_providers() 32 | num = len(providers) 33 | for i, entry in enumerate(data): 34 | color = tuple(next(color_generator)) 35 | dataset = {"data": entry} 36 | dataset.update(self.get_dataset_options(i, color)) 37 | if i < num: 38 | dataset["label"] = providers[i] # series labels for Chart.js 39 | dataset["name"] = providers[i] # HighCharts may need this 40 | datasets.append(dataset) 41 | return datasets 42 | 43 | def get_labels(self): 44 | raise NotImplementedError( # pragma: no cover 45 | "You should return a labels list. " '(i.e: ["January", ...])' 46 | ) 47 | 48 | def get_data(self): 49 | raise NotImplementedError( # pragma: no cover 50 | "You should return a data list list. " "(i.e: [[25, 34, 0, 1, 50], ...])." 51 | ) 52 | 53 | def get_providers(self): 54 | return [] 55 | 56 | 57 | class BaseLineOptionsChartView(BaseLineChartView): 58 | def get_context_data(self, **kwargs): 59 | context = super(BaseLineChartView, self).get_context_data(**kwargs) 60 | context.update( 61 | { 62 | "data": {"labels": self.get_labels(), "datasets": self.get_datasets()}, 63 | "options": self.get_options(), 64 | } 65 | ) 66 | return context 67 | 68 | def get_options(self): 69 | raise NotImplementedError( # pragma: no cover 70 | "You should return a dict. " '(i.e.: {"responsive": false})' 71 | ) 72 | 73 | 74 | class HighchartPlotLineChartView(HighChartsView): 75 | y_axis_title = None 76 | 77 | def get_y_axis_options(self): 78 | return {"title": {"text": u"%s" % self.y_axis_title}} 79 | 80 | def get_x_axis_options(self): 81 | return {"categories": self.get_labels()} 82 | 83 | def get_context_data(self, **kwargs): 84 | data = super(HighchartPlotLineChartView, self).get_context_data(**kwargs) 85 | data.update( 86 | { 87 | "labels": self.get_labels(), 88 | "xAxis": self.get_x_axis_options(), 89 | "series": self.get_series(), 90 | "yAxis": self.get_y_axis_options(), 91 | } 92 | ) 93 | return data 94 | 95 | def get_providers(self): 96 | return [] 97 | -------------------------------------------------------------------------------- /chartjs/views/pie.py: -------------------------------------------------------------------------------- 1 | from . import HighChartsView 2 | 3 | 4 | class HighChartPieView(HighChartsView): 5 | def get_context_data(self, **kwargs): 6 | data = super(HighChartPieView, self).get_context_data(**kwargs) 7 | data["series"] = self.get_series() 8 | return data 9 | 10 | def get_series(self): 11 | series = super(HighChartPieView, self).get_series() 12 | for serie in series: 13 | serie.update({"type": "pie"}) 14 | return series 15 | 16 | def get_providers(self): 17 | return [] 18 | 19 | 20 | class HighChartDonutView(HighChartPieView): 21 | inner_size = "50%" 22 | 23 | def get_series(self): 24 | series = super(HighChartDonutView, self).get_series() 25 | for serie in series: 26 | serie.update({"innerSize": self.inner_size}) 27 | return series 28 | 29 | def get_plot_options(self): 30 | options = super(HighChartDonutView, self).get_plot_options() 31 | options.update({"pie": {"showInLegend": True}}) 32 | return options 33 | -------------------------------------------------------------------------------- /demo/README.rst: -------------------------------------------------------------------------------- 1 | ############ 2 | Demo project 3 | ############ 4 | 5 | The :file:`demo/` folder holds a demo project to illustrate django-chartjs 6 | usage. 7 | 8 | 9 | *********************** 10 | Browse demo code online 11 | *********************** 12 | 13 | See `demo folder in project's repository`_. 14 | 15 | 16 | *************** 17 | Deploy the demo 18 | *************** 19 | 20 | System requirements: 21 | 22 | * `Python`_ version 2.7 or 3.3, available as ``python`` command. 23 | 24 | .. note:: 25 | 26 | You may use `virtualenv`_ to make sure the active ``python`` is the right 27 | one. 28 | 29 | * ``make`` and ``wget`` to use the provided :file:`Makefile`. 30 | 31 | Execute: 32 | 33 | .. code-block:: sh 34 | 35 | git clone git@github.com:peopledoc/django-chartjs.git 36 | cd django-chartjs/ 37 | make demo 38 | 39 | It installs and runs the demo server on localhost, port 8000. So have a look 40 | at http://localhost:8000/ 41 | 42 | .. note:: 43 | 44 | If you cannot execute the Makefile, read it and adapt the few commands it 45 | contains to your needs. 46 | 47 | Browse and use :file:`demo/demoproject/` as a sandbox. 48 | 49 | 50 | ********** 51 | References 52 | ********** 53 | 54 | .. target-notes:: 55 | 56 | .. _`demo folder in project's repository`: 57 | https://github.com/peopledoc/django-chartjs/tree/master/demo/demoproject/ 58 | 59 | .. _`Python`: http://python.org 60 | .. _`virtualenv`: http://virtualenv.org 61 | -------------------------------------------------------------------------------- /demo/demoproject/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peopledoc/django-chartjs/e3c72c948fe8a905a5ee696c8cca9217941058a6/demo/demoproject/__init__.py -------------------------------------------------------------------------------- /demo/demoproject/fixtures/meter-readings.json: -------------------------------------------------------------------------------- 1 | [{"model": "demoproject.meter", "pk": 1, "fields": {"date": "2019-05-26", "name": "water", "reading": 10}}, {"model": "demoproject.meter", "pk": 2, "fields": {"date": "2019-05-27", "name": "water", "reading": 12}}, {"model": "demoproject.meter", "pk": 3, "fields": {"date": "2019-05-28", "name": "water", "reading": 13}}, {"model": "demoproject.meter", "pk": 4, "fields": {"date": "2019-05-29", "name": "water", "reading": 15}}, {"model": "demoproject.meter", "pk": 5, "fields": {"date": "2019-06-01", "name": "water", "reading": 16}}, {"model": "demoproject.meter", "pk": 6, "fields": {"date": "2019-06-02", "name": "water", "reading": 18}}, {"model": "demoproject.meter", "pk": 7, "fields": {"date": "2019-06-03", "name": "water", "reading": 20}}, {"model": "demoproject.meter", "pk": 8, "fields": {"date": "2019-06-04", "name": "water", "reading": 21}}, {"model": "demoproject.meter", "pk": 9, "fields": {"date": "2019-05-28", "name": "gas", "reading": 15}}, {"model": "demoproject.meter", "pk": 10, "fields": {"date": "2019-05-29", "name": "gas", "reading": 13}}, {"model": "demoproject.meter", "pk": 11, "fields": {"date": "2019-05-30", "name": "gas", "reading": 12}}, {"model": "demoproject.meter", "pk": 12, "fields": {"date": "2019-05-31", "name": "gas", "reading": 14}}, {"model": "demoproject.meter", "pk": 13, "fields": {"date": "2019-06-01", "name": "gas", "reading": 16}}, {"model": "demoproject.meter", "pk": 14, "fields": {"date": "2019-06-02", "name": "gas", "reading": 17}}] -------------------------------------------------------------------------------- /demo/demoproject/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | from django.core.management import execute_from_command_line 6 | 7 | 8 | def main(): 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "%s.settings" % __package__) 10 | execute_from_command_line(sys.argv) 11 | 12 | 13 | if __name__ == "__main__": 14 | main() 15 | -------------------------------------------------------------------------------- /demo/demoproject/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.2 on 2019-06-06 10:29 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name="Meter", 15 | fields=[ 16 | ( 17 | "id", 18 | models.AutoField( 19 | auto_created=True, 20 | primary_key=True, 21 | serialize=False, 22 | verbose_name="ID", 23 | ), 24 | ), 25 | ("date", models.DateField()), 26 | ("name", models.CharField(max_length=255)), 27 | ("reading", models.IntegerField()), 28 | ], 29 | options={"ordering": ("-date", "name")}, 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /demo/demoproject/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peopledoc/django-chartjs/e3c72c948fe8a905a5ee696c8cca9217941058a6/demo/demoproject/migrations/__init__.py -------------------------------------------------------------------------------- /demo/demoproject/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Meter(models.Model): 5 | date = models.DateField() 6 | name = models.CharField(max_length=255) 7 | reading = models.IntegerField() 8 | 9 | class Meta: 10 | ordering = ("-date", "name") 11 | -------------------------------------------------------------------------------- /demo/demoproject/settings.py: -------------------------------------------------------------------------------- 1 | """Django settings for Django-DownloadView demo project.""" 2 | from os.path import abspath, dirname, join 3 | 4 | # Configure some relative directories. 5 | demoproject_dir = dirname(abspath(__file__)) 6 | BASE_DIR = demoproject_dir 7 | demo_dir = dirname(demoproject_dir) 8 | root_dir = dirname(demo_dir) 9 | data_dir = join(root_dir, "var") 10 | 11 | 12 | # Mandatory settings. 13 | ROOT_URLCONF = "demoproject.urls" 14 | WSGI_APPLICATION = "demoproject.wsgi.application" 15 | 16 | 17 | # Database. 18 | DATABASES = { 19 | "default": { 20 | "ENGINE": "django.db.backends.sqlite3", 21 | "NAME": join(data_dir, "db.sqlite"), 22 | } 23 | } 24 | 25 | 26 | # Required. 27 | SECRET_KEY = "This is a secret made public on project's repository." 28 | 29 | # Media and static files. 30 | MEDIA_ROOT = join(data_dir, "media") 31 | MEDIA_URL = "/media/" 32 | STATIC_ROOT = join(data_dir, "static") 33 | STATIC_URL = "/static/" 34 | 35 | 36 | # Applications. 37 | INSTALLED_APPS = ( 38 | # Standard Django applications. 39 | "django.contrib.auth", 40 | "django.contrib.contenttypes", 41 | "django.contrib.sessions", 42 | "django.contrib.sites", 43 | "django.contrib.messages", 44 | "django.contrib.staticfiles", 45 | "chartjs", 46 | "demoproject", 47 | # For test purposes. The demo project is part of django-downloadview 48 | # test suite. 49 | "django_nose", 50 | ) 51 | 52 | USE_I18N = False 53 | 54 | TEMPLATE_CONTEXT_PROCESSORS = [ 55 | "django.contrib.auth.context_processors.auth", 56 | "django.core.context_processors.debug", 57 | "django.core.context_processors.media", 58 | "django.core.context_processors.static", 59 | "django.core.context_processors.tz", 60 | "django.contrib.messages.context_processors.messages", 61 | "django.core.context_processors.request", 62 | ] 63 | 64 | TEMPLATES = [ 65 | { 66 | "BACKEND": "django.template.backends.django.DjangoTemplates", 67 | "DIRS": [join(BASE_DIR, "", "templates")], 68 | "APP_DIRS": True, 69 | "OPTIONS": { 70 | "context_processors": [ 71 | "django.template.context_processors.debug", 72 | "django.template.context_processors.request", 73 | "django.contrib.auth.context_processors.auth", 74 | "django.contrib.messages.context_processors.messages", 75 | ], 76 | }, 77 | }, 78 | ] 79 | 80 | # Default middlewares. You may alter the list later. 81 | MIDDLEWARE_CLASSES = ( 82 | "django.contrib.sessions.middleware.SessionMiddleware", 83 | "django.middleware.csrf.CsrfViewMiddleware", 84 | "django.contrib.auth.middleware.AuthenticationMiddleware", 85 | "django.middleware.common.CommonMiddleware", 86 | "django.contrib.messages.middleware.MessageMiddleware", 87 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 88 | ) 89 | 90 | LANGUAGE_CODE = "fr" 91 | 92 | 93 | def ugettext(string): 94 | return string 95 | 96 | 97 | LANGUAGES = ( 98 | ("fr", ugettext("French")), 99 | ("en", ugettext("English")), 100 | ("de", ugettext("German")), 101 | ) 102 | 103 | # Development configuration. 104 | DEBUG = True 105 | TEMPLATE_DEBUG = DEBUG 106 | TEST_RUNNER = "django_nose.NoseTestSuiteRunner" 107 | NOSE_ARGS = [ 108 | "--verbose", 109 | "--nocapture", 110 | "--rednose", 111 | "--with-doctest", 112 | "--with-xunit", 113 | "--xunit-file=%s" % join(data_dir, "nosetests.xml"), 114 | "--with-coverage", 115 | "--cover-erase", 116 | "--cover-package=chartjs", 117 | "--no-path-adjustment", 118 | "--all-modules", 119 | ] 120 | -------------------------------------------------------------------------------- /demo/demoproject/templates/base.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peopledoc/django-chartjs/e3c72c948fe8a905a5ee696c8cca9217941058a6/demo/demoproject/templates/base.html -------------------------------------------------------------------------------- /demo/demoproject/templates/colors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | django-chartjs colors demo 4 | 5 | 6 |

Here is a list of colors generated for each dataset!

7 | 8 | {% for color in colors %} 9 |
10 | {% cycle '' '' '' '' '' '' '' '' '' '
' %} 11 | {% endfor %} 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /demo/demoproject/templates/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | django-chartjs demo 4 | 5 | 6 |

Welcome to django-chartjs demo!

7 |

Here are some demo links. Browse the code to see how they are implemented

8 | 9 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo/demoproject/templates/line_chart.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | django-chartjs line chart demo 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 |

Some Line Charts loaded in Ajax!

15 | 16 |

Chart.js

17 | 18 | 19 | 20 | 32 | 33 |

Chart.js with Discontinuous Data

34 | 35 | 36 | 37 | 50 | 51 |

Chart.js Line Chart with options specified in view

52 | 53 | 54 | 55 | 65 | 66 |

HighCharts

67 | 68 |
69 | 70 | 76 | 77 |
78 | 79 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /demo/demoproject/tests.py: -------------------------------------------------------------------------------- 1 | """Unit tests for chartjs api.""" 2 | import json 3 | 4 | from django.test import TestCase 5 | from django.urls import reverse 6 | 7 | from chartjs.util import NULL, value_or_null 8 | 9 | from demoproject.models import Meter 10 | 11 | 12 | class LineChartJSTestCase(TestCase): 13 | def test_line_chartjs(self): 14 | resp = self.client.get(reverse("line_chart")) 15 | self.assertContains(resp, "Chart.min.js") 16 | 17 | def test_list_chartjs_json(self): 18 | resp = self.client.get(reverse("line_chart_json")) 19 | try: 20 | data = json.loads(resp.content.decode("utf-8")) 21 | except ValueError: 22 | self.fail("%r is not valid json" % self.resp.content) 23 | 24 | self.assertIn("datasets", data) 25 | self.assertNotIn("series", data) 26 | 27 | 28 | class ColorTestCase(TestCase): 29 | def test_colorview(self): 30 | resp = self.client.get(reverse("colors")) 31 | self.assertContains(resp, "100px") 32 | 33 | 34 | class HighChartJSTestCase(TestCase): 35 | def test_column_chartjs_json(self): 36 | resp = self.client.get(reverse("column_highchart_json")) 37 | try: 38 | data = json.loads(resp.content.decode("utf-8")) 39 | except ValueError: 40 | self.fail("%r is not valid json" % self.resp.content) 41 | self.assertIn("title", data) 42 | self.assertIn("text", data["title"]) 43 | self.assertEqual(data["title"]["text"], "Column Highchart test") 44 | self.assertIn("credits", data) 45 | credits = data["credits"] 46 | self.assertEqual(credits["enabled"], False) 47 | 48 | def test_list_chartjs_json(self): 49 | resp = self.client.get(reverse("line_highchart_json")) 50 | try: 51 | data = json.loads(resp.content.decode("utf-8")) 52 | except ValueError: 53 | self.fail("%r is not valid json" % self.resp.content) 54 | 55 | self.assertIn("series", data) 56 | self.assertNotIn("datasets", data) 57 | self.assertIn("credits", data) 58 | credits = data["credits"] 59 | self.assertEqual(credits["enabled"], True) 60 | self.assertEqual(credits["href"], "http://example.com") 61 | self.assertEqual(credits["text"], "Novapost Team") 62 | 63 | def test_pie_chartjs_json(self): 64 | resp = self.client.get(reverse("pie_highchart_json")) 65 | try: 66 | json.loads(resp.content.decode("utf-8")) 67 | except ValueError: 68 | self.fail("%r is not valid json" % self.resp.content) 69 | 70 | def test_donut_chartjs_json(self): 71 | resp = self.client.get(reverse("donut_highchart_json")) 72 | try: 73 | json.loads(resp.content.decode("utf-8")) 74 | except ValueError: 75 | self.fail("%r is not valid json" % self.resp.content) 76 | 77 | 78 | class DiscontinuousDataTestCase(TestCase): 79 | def setUp(self): 80 | self.start_date = "2019-05-26" 81 | self.end_date = "2019-06-04" 82 | # water meter readings 83 | Meter.objects.create(date="2019-05-26", name="water", reading=10) 84 | Meter.objects.create(date="2019-05-27", name="water", reading=12) 85 | Meter.objects.create(date="2019-05-28", name="water", reading=13) 86 | Meter.objects.create(date="2019-05-29", name="water", reading=15) 87 | Meter.objects.create(date="2019-06-01", name="water", reading=16) 88 | Meter.objects.create(date="2019-06-02", name="water", reading=18) 89 | Meter.objects.create(date="2019-06-03", name="water", reading=20) 90 | Meter.objects.create(date="2019-06-04", name="water", reading=21) 91 | # gas meter readings 92 | Meter.objects.create(date="2019-05-28", name="gas", reading=15) 93 | Meter.objects.create(date="2019-05-29", name="gas", reading=13) 94 | Meter.objects.create(date="2019-05-30", name="gas", reading=12) 95 | Meter.objects.create(date="2019-05-31", name="gas", reading=14) 96 | Meter.objects.create(date="2019-06-01", name="gas", reading=16) 97 | Meter.objects.create(date="2019-06-02", name="gas", reading=17) 98 | 99 | def test_generator_fills_end_values_with_null(self): 100 | queryset = Meter.objects.filter(name="gas") 101 | actual_data = [] 102 | for item in value_or_null( 103 | self.start_date, self.end_date, queryset, "date", "reading" 104 | ): 105 | actual_data.append(item) 106 | expected_data = [NULL, NULL, 15, 13, 12, 14, 16, 17, NULL, NULL] 107 | self.assertEqual(actual_data, expected_data) 108 | 109 | def test_generator_fills_middle_values_with_null(self): 110 | queryset = Meter.objects.filter(name="water") 111 | actual_data = [] 112 | for item in value_or_null( 113 | self.start_date, self.end_date, queryset, "date", "reading" 114 | ): 115 | actual_data.append(item) 116 | expected_data = [10, 12, 13, 15, NULL, NULL, 16, 18, 20, 21] 117 | self.assertEqual(actual_data, expected_data) 118 | 119 | 120 | class ChartOptionsTestCase(TestCase): 121 | def test_line_chart_with_options_json(self): 122 | resp = self.client.get(reverse("line_chart_with_options")) 123 | try: 124 | data = json.loads(resp.content.decode("utf-8")) 125 | except ValueError: 126 | self.fail("%r is not valid json" % self.resp.content) 127 | self.assertIn("data", data) 128 | self.assertIn("datasets", data["data"]) 129 | self.assertIn("labels", data["data"]) 130 | self.assertIn("options", data) 131 | self.assertIn("title", data["options"]) 132 | -------------------------------------------------------------------------------- /demo/demoproject/urls.py: -------------------------------------------------------------------------------- 1 | import django 2 | from django.views.generic import TemplateView 3 | try: 4 | from django.urls import re_path 5 | except ImportError: 6 | from django.conf.urls import url as re_path 7 | 8 | from pkg_resources import parse_version 9 | 10 | from . import views 11 | 12 | django_version = parse_version(django.get_version()) 13 | if django_version <= parse_version("1.9"): 14 | from django.conf.urls import patterns 15 | 16 | home = TemplateView.as_view(template_name="home.html") 17 | 18 | patterns_list = [ 19 | re_path(r"^$", home, name="home"), 20 | re_path(r"^colors/$", views.colors, name="colors"), 21 | # Column 22 | re_path( 23 | r"^column_highchart/json/$", 24 | views.column_highchart_json, 25 | name="column_highchart_json", 26 | ), 27 | # Line chart 28 | re_path(r"^line_chart/$", views.line_chart, name="line_chart"), 29 | re_path(r"^line_chart/json/$", views.line_chart_json, name="line_chart_json"), 30 | re_path( 31 | r"^line_chart/discontinuous/json/$", 32 | views.discontinuous_dates_chart_json, 33 | name="discontinuous_line_chart_json", 34 | ), 35 | re_path( 36 | r"^line_chart/options/$", 37 | views.line_chart_with_options, 38 | name="line_chart_with_options", 39 | ), 40 | re_path( 41 | r"^line_highchart/json/$", views.line_highchart_json, name="line_highchart_json" 42 | ), 43 | re_path( 44 | r"^line_highchart/discontinuous/json/$", 45 | views.discontinuous_dates_highchart_json, 46 | name="discontinuous_line_highchart_json", 47 | ), 48 | # Pie 49 | re_path(r"^pie_highchart/json/$", views.pie_highchart_json, name="pie_highchart_json"), 50 | re_path( 51 | r"^donut_highchart/json/$", 52 | views.donut_highchart_json, 53 | name="donut_highchart_json", 54 | ), 55 | ] 56 | 57 | if django_version <= parse_version("1.9"): 58 | urlpatterns = patterns("", *patterns_list) 59 | else: 60 | urlpatterns = patterns_list 61 | -------------------------------------------------------------------------------- /demo/demoproject/views.py: -------------------------------------------------------------------------------- 1 | from itertools import islice 2 | from random import randint, shuffle 3 | 4 | from django.utils.translation import gettext_lazy as _ 5 | from django.views.generic import TemplateView 6 | 7 | from chartjs.colors import COLORS, next_color 8 | from chartjs.util import date_range, value_or_null 9 | from chartjs.views.columns import BaseColumnsHighChartsView 10 | from chartjs.views.lines import ( 11 | BaseLineChartView, 12 | BaseLineOptionsChartView, 13 | HighchartPlotLineChartView, 14 | ) 15 | from chartjs.views.pie import HighChartDonutView, HighChartPieView 16 | 17 | from demoproject.models import Meter 18 | 19 | 20 | class ColorsView(TemplateView): 21 | template_name = "colors.html" 22 | 23 | def get_context_data(self, **kwargs): 24 | data = super(ColorsView, self).get_context_data(**kwargs) 25 | data["colors"] = islice(next_color(), 0, 50) 26 | return data 27 | 28 | 29 | class ChartMixin(object): 30 | def get_providers(self): 31 | """Return names of datasets.""" 32 | return ["Central", "Eastside", "Westside"] 33 | 34 | def get_labels(self): 35 | """Return 7 labels.""" 36 | return ["January", "February", "March", "April", "May", "June", "July"] 37 | 38 | def get_data(self): 39 | """Return 3 random dataset to plot.""" 40 | 41 | def data(): 42 | """Return 7 randint between 0 and 100.""" 43 | return [randint(0, 100) for x in range(7)] 44 | 45 | return [data() for x in range(3)] 46 | 47 | def get_colors(self): 48 | """Return a new shuffle list of color so we change the color 49 | each time.""" 50 | colors = COLORS[:] 51 | shuffle(colors) 52 | return next_color(colors) 53 | 54 | 55 | class ColumnHighChartJSONView(ChartMixin, BaseColumnsHighChartsView): 56 | title = _("Column Highchart test") 57 | yUnit = "%" 58 | providers = ["All"] 59 | credits = {"enabled": False} 60 | 61 | def get_data(self): 62 | return [super(ColumnHighChartJSONView, self).get_data()] 63 | 64 | 65 | class LineChartJSONView(ChartMixin, BaseLineChartView): 66 | pass 67 | 68 | 69 | class LineHighChartJSONView(ChartMixin, HighchartPlotLineChartView): 70 | title = _("Line HighChart Test") 71 | y_axis_title = _("Kangaroos") 72 | 73 | # special - line charts credits are personalized 74 | credits = { 75 | "enabled": True, 76 | "href": "http://example.com", 77 | "text": "Novapost Team", 78 | } 79 | 80 | 81 | class PieHighChartJSONView(ChartMixin, HighChartPieView): 82 | pass 83 | 84 | 85 | class DonutHighChartJSONView(ChartMixin, HighChartDonutView): 86 | pass 87 | 88 | 89 | class DiscontinuousDatesChartJSONView(ChartMixin, BaseLineChartView): 90 | start_date = "2019-05-26" 91 | end_date = "2019-06-04" 92 | 93 | def get_providers(self): 94 | return ["Water", "Gas"] 95 | 96 | def get_labels(self): 97 | return [dt for dt in date_range(self.start_date, self.end_date)] 98 | 99 | def get_data(self): 100 | result = [] 101 | water = Meter.objects.filter(name="water") 102 | data = [ 103 | item 104 | for item in value_or_null( 105 | self.start_date, self.end_date, water, "date", "reading" 106 | ) 107 | ] 108 | result.append(data) 109 | gas = Meter.objects.filter(name="gas") 110 | data = [ 111 | item 112 | for item in value_or_null( 113 | self.start_date, self.end_date, gas, "date", "reading" 114 | ) 115 | ] 116 | result.append(data) 117 | return result 118 | 119 | 120 | class DiscontinuousDatesHighChartJSONView(ChartMixin, HighchartPlotLineChartView): 121 | title = _("Discontinuous Line HighChart Test") 122 | y_axis_title = _("Volume") 123 | start_date = "2019-05-26" 124 | end_date = "2019-06-04" 125 | 126 | def get_providers(self): 127 | return ["Water", "Gas"] 128 | 129 | def get_labels(self): 130 | return [dt for dt in date_range(self.start_date, self.end_date)] 131 | 132 | def get_data(self): 133 | result = [] 134 | water = Meter.objects.filter(name="water") 135 | data = [ 136 | item 137 | for item in value_or_null( 138 | self.start_date, self.end_date, water, "date", "reading" 139 | ) 140 | ] 141 | result.append(data) 142 | gas = Meter.objects.filter(name="gas") 143 | data = [ 144 | item 145 | for item in value_or_null( 146 | self.start_date, self.end_date, gas, "date", "reading" 147 | ) 148 | ] 149 | result.append(data) 150 | return result 151 | 152 | 153 | class LineChartWithOptionsJSONView(ChartMixin, BaseLineOptionsChartView): 154 | def get_options(self): 155 | options = { 156 | "title": {"display": True, "text": "Custom Chart Title"}, 157 | "elements": {"point": {"pointStyle": "rectRounded", "radius": 10}}, 158 | "responsive": False, 159 | } 160 | return options 161 | 162 | 163 | # Pre-configured views. 164 | colors = ColorsView.as_view() 165 | 166 | column_highchart_json = ColumnHighChartJSONView.as_view() 167 | line_chart = TemplateView.as_view(template_name="line_chart.html") 168 | line_chart_json = LineChartJSONView.as_view() 169 | line_highchart_json = LineHighChartJSONView.as_view() 170 | pie_highchart_json = PieHighChartJSONView.as_view() 171 | donut_highchart_json = DonutHighChartJSONView.as_view() 172 | discontinuous_dates_chart_json = DiscontinuousDatesChartJSONView.as_view() 173 | discontinuous_dates_highchart_json = DiscontinuousDatesHighChartJSONView.as_view() 174 | line_chart_with_options = LineChartWithOptionsJSONView.as_view() 175 | -------------------------------------------------------------------------------- /demo/demoproject/wsgi.py: -------------------------------------------------------------------------------- 1 | """WSGI config for Django-DownloadView demo project. 2 | 3 | This module contains the WSGI application used by Django's development server 4 | and any production WSGI deployments. It should expose a module-level variable 5 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 6 | this application via the ``WSGI_APPLICATION`` setting. 7 | 8 | Usually you will have the standard Django WSGI application here, but it also 9 | might make sense to replace the whole Django WSGI application with a custom one 10 | that later delegates to the Django one. For example, you could introduce WSGI 11 | middleware here, or combine a Django application with an application of another 12 | framework. 13 | 14 | """ 15 | import os 16 | 17 | from django.core.wsgi import get_wsgi_application 18 | 19 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "%s.settings" % __package__) 20 | 21 | # This application object is used by any WSGI server configured to use this 22 | # file. This includes Django's development server, if the WSGI_APPLICATION 23 | # setting points here. 24 | application = get_wsgi_application() 25 | 26 | # Apply WSGI middleware here. 27 | # from helloworld.wsgi import HelloWorldApplication 28 | # application = HelloWorldApplication(application) 29 | -------------------------------------------------------------------------------- /demo/setup.py: -------------------------------------------------------------------------------- 1 | """Python packaging.""" 2 | import os 3 | 4 | from setuptools import setup 5 | 6 | 7 | def read_relative_file(filename): 8 | """Returns contents of the given file, which path is supposed relative 9 | to this module.""" 10 | with open(os.path.join(os.path.dirname(__file__), filename)) as f: 11 | return f.read() 12 | 13 | 14 | NAME = "django-chartjs-demo" 15 | README = read_relative_file("README.rst") 16 | VERSION = "0.1" 17 | PACKAGES = ["demoproject"] 18 | REQUIRES = ["django-chartjs", "Django", "django_nose"] 19 | 20 | 21 | setup( 22 | name=NAME, 23 | version=VERSION, 24 | description="Demo project for django-chartjs.", 25 | long_description=README, 26 | classifiers=[ 27 | "Development Status :: 1 - Planning", 28 | "License :: OSI Approved :: BSD License", 29 | "Programming Language :: Python :: 3", 30 | "Framework :: Django", 31 | ], 32 | keywords="class-based view, generic view, download", 33 | author=u"Rémy HUBSCHER", 34 | author_email="hubscher.remy@gmail.com", 35 | url="https://github.com/peopledoc/django-chartjs", 36 | license="BSD", 37 | packages=PACKAGES, 38 | include_package_data=True, 39 | zip_safe=False, 40 | install_requires=REQUIRES, 41 | entry_points={"console_scripts": ["demo = demoproject.manage:main"]}, 42 | ) 43 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = ../var/docs 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-chartjs.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-chartjs.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/django-chartjs" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-chartjs" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | -------------------------------------------------------------------------------- /docs/_static/django-chartjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peopledoc/django-chartjs/e3c72c948fe8a905a5ee696c8cca9217941058a6/docs/_static/django-chartjs.png -------------------------------------------------------------------------------- /docs/about/authors.txt: -------------------------------------------------------------------------------- 1 | .. include:: ../../AUTHORS 2 | -------------------------------------------------------------------------------- /docs/about/changelog.txt: -------------------------------------------------------------------------------- 1 | 1.3 (2018-04-18) 2 | ---------------- -------------------------------------------------------------------------------- /docs/about/index.txt: -------------------------------------------------------------------------------- 1 | ############################ 2 | About django-chartjs 3 | ############################ 4 | 5 | .. toctree:: 6 | 7 | license 8 | authors 9 | changelog 10 | -------------------------------------------------------------------------------- /docs/about/license.txt: -------------------------------------------------------------------------------- 1 | .. include:: ../../LICENSE 2 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # django-chartjs documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Aug 27 11:37:23 2012. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | 17 | # Minimal Django settings. Required to use sphinx.ext.autodoc, because 18 | # django-chartjs depends on Django... 19 | from django.conf import settings 20 | 21 | 22 | settings.configure( 23 | DATABASES={}, # Required to load ``django.views.generic``. 24 | ) 25 | 26 | 27 | doc_dir = os.path.dirname(os.path.abspath(__file__)) 28 | project_dir = os.path.dirname(doc_dir) 29 | version_filename = os.path.join(project_dir, 'VERSION') 30 | 31 | 32 | # If extensions (or modules to document with autodoc) are in another directory, 33 | # add these directories to sys.path here. If the directory is relative to the 34 | # documentation root, use os.path.abspath to make it absolute, like shown here. 35 | #sys.path.insert(0, os.path.abspath('.')) 36 | 37 | # -- General configuration ----------------------------------------------------- 38 | 39 | # If your documentation needs a minimal Sphinx version, state it here. 40 | #needs_sphinx = '1.0' 41 | 42 | # Add any Sphinx extension module names here, as strings. They can be extensions 43 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 44 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.autosummary', 45 | 'sphinx.ext.doctest', 'sphinx.ext.coverage'] 46 | 47 | # Add any paths that contain templates here, relative to this directory. 48 | templates_path = ['_templates'] 49 | 50 | # The suffix of source filenames. 51 | source_suffix = '.txt' 52 | 53 | # The encoding of source files. 54 | #source_encoding = 'utf-8-sig' 55 | 56 | # The master toctree document. 57 | master_doc = 'index' 58 | 59 | # General information about the project. 60 | project = u'django-chartjs' 61 | copyright = u'2013, Novapost' 62 | 63 | # The version info for the project you're documenting, acts as replacement for 64 | # |version| and |release|, also used in various other places throughout the 65 | # built documents. 66 | # 67 | # The short X.Y version. 68 | version = open(version_filename).read().strip() 69 | # The full version, including alpha/beta/rc tags. 70 | release = version 71 | 72 | # The language for content autogenerated by Sphinx. Refer to documentation 73 | # for a list of supported languages. 74 | language = 'en' 75 | 76 | # There are two options for replacing |today|: either, you set today to some 77 | # non-false value, then it is used: 78 | #today = '' 79 | # Else, today_fmt is used as the format for a strftime call. 80 | #today_fmt = '%B %d, %Y' 81 | 82 | # List of patterns, relative to source directory, that match files and 83 | # directories to ignore when looking for source files. 84 | exclude_patterns = ['_build'] 85 | 86 | # The reST default role (used for this markup: `text`) to use for all documents. 87 | #default_role = None 88 | 89 | # If true, '()' will be appended to :func: etc. cross-reference text. 90 | #add_function_parentheses = True 91 | 92 | # If true, the current module name will be prepended to all description 93 | # unit titles (such as .. function::). 94 | #add_module_names = True 95 | 96 | # If true, sectionauthor and moduleauthor directives will be shown in the 97 | # output. They are ignored by default. 98 | #show_authors = False 99 | 100 | # The name of the Pygments (syntax highlighting) style to use. 101 | pygments_style = 'sphinx' 102 | 103 | # A list of ignored prefixes for module index sorting. 104 | #modindex_common_prefix = [] 105 | 106 | 107 | # -- Options for HTML output --------------------------------------------------- 108 | 109 | # The theme to use for HTML and HTML Help pages. See the documentation for 110 | # a list of builtin themes. 111 | html_theme = 'default' 112 | 113 | # Theme options are theme-specific and customize the look and feel of a theme 114 | # further. For a list of options available for each theme, see the 115 | # documentation. 116 | #html_theme_options = {} 117 | 118 | # Add any paths that contain custom themes here, relative to this directory. 119 | #html_theme_path = [] 120 | 121 | # The name for this set of Sphinx documents. If None, it defaults to 122 | # " v documentation". 123 | #html_title = None 124 | 125 | # A shorter title for the navigation bar. Default is the same as html_title. 126 | #html_short_title = None 127 | 128 | # The name of an image file (relative to this directory) to place at the top 129 | # of the sidebar. 130 | #html_logo = None 131 | 132 | # The name of an image file (within the static path) to use as favicon of the 133 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 134 | # pixels large. 135 | #html_favicon = None 136 | 137 | # Add any paths that contain custom static files (such as style sheets) here, 138 | # relative to this directory. They are copied after the builtin static files, 139 | # so a file named "default.css" will overwrite the builtin "default.css". 140 | html_static_path = ['_static'] 141 | 142 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 143 | # using the given strftime format. 144 | #html_last_updated_fmt = '%b %d, %Y' 145 | 146 | # If true, SmartyPants will be used to convert quotes and dashes to 147 | # typographically correct entities. 148 | #html_use_smartypants = True 149 | 150 | # Custom sidebar templates, maps document names to template names. 151 | html_sidebars = { 152 | '**': ['globaltoc.html', 153 | 'relations.html', 154 | 'sourcelink.html', 155 | 'searchbox.html'], 156 | } 157 | 158 | # Additional templates that should be rendered to pages, maps page names to 159 | # template names. 160 | #html_additional_pages = {} 161 | 162 | # If false, no module index is generated. 163 | #html_domain_indices = True 164 | 165 | # If false, no index is generated. 166 | #html_use_index = True 167 | 168 | # If true, the index is split into individual pages for each letter. 169 | #html_split_index = False 170 | 171 | # If true, links to the reST sources are added to the pages. 172 | #html_show_sourcelink = True 173 | 174 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 175 | #html_show_sphinx = True 176 | 177 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 178 | #html_show_copyright = True 179 | 180 | # If true, an OpenSearch description file will be output, and all pages will 181 | # contain a tag referring to it. The value of this option must be the 182 | # base URL from which the finished HTML is served. 183 | #html_use_opensearch = '' 184 | 185 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 186 | #html_file_suffix = None 187 | 188 | # Output file base name for HTML help builder. 189 | htmlhelp_basename = 'django-chartjsdoc' 190 | 191 | 192 | # -- Options for LaTeX output -------------------------------------------------- 193 | 194 | latex_elements = { 195 | # The paper size ('letterpaper' or 'a4paper'). 196 | #'papersize': 'letterpaper', 197 | 198 | # The font size ('10pt', '11pt' or '12pt'). 199 | #'pointsize': '10pt', 200 | 201 | # Additional stuff for the LaTeX preamble. 202 | #'preamble': '', 203 | } 204 | 205 | # Grouping the document tree into LaTeX files. List of tuples 206 | # (source start file, target name, title, author, documentclass [howto/manual]). 207 | latex_documents = [ 208 | ('index', 'django-chartjs.tex', u'django-chartjs Documentation', 209 | u'Novapost', 'manual'), 210 | ] 211 | 212 | # The name of an image file (relative to this directory) to place at the top of 213 | # the title page. 214 | #latex_logo = None 215 | 216 | # For "manual" documents, if this is true, then toplevel headings are parts, 217 | # not chapters. 218 | #latex_use_parts = False 219 | 220 | # If true, show page references after internal links. 221 | #latex_show_pagerefs = False 222 | 223 | # If true, show URL addresses after external links. 224 | #latex_show_urls = False 225 | 226 | # Documents to append as an appendix to all manuals. 227 | #latex_appendices = [] 228 | 229 | # If false, no module index is generated. 230 | #latex_domain_indices = True 231 | 232 | 233 | # -- Options for manual page output -------------------------------------------- 234 | 235 | # One entry per manual page. List of tuples 236 | # (source start file, name, description, authors, manual section). 237 | man_pages = [ 238 | ('index', 'django-chartjs', u'django-chartjs Documentation', 239 | [u'Novapost'], 1) 240 | ] 241 | 242 | # If true, show URL addresses after external links. 243 | #man_show_urls = False 244 | 245 | 246 | # -- Options for Texinfo output ------------------------------------------------ 247 | 248 | # Grouping the document tree into Texinfo files. List of tuples 249 | # (source start file, target name, title, author, 250 | # dir menu entry, description, category) 251 | texinfo_documents = [ 252 | ('index', 'django-chartjs', u'django-chartjs Documentation', 253 | u'Novapost', 'django-chartjs', 'One line description of project.', 254 | 'Miscellaneous'), 255 | ] 256 | 257 | # Documents to append as an appendix to all manuals. 258 | #texinfo_appendices = [] 259 | 260 | # If false, no module index is generated. 261 | #texinfo_domain_indices = True 262 | 263 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 264 | #texinfo_show_urls = 'footnote' 265 | -------------------------------------------------------------------------------- /docs/demo.txt: -------------------------------------------------------------------------------- 1 | .. include:: ../demo/README 2 | -------------------------------------------------------------------------------- /docs/dev.txt: -------------------------------------------------------------------------------- 1 | ########################### 2 | Contributing to the project 3 | ########################### 4 | 5 | This document provides guidelines for people who want to contribute to the 6 | project. 7 | 8 | 9 | ************** 10 | Create tickets 11 | ************** 12 | 13 | Please use the `bugtracker`_ **before** starting some work: 14 | 15 | * check if the bug or feature request has already been filed. It may have been 16 | answered too! 17 | 18 | * else create a new ticket. 19 | 20 | * if you plan to contribute, tell us, so that we are given an opportunity to 21 | give feedback as soon as possible. 22 | 23 | * Then, in your commit messages, reference the ticket with some 24 | ``refs #TICKET-ID`` syntax. 25 | 26 | 27 | *************** 28 | Fork and branch 29 | *************** 30 | 31 | * Work in forks and branches. 32 | 33 | * Prefix your branch with the ticket ID corresponding to the issue. As an 34 | example, if you are working on ticket #23 which is about contribute 35 | documentation, name your branch like ``23-contribute-doc``. 36 | 37 | * If you work in a development branch and want to refresh it with changes from 38 | master, please `rebase`_ or `merge-based rebase`_, i.e. don't merge master. 39 | 40 | 41 | ******************************* 42 | Setup a development environment 43 | ******************************* 44 | 45 | System requirements: 46 | 47 | * `Python`_ version 2.7 or 3.3, available as ``python`` command. 48 | 49 | .. note:: 50 | 51 | You may use `Virtualenv`_ to make sure the active ``python`` is the right 52 | one. 53 | 54 | * make and wget to use the provided :file:`Makefile`. 55 | 56 | Execute: 57 | 58 | .. code-block:: sh 59 | 60 | git clone git@github.com:peopledoc/django-chartjs.git 61 | cd django-chartjs/ 62 | make test 63 | 64 | If you cannot execute the Makefile, read it and adapt the few commands it 65 | contains to your needs. 66 | 67 | 68 | ************ 69 | The Makefile 70 | ************ 71 | 72 | A :file:`Makefile` is provided to ease development. Use it to: 73 | 74 | * setup the development environment: ``make develop`` 75 | * update it, as an example, after a pull: ``make update`` 76 | * run tests: ``make test`` 77 | * build documentation: ``make documentation`` 78 | 79 | The :file:`Makefile` is intended to be a live reference for the development 80 | environment. 81 | 82 | 83 | ************* 84 | Documentation 85 | ************* 86 | 87 | Follow `style guide for Sphinx-based documentations`_ when editing the 88 | documentation. 89 | 90 | 91 | ************** 92 | Test and build 93 | ************** 94 | 95 | Use `the Makefile`_. 96 | 97 | 98 | ********************* 99 | Demo project included 100 | ********************* 101 | 102 | The :doc:`/demo` is part of the tests. Maintain it along with code and 103 | documentation. 104 | 105 | 106 | ********** 107 | References 108 | ********** 109 | 110 | .. target-notes:: 111 | 112 | .. _`bugtracker`: 113 | https://github.com/peopledoc/django-chartjs/issues 114 | .. _`rebase`: http://git-scm.com/book/en/Git-Branching-Rebasing 115 | .. _`merge-based rebase`: http://tech.peopledoc.fr/psycho-rebasing-en.html 116 | .. _`Python`: http://python.org 117 | .. _`Virtualenv`: http://virtualenv.org 118 | .. _`style guide for Sphinx-based documentations`: 119 | http://documentation-style-guide-sphinx.readthedocs.org/ 120 | -------------------------------------------------------------------------------- /docs/index.txt: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | 3 | 4 | ******** 5 | Contents 6 | ******** 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | :titlesonly: 11 | 12 | demo 13 | install 14 | about/index 15 | dev 16 | -------------------------------------------------------------------------------- /docs/install.txt: -------------------------------------------------------------------------------- 1 | .. include:: ../INSTALL 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | from os.path import abspath, dirname, join 3 | from setuptools import find_packages, setup 4 | 5 | here = abspath(dirname(__file__)) 6 | 7 | 8 | def read_relative_file(filename): 9 | """Returns contents of the given file, whose path is supposed relative 10 | to this module.""" 11 | with codecs.open(join(here, filename), encoding="utf-8") as f: 12 | content = f.read() 13 | return content 14 | 15 | 16 | if __name__ == "__main__": 17 | setup( 18 | name="django-chartjs", 19 | version=read_relative_file("VERSION").strip(), 20 | description="Django Chart.js and Hightchart ajax views", 21 | long_description=read_relative_file("README.rst"), 22 | classifiers=[ 23 | "Development Status :: 4 - Beta", 24 | "Environment :: Web Environment", 25 | "Framework :: Django", 26 | "Framework :: Django :: 1.10", 27 | "Framework :: Django :: 2.2", 28 | "Framework :: Django :: 3.0", 29 | "Intended Audience :: Developers", 30 | "License :: OSI Approved :: BSD License", 31 | "Programming Language :: Python", 32 | "Programming Language :: Python :: 3", 33 | "Programming Language :: Python :: 3.6", 34 | "Programming Language :: Python :: 3.7", 35 | "Programming Language :: Python :: 3.8", 36 | ], 37 | keywords="django chart chartjs highchart ajax class based views", 38 | author="Rémy Hubscher", 39 | author_email="hubscher.remy@gmail.com", 40 | url="https://github.com/peopledoc/django-chartjs", 41 | license="BSD Licence", 42 | packages=find_packages(), 43 | include_package_data=True, 44 | zip_safe=False, 45 | ) 46 | -------------------------------------------------------------------------------- /test-requirements.pip: -------------------------------------------------------------------------------- 1 | Django 2 | flake8 3 | django-nose 4 | coverage 5 | nose 6 | rednose 7 | black 8 | isort 9 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py{36,37}-django110 4 | py{37,38,39}-django{22,30,31,32} 5 | py{38,39,310}-django40 6 | flake8 7 | 8 | [testenv] 9 | deps = 10 | django110: Django==1.10.* 11 | django22: Django==2.2.* 12 | django30: Django==3.0.* 13 | django31: Django==3.1.* 14 | django32: Django==3.2.* 15 | django40: Django==4.0.* 16 | commands = 17 | pip install -r test-requirements.pip 18 | pip install -e ./ 19 | pip install -e demo/ 20 | coverage run --branch --source=chartjs {envbindir}/demo test demoproject 21 | 22 | [testenv:flake8] 23 | commands = flake8 chartjs demo 24 | deps = 25 | flake8 26 | 27 | [flake8] 28 | max-line-length = 99 29 | -------------------------------------------------------------------------------- /update-Chart.js.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | wget -O chartjs/static/js/Chart.min.js https://raw.github.com/nnnick/Chart.js/master/Chart.min.js 4 | wget -O chartjs/static/js/excanvas.js https://explorercanvas.googlecode.com/svn/trunk/excanvas.js 5 | -------------------------------------------------------------------------------- /var/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peopledoc/django-chartjs/e3c72c948fe8a905a5ee696c8cca9217941058a6/var/.gitkeep --------------------------------------------------------------------------------