From 165844e4caf7a53785a9a7ae7d7d43caf6a0d70e Mon Sep 17 00:00:00 2001 From: simon Date: Wed, 21 Feb 2018 20:07:59 -0500 Subject: [PATCH] Added web interface, crawler and more work on local storage --- crawler.py | 173 ++----------- database.sql | 8 +- indexer.py | 55 +++++ parsing.py | 137 +++++++++++ requirements.txt | 6 +- run.py | 91 ++++++- spec/Crawler_spec.py | 14 +- spec/FileParser_spec.py | 38 ++- spec/Indexer_spec.py | 16 ++ spec/LocalStorage_spec.py | 133 ++++++++-- spec/MimeGuesser_spec.py | 23 ++ .../{more_books.jsonl => more_books.json} | 0 static/css/keen-dashboards.css | 72 ++++++ static/css/keen-static.css | 155 ++++++++++++ static/img/bg-bars.png | Bin 0 -> 8480 bytes static/js/Chart.min.js | 10 + storage.py | 155 ++++++++++-- templates/dashboard.html | 230 ++++++++++++++++++ templates/directory.html | 56 +++++ templates/directory_manage.html | 108 ++++++++ templates/index.html | 16 -- templates/layout.html | 75 +++++- templates/{search.html => search-old.html} | 0 templates/task.html | 10 + 24 files changed, 1346 insertions(+), 235 deletions(-) create mode 100644 indexer.py create mode 100644 parsing.py create mode 100644 spec/Indexer_spec.py create mode 100644 spec/MimeGuesser_spec.py rename spec/test_folder/{more_books.jsonl => more_books.json} (100%) create mode 100644 static/css/keen-dashboards.css create mode 100644 static/css/keen-static.css create mode 100644 static/img/bg-bars.png create mode 100644 static/js/Chart.min.js create mode 100644 templates/dashboard.html create mode 100644 templates/directory.html create mode 100644 templates/directory_manage.html delete mode 100644 templates/index.html rename templates/{search.html => search-old.html} (100%) create mode 100644 templates/task.html diff --git a/crawler.py b/crawler.py index ec6ea6a..e6641a7 100644 --- a/crawler.py +++ b/crawler.py @@ -1,152 +1,33 @@ import os -import hashlib class Crawler: - pass + + def __init__(self, enabled_parsers: list): + self.documents = [] + self.enabled_parsers = enabled_parsers + + def crawl(self, root_dir: str): + for root, dirs, files in os.walk(root_dir): + + for filename in files: + full_path = os.path.join(root, filename) + + parser = self.get_parser_by_ext(os.path.splitext(filename)[1]) + + doc = parser.parse(full_path) + + self.documents.append(doc) + + def get_parser_by_ext(self, ext: str): + + for parser in self.enabled_parsers: + + if ext in parser.extensions: + return parser + + for parser in self.enabled_parsers: + if parser.is_default: + return parser -class FileParser: - pass - - -class FileCheckSumCalculator: - - def checksum(self, path: str) -> str: - """ - Calculate the checksum of a file - :param path: path of the file - :return: checksum - """ - raise NotImplementedError() - - -class Md5CheckSumCalculator(FileCheckSumCalculator): - - def __init__(self): - self.name = "md5" - - def checksum(self, path: str) -> str: - """ - Calculate the md5 checksum of a file - :param path: path of the file - :return: md5 checksum - """ - result = hashlib.md5() - - with open(path, "rb") as f: - for block in iter(lambda: f.read(65536), b""): - result.update(block) - - return result.hexdigest().upper() - - -class Sha1CheckSumCalculator(FileCheckSumCalculator): - - def __init__(self): - self.name = "sha1" - - def checksum(self, path: str) -> str: - """ - Calculate the sha1 checksum of a file - :param path: path of the file - :return: sha1 checksum - """ - result = hashlib.sha1() - - with open(path, "rb") as f: - for block in iter(lambda: f.read(65536), b""): - result.update(block) - - return result.hexdigest().upper() - - -class Sha256CheckSumCalculator(FileCheckSumCalculator): - - def __init__(self): - self.name = "sha256" - - def checksum(self, path: str) -> str: - """ - Calculate the sha256 checksum of a file - :param path: path of the file - :return: sha256 checksum - """ - result = hashlib.sha256() - - with open(path, "rb") as f: - for block in iter(lambda: f.read(65536), b""): - result.update(block) - - return result.hexdigest().upper() - - -class GenericFileParser(FileParser): - - def __init__(self, checksum_calculators: list): - self.checksum_calculators = checksum_calculators - - def parse(self, path: str) -> dict: - """ - Parse a generic file - :param path: path of the file to parse - :return: dict information about the file - """ - - info = dict() - - info["size"] = os.path.getsize(path) - info["name"] = os.path.splitext(path)[0] - - for calculator in self.checksum_calculators: - info[calculator.name] = calculator.checksum(path) - - return info - - - - -# def crawl(root_dir: str) -> None: -# docs = [] -# -# for root, dirs, files in os.walk(root_dir): -# -# print(root) -# -# for filename in files: -# full_path = os.path.join(root, filename) -# -# doc = dict() -# -# doc["md5"] = md5sum(full_path) -# doc["path"] = root -# doc["name"] = filename -# doc["size"] = os.path.getsize(full_path) -# doc["mtime"] = int(os.path.getmtime(full_path)) -# -# mime_type = mimetypes.guess_type(full_path)[0] -# -# if mime_type is not None: -# -# doc["mime"] = mime_type -# -# if mime_type.startswith("image"): -# try: -# width, height = Image.open(full_path).size -# -# doc["width"] = width -# doc["height"] = height -# except OSError: -# doc.pop('mime', None) -# pass -# except ValueError: -# doc.pop('mime', None) -# pass -# -# docs.append(doc) -# -# file = open("crawler.json", "w") -# file.write(simplejson.dumps(docs)) -# file.close() -# -# \ No newline at end of file diff --git a/database.sql b/database.sql index d186ed6..0ac7b76 100644 --- a/database.sql +++ b/database.sql @@ -4,6 +4,7 @@ PRAGMA FOREIGN_KEYS = ON; CREATE TABLE Directory ( id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT UNIQUE, + name TEXT, enabled BOOLEAN ); @@ -19,10 +20,11 @@ CREATE TABLE Task ( -- You can set an option on a directory to change the crawler's behavior CREATE TABLE Option ( - name STRING, + id INTEGER PRIMARY KEY AUTOINCREMENT, + key TEXT, + value TEXT, directory_id INTEGER, - FOREIGN KEY (directory_id) REFERENCES Directory(id), - PRIMARY KEY (name, directory_id) + FOREIGN KEY (directory_id) REFERENCES Directory(id) ); -- User accounts diff --git a/indexer.py b/indexer.py new file mode 100644 index 0000000..a9c0883 --- /dev/null +++ b/indexer.py @@ -0,0 +1,55 @@ +import json +import elasticsearch +from threading import Thread +import subprocess +import requests + + +class Indexer: + + def __init__(self, index: str): + + self.index_name = index + self.es = elasticsearch.Elasticsearch() + + try: + requests.head("http://localhost:9200") + print("elasticsearch is already running") + + except requests.exceptions.ConnectionError: + import time + t = Thread(target=Indexer.run_elasticsearch) + t.daemon = True + t.start() + + time.sleep(5) + + @staticmethod + def run_elasticsearch(): + subprocess.Popen(["elasticsearch/bin/elasticsearch"]) + + @staticmethod + def create_bulk_index_string(docs: list, index_name: str): + """ + Creates a insert string for sending to elasticsearch + """ + + result = "" + + action_string = '{"index":{"_index":"' + index_name + '","_type":"file"}}\n' + + for doc in docs: + result += action_string + result += json.dumps(doc) + "\n" + + return result + + def index(self, docs: list): + + index_string = self.create_bulk_index_string(docs, self.index_name) + self.es.bulk(index_string) + + def clear(self): + + self.es.indices.delete(self.index_name) + self.es.indices.create(self.index_name) diff --git a/parsing.py b/parsing.py new file mode 100644 index 0000000..a4e897a --- /dev/null +++ b/parsing.py @@ -0,0 +1,137 @@ +import hashlib +import magic +import os +import mimetypes + + +class MimeGuesser: + + def guess_mime(self, full_path): + raise NotImplementedError() + + +class ContentMimeGuesser(MimeGuesser): + + def __init__(self): + self.libmagic = magic.Magic(mime=True) + + def guess_mime(self, full_path): + return self.libmagic.from_file(full_path) + + +class ExtensionMimeGuesser(MimeGuesser): + + def guess_mime(self, full_path): + return mimetypes.guess_type(full_path, strict=False)[0] + + +class FileParser: + extensions = [] + is_default = False + pass + + +class FileCheckSumCalculator: + + def checksum(self, path: str) -> str: + """ + Calculate the checksum of a file + :param path: path of the file + :return: checksum + """ + raise NotImplementedError() + + +class Md5CheckSumCalculator(FileCheckSumCalculator): + + def __init__(self): + self.name = "md5" + + def checksum(self, path: str) -> str: + """ + Calculate the md5 checksum of a file + :param path: path of the file + :return: md5 checksum + """ + result = hashlib.md5() + + with open(path, "rb") as f: + for block in iter(lambda: f.read(65536), b""): + result.update(block) + + return result.hexdigest().upper() + + +class Sha1CheckSumCalculator(FileCheckSumCalculator): + + def __init__(self): + self.name = "sha1" + + def checksum(self, path: str) -> str: + """ + Calculate the sha1 checksum of a file + :param path: path of the file + :return: sha1 checksum + """ + result = hashlib.sha1() + + with open(path, "rb") as f: + for block in iter(lambda: f.read(65536), b""): + result.update(block) + + return result.hexdigest().upper() + + +class Sha256CheckSumCalculator(FileCheckSumCalculator): + + def __init__(self): + self.name = "sha256" + + def checksum(self, path: str) -> str: + """ + Calculate the sha256 checksum of a file + :param path: path of the file + :return: sha256 checksum + """ + result = hashlib.sha256() + + with open(path, "rb") as f: + for block in iter(lambda: f.read(65536), b""): + result.update(block) + + return result.hexdigest().upper() + + +class GenericFileParser(FileParser): + + extensions = [] + is_default = True + + def __init__(self, checksum_calculators: list, mime_guesser: MimeGuesser): + + self.checksum_calculators = checksum_calculators + self.mime_guesser = mime_guesser + + def parse(self, full_path: str) -> dict: + """ + Parse a generic file + :param full_path: path of the file to parse + :return: dict information about the file + """ + + info = dict() + + file_stat = os.stat(full_path) + path, name = os.path.split(full_path) + + info["size"] = file_stat.st_size + info["path"] = path + info["name"] = name + info["mtime"] = file_stat.st_mtime + info["mime"] = self.mime_guesser.guess_mime(full_path) + + for calculator in self.checksum_calculators: + info[calculator.name] = calculator.checksum(full_path) + + return info + diff --git a/requirements.txt b/requirements.txt index a7478f9..57e9b2c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ -PIL simplejson flask -flask_bcrypt \ No newline at end of file +flask_bcrypt +elasticsearch +python-magic +requests \ No newline at end of file diff --git a/run.py b/run.py index d752381..d2fa965 100644 --- a/run.py +++ b/run.py @@ -1,14 +1,8 @@ -from flask import Flask, render_template, send_file, request -import pysolr -import mimetypes -import requests -import json -from PIL import Image -import os +from flask import Flask, render_template, send_file, request, redirect +from indexer import Indexer +from storage import Directory, Option -SOLR_URL = "http://localhost:8983/solr/test/" - -solr = pysolr.Solr(SOLR_URL, timeout=10) +# indexer = Indexer("fse") app = Flask(__name__) @@ -124,11 +118,86 @@ app = Flask(__name__) # return send_file("thumbnails/" + doc_id, mimetype=mimetypes.guess_type(thumb_path)[0]) # else: # return "File not found" +from storage import LocalStorage +storage = LocalStorage("local_storage.db") @app.route("/") def tmp_route(): - return "test" + return "huh" + + +@app.route("/directory") +def directory(): + + directories = storage.dirs() + print(directories) + + return render_template("directory.html", directories=directories) + + +@app.route("/directory/add") +def directory_add(): + + path = request.args.get("path") + name = request.args.get("name") + + print(name) + + if path is not None and name is not None: + d = Directory(path, True, [], name) + storage.save_directory(d) + + return redirect("/directory") + + return "Error" # todo better message + + +@app.route("/directory/") +def directory_manage(dir_id): + + directory = storage.dirs()[dir_id] + + return render_template("directory_manage.html", directory=directory) + + +@app.route("/directory//add_opt") +def directory_add_opt(dir_id): + + key = request.args.get("key") + value = request.args.get("value") + + if key is not None and value is not None: + storage.save_option(Option(key, value, dir_id)) + + return redirect("/directory/" + str(dir_id)) + + +@app.route("/directory//del_opt/") +def directory_del_opt(dir_id, opt_id): + + storage.del_option(opt_id) + + return redirect("/directory/" + str(dir_id)) + + +@app.route("/directory//del") +def directory_del(dir_id): + + storage.remove_directory(dir_id) + + return redirect("/directory") + + +@app.route("/task") +def task(): + + return + +@app.route("/dashboard") +def dashboard(): + + return render_template("dashboard.html") if __name__ == "__main__": diff --git a/spec/Crawler_spec.py b/spec/Crawler_spec.py index 7cd74ae..860ddec 100644 --- a/spec/Crawler_spec.py +++ b/spec/Crawler_spec.py @@ -1,10 +1,22 @@ from unittest import TestCase +from parsing import GenericFileParser, Sha1CheckSumCalculator, ExtensionMimeGuesser from crawler import Crawler class CrawlerTest(TestCase): def test_dir_walk(self): - c = Crawler() + c = Crawler([GenericFileParser([Sha1CheckSumCalculator()], ExtensionMimeGuesser())]) + c.crawl("test_folder") + + self.assertEqual(len(c.documents), 28) + + def test_get_parser_by_ext(self): + + c = Crawler([GenericFileParser([Sha1CheckSumCalculator()], ExtensionMimeGuesser())]) + + self.assertIsInstance(c.get_parser_by_ext("any"), GenericFileParser) + + # todo add more parsers here diff --git a/spec/FileParser_spec.py b/spec/FileParser_spec.py index e09ae05..8e0aadf 100644 --- a/spec/FileParser_spec.py +++ b/spec/FileParser_spec.py @@ -1,39 +1,52 @@ import os from unittest import TestCase -from crawler import GenericFileParser, Md5CheckSumCalculator, Sha1CheckSumCalculator, Sha256CheckSumCalculator + +from parsing import GenericFileParser, Md5CheckSumCalculator, Sha1CheckSumCalculator, Sha256CheckSumCalculator, ExtensionMimeGuesser class GenericFileParserTest(TestCase): def setUp(self): - if os.path.exists("test_parse"): - os.remove("test_parse") + if os.path.exists("test_parse.txt"): + os.remove("test_parse.txt") - test_file = open("test_parse", "w") + test_file = open("test_parse.txt", "w") test_file.write("12345678") test_file.close() + os.utime("test_parse.txt", (1330123456, 1330654321)) - self.parser = GenericFileParser([Md5CheckSumCalculator()]) + self.parser = GenericFileParser([Md5CheckSumCalculator()], ExtensionMimeGuesser()) def tearDown(self): - os.remove("test_parse") + os.remove("test_parse.txt") def test_parse_size(self): - result = self.parser.parse("test_parse") + result = self.parser.parse("test_parse.txt") self.assertEqual(result["size"], 8) def test_parse_name(self): - result = self.parser.parse("test_parse") + result = self.parser.parse("test_parse.txt") - self.assertEqual(result["name"], "test_parse") + self.assertEqual(result["name"], "test_parse.txt") def test_parse_md5(self): - result = self.parser.parse("test_parse") + result = self.parser.parse("test_parse.txt") self.assertEqual(result["md5"], "25D55AD283AA400AF464C76D713C07AD") + def test_mtime(self): + + result = self.parser.parse("test_parse.txt") + + self.assertEqual(result["mtime"], 1330654321) + + def test_mime(self): + + result = self.parser.parse("test_parse.txt") + self.assertEqual(result["mime"], "text/plain") + class Md5CheckSumCalculatorTest(TestCase): @@ -128,4 +141,7 @@ class Sha256CheckSumCalculatorTest(TestCase): self.assertEqual(result, "DA7606DC763306B700685A71E2E72A2D95F1291209E5DA344B82DA2508FC27C5") result = self.calculator.checksum("test_sha256_2") - self.assertEqual(result, "C39C7E0E7D84C9692F3C9C22E1EA0327DEBF1BF531B5738EEA8E79FE27EBC570") \ No newline at end of file + self.assertEqual(result, "C39C7E0E7D84C9692F3C9C22E1EA0327DEBF1BF531B5738EEA8E79FE27EBC570") + + + diff --git a/spec/Indexer_spec.py b/spec/Indexer_spec.py new file mode 100644 index 0000000..a43fa01 --- /dev/null +++ b/spec/Indexer_spec.py @@ -0,0 +1,16 @@ +from unittest import TestCase +from indexer import Indexer + + +class IndexerTest(TestCase): + + def test_create_bulk_query(self): + + docs = [{"name": "doc1"}, {"name": "doc2"}] + + result = Indexer.create_bulk_index_string(docs, "indexName") + + self.assertEqual(result, '{"index":{"_index":"indexName","_type":"file"}}\n' + '{"name": "doc1"}\n' + '{"index":{"_index":"indexName","_type":"file"}}\n' + '{"name": "doc2"}\n') diff --git a/spec/LocalStorage_spec.py b/spec/LocalStorage_spec.py index 81e8bef..b5b685c 100644 --- a/spec/LocalStorage_spec.py +++ b/spec/LocalStorage_spec.py @@ -1,10 +1,13 @@ from unittest import TestCase -from storage import LocalStorage, Directory, DuplicateDirectoryException, User +from storage import LocalStorage, Directory, DuplicateDirectoryException, User, DuplicateUserException, Option +import os class LocalStorageTest(TestCase): def setUp(self): + if os.path.exists("test_database.db"): + os.remove("test_database.db") s = LocalStorage("test_database.db") s.init_db("../database.sql") @@ -13,42 +16,59 @@ class LocalStorageTest(TestCase): storage = LocalStorage("test_database.db") - d = Directory("/some/directory", True, ["opt1", "opt2", "opt3"]) + d = Directory("/some/directory", True, [Option("key1", "val1"), Option("key2", "val2")], "An excellent name") - storage.save_directory(d) + dir_id = storage.save_directory(d) + + self.assertEqual(storage.dirs()[dir_id].enabled, True) + self.assertEqual(storage.dirs()[dir_id].options[0].key, "key1") + self.assertEqual(storage.dirs()[dir_id].options[0].value, "val1") + self.assertEqual(storage.dirs()[dir_id].options[0].dir_id, 1) - self.assertEqual(storage.dirs()["/some/directory"].enabled, True) - self.assertEqual(storage.dirs()["/some/directory"].options[0], "opt1") def test_save_and_retrieve_dir_persistent(self): s1 = LocalStorage("test_database.db") - d = Directory("/some/directory", True, ["opt1", "opt2", "opt3"]) + d = Directory("/some/directory", True, [Option("key1", "val1"), Option("key2", "val2")], "An excellent name") - s1.save_directory(d) + dir_id = s1.save_directory(d) s2 = LocalStorage("test_database.db") - self.assertEqual(s2.dirs()["/some/directory"].enabled, True) - self.assertEqual(s2.dirs()["/some/directory"].options[0], "opt1") + self.assertEqual(s2.dirs()[dir_id].enabled, True) + + self.assertEqual(s2.dirs()[dir_id].options[0].key, "key1") + self.assertEqual(s2.dirs()[dir_id].options[0].value, "val1") + self.assertEqual(s2.dirs()[dir_id].options[0].dir_id, 1) def test_reject_duplicate_path(self): s = LocalStorage("test_database.db") - d1 = Directory("/some/directory", True, ["opt1", "opt2"]) - d2 = Directory("/some/directory", True, ["opt1", "opt2"]) + d1 = Directory("/some/directory", True, [Option("key1", "val1"), Option("key2", "val2")], "An excellent name") + d2 = Directory("/some/directory", True, [Option("key1", "val1"), Option("key2", "val2")], "An excellent name") s.save_directory(d1) with self.assertRaises(DuplicateDirectoryException) as e: s.save_directory(d2) + def test_remove_dir(self): + s = LocalStorage("test_database.db") + + d = Directory("/some/directory", True, [Option("key1", "val1"), Option("key2", "val3")], "An excellent name") + dir_id = s.save_directory(d) + + s.remove_directory(dir_id) + + with self.assertRaises(KeyError): + _ = s.dirs()[dir_id] + def test_save_and_retrieve_user(self): s = LocalStorage("test_database.db") - u = User("bob", "anHashedPassword", True) + u = User("bob", b"anHashedPassword", True) s.save_user(u) @@ -66,12 +86,97 @@ class LocalStorageTest(TestCase): s = LocalStorage("test_database.db") - u = User("bob", b'$2b$14$VZEMbwAdy/HvLL/zh0.Iv.8XYnoZMz/LU9V4VKXLiuS.pthcUly2O', True) + u = User("bob", b'$2b$10$RakMb.3n/tl76sK7iVahJuklNYkR7f2Y4dsf73tPANwYBkp4VuJ7.', True) s.save_user(u) self.assertTrue(s.auth_user("bob", "test")) self.assertFalse(s.auth_user("bob", "wrong")) - self.assertFalse(s.auth_user("wrong", "test")) pass + + def test_reject_duplicate_user(self): + + s = LocalStorage("test_database.db") + + u1 = User("user1", b"anHashedPassword", True) + u2 = User("user1", b"anotherHashedPassword", True) + + s.save_user(u1) + + with self.assertRaises(DuplicateUserException) as e: + s.save_user(u2) + + def test_update_user(self): + s = LocalStorage("test_database.db") + + u = User("neil", b"anHashedPassword", True) + + s.save_user(u) + + u.admin = False + s.update_user(u) + + self.assertFalse(s.users()["neil"].admin) + + def test_remove_user(self): + + s = LocalStorage("test_database.db") + + u = User("martin", b"anHashedPassword", True) + s.save_user(u) + + s.remove_user(u.username) + + with self.assertRaises(KeyError): + _ = s.users()["martin"] + + def test_update_directory(self): + + s = LocalStorage("test_database.db") + + d = Directory("/some/directory", True, [Option("key1", "val1"), Option("key2", "val2")], "An excellent name") + + dir_id = s.save_directory(d) + + d.name = "A modified name" + d.path = "/another/directory" + d.id = dir_id + + s.update_directory(d) + + s2 = LocalStorage("test_database.db") + + self.assertEqual(s2.dirs()[dir_id].name, "A modified name") + self.assertEqual(len(s2.dirs()[dir_id].options), 2) + self.assertEqual(s2.dirs()[dir_id].path, "/another/directory") + + def test_save_option(self): + + s = LocalStorage("test_database.db") + + d = Directory("/some/directory", True, [Option("key1", "val1"), Option("key2", "val2")], "An excellent name") + dir_id = s.save_directory(d) + + opt_id = s.save_option(Option("key3", "val3", dir_id)) + + self.assertEqual(s.dirs()[dir_id].options[2].key, "key3") + self.assertEqual(s.dirs()[dir_id].options[2].value, "val3") + self.assertEqual(s.dirs()[dir_id].options[2].dir_id, dir_id) + self.assertEqual(opt_id, 3) + + def test_del_option(self): + + s = LocalStorage("test_database.db") + + d = Directory("/some/directory", True, [Option("key1", "val1"), Option("key2", "val2")], "An excellent name") + dir_id = s.save_directory(d) + s.del_option(1) + + self.assertEqual(len(s.dirs()[dir_id].options), 1) + self.assertEqual(s.dirs()[dir_id].options[0].key, "key2") + self.assertEqual(s.dirs()[dir_id].options[0].value, "val2") + self.assertEqual(s.dirs()[dir_id].options[0].dir_id, 1) + + + diff --git a/spec/MimeGuesser_spec.py b/spec/MimeGuesser_spec.py new file mode 100644 index 0000000..46950f8 --- /dev/null +++ b/spec/MimeGuesser_spec.py @@ -0,0 +1,23 @@ +from parsing import ContentMimeGuesser, ExtensionMimeGuesser +from unittest import TestCase + + +class MimeGuesserTest(TestCase): + + def test_content_guesser(self): + + guesser = ContentMimeGuesser() + + self.assertEqual("text/x-shellscript", guesser.guess_mime("test_folder/test_utf8.sh")) + self.assertEqual("text/plain", guesser.guess_mime("test_folder/more_books.json")) + self.assertEqual("application/java-archive", guesser.guess_mime("test_folder/post.jar")) + self.assertEqual("image/jpeg", guesser.guess_mime("test_folder/sample_1.jpg")) + + def test_extension_guesser(self): + + guesser = ExtensionMimeGuesser() + + self.assertEqual("text/x-sh", guesser.guess_mime("test_folder/test_utf8.sh")) + self.assertEqual("application/json", guesser.guess_mime("test_folder/more_books.json")) + self.assertEqual("application/java-archive", guesser.guess_mime("test_folder/post.jar")) + self.assertEqual("image/jpeg", guesser.guess_mime("test_folder/sample_1.jpg")) diff --git a/spec/test_folder/more_books.jsonl b/spec/test_folder/more_books.json similarity index 100% rename from spec/test_folder/more_books.jsonl rename to spec/test_folder/more_books.json diff --git a/static/css/keen-dashboards.css b/static/css/keen-dashboards.css new file mode 100644 index 0000000..80bcff9 --- /dev/null +++ b/static/css/keen-dashboards.css @@ -0,0 +1,72 @@ +.keen-dashboard { + background: #f2f2f2; + font-family: 'Gotham Rounded SSm A', 'Gotham Rounded SSm B', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +.keen-dataviz { + background: #fff; + border: 1px solid #e7e7e7; + border-radius: 2px; + box-sizing: border-box; +} +.keen-dataviz-title { + border-bottom: 1px solid #e7e7e7; + border-radius: 2px 2px 0 0; + font-size: 13px; + padding: 2px 10px 0; + text-transform: uppercase; +} +.keen-dataviz-stage { + padding: 10px; +} + +.keen-dataviz-notes { + background: #fbfbfb; + border-radius: 0 0 2px 2px; + border-top: 1px solid #e7e7e7; + font-family: 'Helvetica Neue', Helvetica, sans-serif; + font-size: 11px; + padding: 0 10px; +} + +.keen-dataviz .keen-dataviz-metric { + border-radius: 2px; +} + +.keen-dataviz .keen-spinner-indicator { + border-top-color: rgba(0, 187, 222, .4); +} + +.keen-dashboard .chart-wrapper { + background: #fff; + border: 1px solid #e2e2e2; + border-radius: 3px; + margin-bottom: 10px; +} +.keen-dashboard .chart-wrapper .chart-title { + border-bottom: 1px solid #d7d7d7; + color: #666; + font-size: 14px; + font-weight: 200; + padding: 7px 10px 4px; +} + +.keen-dashboard .chart-wrapper .chart-stage { + overflow: hidden; + padding: 5px 10px; + position: relative; +} + +.keen-dashboard .chart-wrapper .chart-notes { + background: #fbfbfb; + border-top: 1px solid #e2e2e2; + color: #808080; + font-size: 12px; + padding: 8px 10px 5px; +} + +.keen-dashboard .chart-wrapper .keen-dataviz, +.keen-dashboard .chart-wrapper .keen-dataviz-title, +.keen-dashboard .chart-stage .chart-title { + border: medium none; +} diff --git a/static/css/keen-static.css b/static/css/keen-static.css new file mode 100644 index 0000000..a31bd7a --- /dev/null +++ b/static/css/keen-static.css @@ -0,0 +1,155 @@ +a, a:focus, a:hover, a:active { + color: #00afd7; +} + +h1, h2, h3 { + font-family: "Gotham Rounded", "Helvetica Neue", Helvetica, Arial, sans-serif; + margin: 12px 0; +} +h1 { + font-size: 32px; + font-weight: 100; + letter-spacing: .02em; + line-height: 48px; + margin: 12px 0; +} +h2 { + color: #2a333c; + font-weight: 200; + font-size: 21px; +} +h3 { + color: rgb(84, 102, 120); + font-size: 21px; + font-weight: 500; + letter-spacing: -0.28px; + line-height: 29.39px; +} + +.btn { + background: transparent; + border: 1px solid white; +} + +.keen-logo { + height: 38px; + margin: 0 15px 0 0; + width: 150px; +} + +.navbar-toggle { + background-color: rgba(255,255,255,.25); +} +.navbar-toggle .icon-bar { + background: #fff; +} + + +.navbar-nav { + margin: 5px 0 0; +} +.navbar-nav > li > a { + font-size: 15px; + font-weight: 200; + letter-spacing: 0.03em; + padding-top: 19px; + text-shadow: 0 0 2px rgba(0,0,0,.1); +} +.navbar-nav > li > a:focus, +.navbar-nav > li > a:hover { + background: transparent none; +} + +.navbar-nav > li > a.navbar-btn { + background-color: rgba(255,255,255,.25); + border: medium none; + padding: 10px 15px; +} +.navbar-nav > li > a.navbar-btn:focus, +.navbar-nav > li > a.navbar-btn:hover { + background-color: rgba(255,255,255,.35); +} +.navbar-collapse { + box-shadow: none; +} + +.masthead { + background-color: #00afd7; + background-image: url("../img/bg-bars.png"); + background-position: 0 -290px; + background-repeat: repeat-x; + color: #fff; + margin: 0 0 24px; + padding: 20px 0; +} +.masthead h1 { + margin: 0; +} +.masthead small, +.masthead a, +.masthead a:focus, +.masthead a:hover, +.masthead a:active { + color: #fff; +} +.masthead p { + color: #b3e7f3; + font-weight: 100; + letter-spacing: .05em; +} + + + +.hero { + background-position: 50% 100%; + min-height: 450px; + text-align: center; +} +.hero h1 { + font-size: 48px; + margin: 120px 0 0; +} +.hero .lead { + margin-bottom: 32px; +} +.hero a.hero-btn { + border: 2px solid #fff; + display: block; + font-family: "Gotham Rounded", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 24px; + font-weight: 200; + margin: 0 auto 12px; + max-width: 320px; + padding: 12px 0 6px; +} +.hero a.hero-btn:focus, +.hero a.hero-btn:hover { + border-color: transparent; + background-color: #fff; + color: #00afd7; +} + + +.sample-item { + margin-bottom: 24px; +} + +.signup { + float: left; + display: inline-block; + vertical-align: middle; + margin-top: -6px; + margin-right: 10px; +} + +.love { + border-top: 1px solid #d7d7d7; + color: #546678; + margin: 24px 0 0; + padding: 15px 0; + text-align: center; +} + +.love p { + margin-bottom: 0; +} diff --git a/static/img/bg-bars.png b/static/img/bg-bars.png new file mode 100644 index 0000000000000000000000000000000000000000..d89303e3ded427475d50b25d21340e875daaad7d GIT binary patch literal 8480 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 ZYWzywFzTI90RDskSy|Ya=kIkr`#+K*ZpHur literal 0 HcmV?d00001 diff --git a/static/js/Chart.min.js b/static/js/Chart.min.js new file mode 100644 index 0000000..2130e2a --- /dev/null +++ b/static/js/Chart.min.js @@ -0,0 +1,10 @@ +/*! + * Chart.js + * http://chartjs.org/ + * Version: 2.7.1 + * + * Copyright 2017 Nick Downie + * Released under the MIT license + * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md + */ +!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).Chart=t()}}(function(){return function t(e,n,i){function a(r,l){if(!n[r]){if(!e[r]){var s="function"==typeof require&&require;if(!l&&s)return s(r,!0);if(o)return o(r,!0);var u=new Error("Cannot find module '"+r+"'");throw u.code="MODULE_NOT_FOUND",u}var d=n[r]={exports:{}};e[r][0].call(d.exports,function(t){var n=e[r][1][t];return a(n||t)},d,d.exports,t,e,n,i)}return n[r].exports}for(var o="function"==typeof require&&require,r=0;rn?(e+.05)/(n+.05):(n+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,n=(e[0]+t)%360;return e[0]=n<0?360+n:n,this.setValues("hsl",e),this},mix:function(t,e){var n=this,i=t,a=void 0===e?.5:e,o=2*a-1,r=n.alpha()-i.alpha(),l=((o*r==-1?o:(o+r)/(1+o*r))+1)/2,s=1-l;return this.rgb(l*n.red()+s*i.red(),l*n.green()+s*i.green(),l*n.blue()+s*i.blue()).alpha(n.alpha()*a+i.alpha()*(1-a))},toJSON:function(){return this.rgb()},clone:function(){var t,e,n=new o,i=this.values,a=n.values;for(var r in i)i.hasOwnProperty(r)&&(t=i[r],"[object Array]"===(e={}.toString.call(t))?a[r]=t.slice(0):"[object Number]"===e?a[r]=t:console.error("unexpected color value:",t));return n}},o.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},o.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},o.prototype.getValues=function(t){for(var e=this.values,n={},i=0;i.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)+.1805*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)),100*(.2126*e+.7152*n+.0722*i),100*(.0193*e+.1192*n+.9505*i)]}function d(t){var e,n,i,a=u(t),o=a[0],r=a[1],l=a[2];return o/=95.047,r/=100,l/=108.883,o=o>.008856?Math.pow(o,1/3):7.787*o+16/116,r=r>.008856?Math.pow(r,1/3):7.787*r+16/116,l=l>.008856?Math.pow(l,1/3):7.787*l+16/116,e=116*r-16,n=500*(o-r),i=200*(r-l),[e,n,i]}function c(t){var e,n,i,a,o,r=t[0]/360,l=t[1]/100,s=t[2]/100;if(0==l)return o=255*s,[o,o,o];e=2*s-(n=s<.5?s*(1+l):s+l-s*l),a=[0,0,0];for(var u=0;u<3;u++)(i=r+1/3*-(u-1))<0&&i++,i>1&&i--,o=6*i<1?e+6*(n-e)*i:2*i<1?n:3*i<2?e+(n-e)*(2/3-i)*6:e,a[u]=255*o;return a}function h(t){var e=t[0]/60,n=t[1]/100,i=t[2]/100,a=Math.floor(e)%6,o=e-Math.floor(e),r=255*i*(1-n),l=255*i*(1-n*o),s=255*i*(1-n*(1-o)),i=255*i;switch(a){case 0:return[i,s,r];case 1:return[l,i,r];case 2:return[r,i,s];case 3:return[r,l,i];case 4:return[s,r,i];case 5:return[i,r,l]}}function f(t){var e,n,i,a,o=t[0]/360,l=t[1]/100,s=t[2]/100,u=l+s;switch(u>1&&(l/=u,s/=u),e=Math.floor(6*o),n=1-s,i=6*o-e,0!=(1&e)&&(i=1-i),a=l+i*(n-l),e){default:case 6:case 0:r=n,g=a,b=l;break;case 1:r=a,g=n,b=l;break;case 2:r=l,g=n,b=a;break;case 3:r=l,g=a,b=n;break;case 4:r=a,g=l,b=n;break;case 5:r=n,g=l,b=a}return[255*r,255*g,255*b]}function p(t){var e,n,i,a=t[0]/100,o=t[1]/100,r=t[2]/100,l=t[3]/100;return e=1-Math.min(1,a*(1-l)+l),n=1-Math.min(1,o*(1-l)+l),i=1-Math.min(1,r*(1-l)+l),[255*e,255*n,255*i]}function v(t){var e,n,i,a=t[0]/100,o=t[1]/100,r=t[2]/100;return e=3.2406*a+-1.5372*o+-.4986*r,n=-.9689*a+1.8758*o+.0415*r,i=.0557*a+-.204*o+1.057*r,e=e>.0031308?1.055*Math.pow(e,1/2.4)-.055:e*=12.92,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:n*=12.92,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:i*=12.92,e=Math.min(Math.max(0,e),1),n=Math.min(Math.max(0,n),1),i=Math.min(Math.max(0,i),1),[255*e,255*n,255*i]}function m(t){var e,n,i,a=t[0],o=t[1],r=t[2];return a/=95.047,o/=100,r/=108.883,a=a>.008856?Math.pow(a,1/3):7.787*a+16/116,o=o>.008856?Math.pow(o,1/3):7.787*o+16/116,r=r>.008856?Math.pow(r,1/3):7.787*r+16/116,e=116*o-16,n=500*(a-o),i=200*(o-r),[e,n,i]}function x(t){var e,n,i,a,o=t[0],r=t[1],l=t[2];return o<=8?a=(n=100*o/903.3)/100*7.787+16/116:(n=100*Math.pow((o+16)/116,3),a=Math.pow(n/100,1/3)),e=e/95.047<=.008856?e=95.047*(r/500+a-16/116)/7.787:95.047*Math.pow(r/500+a,3),i=i/108.883<=.008859?i=108.883*(a-l/200-16/116)/7.787:108.883*Math.pow(a-l/200,3),[e,n,i]}function y(t){var e,n,i,a=t[0],o=t[1],r=t[2];return e=Math.atan2(r,o),(n=360*e/2/Math.PI)<0&&(n+=360),i=Math.sqrt(o*o+r*r),[a,i,n]}function k(t){return v(x(t))}function w(t){var e,n,i,a=t[0],o=t[1];return i=t[2]/360*2*Math.PI,e=o*Math.cos(i),n=o*Math.sin(i),[a,e,n]}function M(t){return S[t]}e.exports={rgb2hsl:i,rgb2hsv:a,rgb2hwb:o,rgb2cmyk:l,rgb2keyword:s,rgb2xyz:u,rgb2lab:d,rgb2lch:function(t){return y(d(t))},hsl2rgb:c,hsl2hsv:function(t){var e,n,i=t[0],a=t[1]/100,o=t[2]/100;return 0===o?[0,0,0]:(o*=2,a*=o<=1?o:2-o,n=(o+a)/2,e=2*a/(o+a),[i,100*e,100*n])},hsl2hwb:function(t){return o(c(t))},hsl2cmyk:function(t){return l(c(t))},hsl2keyword:function(t){return s(c(t))},hsv2rgb:h,hsv2hsl:function(t){var e,n,i=t[0],a=t[1]/100,o=t[2]/100;return n=(2-a)*o,e=a*o,e/=n<=1?n:2-n,e=e||0,n/=2,[i,100*e,100*n]},hsv2hwb:function(t){return o(h(t))},hsv2cmyk:function(t){return l(h(t))},hsv2keyword:function(t){return s(h(t))},hwb2rgb:f,hwb2hsl:function(t){return i(f(t))},hwb2hsv:function(t){return a(f(t))},hwb2cmyk:function(t){return l(f(t))},hwb2keyword:function(t){return s(f(t))},cmyk2rgb:p,cmyk2hsl:function(t){return i(p(t))},cmyk2hsv:function(t){return a(p(t))},cmyk2hwb:function(t){return o(p(t))},cmyk2keyword:function(t){return s(p(t))},keyword2rgb:M,keyword2hsl:function(t){return i(M(t))},keyword2hsv:function(t){return a(M(t))},keyword2hwb:function(t){return o(M(t))},keyword2cmyk:function(t){return l(M(t))},keyword2lab:function(t){return d(M(t))},keyword2xyz:function(t){return u(M(t))},xyz2rgb:v,xyz2lab:m,xyz2lch:function(t){return y(m(t))},lab2xyz:x,lab2rgb:k,lab2lch:y,lch2lab:w,lch2xyz:function(t){return x(w(t))},lch2rgb:function(t){return k(w(t))}};var S={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},C={};for(var _ in S)C[JSON.stringify(S[_])]=_},{}],5:[function(t,e,n){var i=t(4),a=function(){return new u};for(var o in i){a[o+"Raw"]=function(t){return function(e){return"number"==typeof e&&(e=Array.prototype.slice.call(arguments)),i[t](e)}}(o);var r=/(\w+)2(\w+)/.exec(o),l=r[1],s=r[2];(a[l]=a[l]||{})[s]=a[o]=function(t){return function(e){"number"==typeof e&&(e=Array.prototype.slice.call(arguments));var n=i[t](e);if("string"==typeof n||void 0===n)return n;for(var a=0;a0&&(t[0].yLabel?n=t[0].yLabel:e.labels.length>0&&t[0].index=0&&a>0)&&(v+=a));return o=c.getPixelForValue(v),r=c.getPixelForValue(v+f),l=(r-o)/2,{size:l,base:o,head:r,center:r+l/2}},calculateBarIndexPixels:function(t,e,n){var i,a,r,l,s,u,d=this,c=n.scale.options,h=d.getStackIndex(t),f=n.pixels,g=f[e],p=f.length,v=n.start,m=n.end;return 1===p?(i=g>v?g-v:m-g,a=g0&&(i=(g-f[e-1])/2,e===p-1&&(a=i)),e');var n=t.data,i=n.datasets,a=n.labels;if(i.length)for(var o=0;o'),a[o]&&e.push(a[o]),e.push("");return e.push(""),e.join("")},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map(function(n,i){var a=t.getDatasetMeta(0),r=e.datasets[0],l=a.data[i],s=l&&l.custom||{},u=o.valueAtIndexOrDefault,d=t.options.elements.arc;return{text:n,fillStyle:s.backgroundColor?s.backgroundColor:u(r.backgroundColor,i,d.backgroundColor),strokeStyle:s.borderColor?s.borderColor:u(r.borderColor,i,d.borderColor),lineWidth:s.borderWidth?s.borderWidth:u(r.borderWidth,i,d.borderWidth),hidden:isNaN(r.data[i])||a.data[i].hidden,index:i}}):[]}},onClick:function(t,e){var n,i,a,o=e.index,r=this.chart;for(n=0,i=(r.data.datasets||[]).length;n=Math.PI?-1:g<-Math.PI?1:0))+f,v={x:Math.cos(g),y:Math.sin(g)},m={x:Math.cos(p),y:Math.sin(p)},b=g<=0&&p>=0||g<=2*Math.PI&&2*Math.PI<=p,x=g<=.5*Math.PI&&.5*Math.PI<=p||g<=2.5*Math.PI&&2.5*Math.PI<=p,y=g<=-Math.PI&&-Math.PI<=p||g<=Math.PI&&Math.PI<=p,k=g<=.5*-Math.PI&&.5*-Math.PI<=p||g<=1.5*Math.PI&&1.5*Math.PI<=p,w=h/100,M={x:y?-1:Math.min(v.x*(v.x<0?1:w),m.x*(m.x<0?1:w)),y:k?-1:Math.min(v.y*(v.y<0?1:w),m.y*(m.y<0?1:w))},S={x:b?1:Math.max(v.x*(v.x>0?1:w),m.x*(m.x>0?1:w)),y:x?1:Math.max(v.y*(v.y>0?1:w),m.y*(m.y>0?1:w))},C={width:.5*(S.x-M.x),height:.5*(S.y-M.y)};u=Math.min(l/C.width,s/C.height),d={x:-.5*(S.x+M.x),y:-.5*(S.y+M.y)}}n.borderWidth=e.getMaxBorderWidth(c.data),n.outerRadius=Math.max((u-n.borderWidth)/2,0),n.innerRadius=Math.max(h?n.outerRadius/100*h:0,0),n.radiusLength=(n.outerRadius-n.innerRadius)/n.getVisibleDatasetCount(),n.offsetX=d.x*n.outerRadius,n.offsetY=d.y*n.outerRadius,c.total=e.calculateTotal(),e.outerRadius=n.outerRadius-n.radiusLength*e.getRingIndex(e.index),e.innerRadius=Math.max(e.outerRadius-n.radiusLength,0),o.each(c.data,function(n,i){e.updateElement(n,i,t)})},updateElement:function(t,e,n){var i=this,a=i.chart,r=a.chartArea,l=a.options,s=l.animation,u=(r.left+r.right)/2,d=(r.top+r.bottom)/2,c=l.rotation,h=l.rotation,f=i.getDataset(),g=n&&s.animateRotate?0:t.hidden?0:i.calculateCircumference(f.data[e])*(l.circumference/(2*Math.PI)),p=n&&s.animateScale?0:i.innerRadius,v=n&&s.animateScale?0:i.outerRadius,m=o.valueAtIndexOrDefault;o.extend(t,{_datasetIndex:i.index,_index:e,_model:{x:u+a.offsetX,y:d+a.offsetY,startAngle:c,endAngle:h,circumference:g,outerRadius:v,innerRadius:p,label:m(f.label,e,a.data.labels[e])}});var b=t._model;this.removeHoverStyle(t),n&&s.animateRotate||(b.startAngle=0===e?l.rotation:i.getMeta().data[e-1]._model.endAngle,b.endAngle=b.startAngle+b.circumference),t.pivot()},removeHoverStyle:function(e){t.DatasetController.prototype.removeHoverStyle.call(this,e,this.chart.options.elements.arc)},calculateTotal:function(){var t,e=this.getDataset(),n=this.getMeta(),i=0;return o.each(n.data,function(n,a){t=e.data[a],isNaN(t)||n.hidden||(i+=Math.abs(t))}),i},calculateCircumference:function(t){var e=this.getMeta().total;return e>0&&!isNaN(t)?2*Math.PI*(t/e):0},getMaxBorderWidth:function(t){for(var e,n,i=0,a=this.index,o=t.length,r=0;r(i=e>i?e:i)?n:i;return i}})}},{25:25,40:40,45:45}],18:[function(t,e,n){"use strict";var i=t(25),a=t(40),o=t(45);i._set("line",{showLines:!0,spanGaps:!1,hover:{mode:"label"},scales:{xAxes:[{type:"category",id:"x-axis-0"}],yAxes:[{type:"linear",id:"y-axis-0"}]}}),e.exports=function(t){function e(t,e){return o.valueOrDefault(t.showLine,e.showLines)}t.controllers.line=t.DatasetController.extend({datasetElementType:a.Line,dataElementType:a.Point,update:function(t){var n,i,a,r=this,l=r.getMeta(),s=l.dataset,u=l.data||[],d=r.chart.options,c=d.elements.line,h=r.getScaleForId(l.yAxisID),f=r.getDataset(),g=e(f,d);for(g&&(a=s.custom||{},void 0!==f.tension&&void 0===f.lineTension&&(f.lineTension=f.tension),s._scale=h,s._datasetIndex=r.index,s._children=u,s._model={spanGaps:f.spanGaps?f.spanGaps:d.spanGaps,tension:a.tension?a.tension:o.valueOrDefault(f.lineTension,c.tension),backgroundColor:a.backgroundColor?a.backgroundColor:f.backgroundColor||c.backgroundColor,borderWidth:a.borderWidth?a.borderWidth:f.borderWidth||c.borderWidth,borderColor:a.borderColor?a.borderColor:f.borderColor||c.borderColor,borderCapStyle:a.borderCapStyle?a.borderCapStyle:f.borderCapStyle||c.borderCapStyle,borderDash:a.borderDash?a.borderDash:f.borderDash||c.borderDash,borderDashOffset:a.borderDashOffset?a.borderDashOffset:f.borderDashOffset||c.borderDashOffset,borderJoinStyle:a.borderJoinStyle?a.borderJoinStyle:f.borderJoinStyle||c.borderJoinStyle,fill:a.fill?a.fill:void 0!==f.fill?f.fill:c.fill,steppedLine:a.steppedLine?a.steppedLine:o.valueOrDefault(f.steppedLine,c.stepped),cubicInterpolationMode:a.cubicInterpolationMode?a.cubicInterpolationMode:o.valueOrDefault(f.cubicInterpolationMode,c.cubicInterpolationMode)},s.pivot()),n=0,i=u.length;n');var n=t.data,i=n.datasets,a=n.labels;if(i.length)for(var o=0;o'),a[o]&&e.push(a[o]),e.push("");return e.push(""),e.join("")},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map(function(n,i){var a=t.getDatasetMeta(0),r=e.datasets[0],l=a.data[i].custom||{},s=o.valueAtIndexOrDefault,u=t.options.elements.arc;return{text:n,fillStyle:l.backgroundColor?l.backgroundColor:s(r.backgroundColor,i,u.backgroundColor),strokeStyle:l.borderColor?l.borderColor:s(r.borderColor,i,u.borderColor),lineWidth:l.borderWidth?l.borderWidth:s(r.borderWidth,i,u.borderWidth),hidden:isNaN(r.data[i])||a.data[i].hidden,index:i}}):[]}},onClick:function(t,e){var n,i,a,o=e.index,r=this.chart;for(n=0,i=(r.data.datasets||[]).length;n0&&!isNaN(t)?2*Math.PI/e:0}})}},{25:25,40:40,45:45}],20:[function(t,e,n){"use strict";var i=t(25),a=t(40),o=t(45);i._set("radar",{scale:{type:"radialLinear"},elements:{line:{tension:0}}}),e.exports=function(t){t.controllers.radar=t.DatasetController.extend({datasetElementType:a.Line,dataElementType:a.Point,linkScales:o.noop,update:function(t){var e=this,n=e.getMeta(),i=n.dataset,a=n.data,r=i.custom||{},l=e.getDataset(),s=e.chart.options.elements.line,u=e.chart.scale;void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),o.extend(n.dataset,{_datasetIndex:e.index,_scale:u,_children:a,_loop:!0,_model:{tension:r.tension?r.tension:o.valueOrDefault(l.lineTension,s.tension),backgroundColor:r.backgroundColor?r.backgroundColor:l.backgroundColor||s.backgroundColor,borderWidth:r.borderWidth?r.borderWidth:l.borderWidth||s.borderWidth,borderColor:r.borderColor?r.borderColor:l.borderColor||s.borderColor,fill:r.fill?r.fill:void 0!==l.fill?l.fill:s.fill,borderCapStyle:r.borderCapStyle?r.borderCapStyle:l.borderCapStyle||s.borderCapStyle,borderDash:r.borderDash?r.borderDash:l.borderDash||s.borderDash,borderDashOffset:r.borderDashOffset?r.borderDashOffset:l.borderDashOffset||s.borderDashOffset,borderJoinStyle:r.borderJoinStyle?r.borderJoinStyle:l.borderJoinStyle||s.borderJoinStyle}}),n.dataset.pivot(),o.each(a,function(n,i){e.updateElement(n,i,t)},e),e.updateBezierControlPoints()},updateElement:function(t,e,n){var i=this,a=t.custom||{},r=i.getDataset(),l=i.chart.scale,s=i.chart.options.elements.point,u=l.getPointPositionForValue(e,r.data[e]);void 0!==r.radius&&void 0===r.pointRadius&&(r.pointRadius=r.radius),void 0!==r.hitRadius&&void 0===r.pointHitRadius&&(r.pointHitRadius=r.hitRadius),o.extend(t,{_datasetIndex:i.index,_index:e,_scale:l,_model:{x:n?l.xCenter:u.x,y:n?l.yCenter:u.y,tension:a.tension?a.tension:o.valueOrDefault(r.lineTension,i.chart.options.elements.line.tension),radius:a.radius?a.radius:o.valueAtIndexOrDefault(r.pointRadius,e,s.radius),backgroundColor:a.backgroundColor?a.backgroundColor:o.valueAtIndexOrDefault(r.pointBackgroundColor,e,s.backgroundColor),borderColor:a.borderColor?a.borderColor:o.valueAtIndexOrDefault(r.pointBorderColor,e,s.borderColor),borderWidth:a.borderWidth?a.borderWidth:o.valueAtIndexOrDefault(r.pointBorderWidth,e,s.borderWidth),pointStyle:a.pointStyle?a.pointStyle:o.valueAtIndexOrDefault(r.pointStyle,e,s.pointStyle),hitRadius:a.hitRadius?a.hitRadius:o.valueAtIndexOrDefault(r.pointHitRadius,e,s.hitRadius)}}),t._model.skip=a.skip?a.skip:isNaN(t._model.x)||isNaN(t._model.y)},updateBezierControlPoints:function(){var t=this.chart.chartArea,e=this.getMeta();o.each(e.data,function(n,i){var a=n._model,r=o.splineCurve(o.previousItem(e.data,i,!0)._model,a,o.nextItem(e.data,i,!0)._model,a.tension);a.controlPointPreviousX=Math.max(Math.min(r.previous.x,t.right),t.left),a.controlPointPreviousY=Math.max(Math.min(r.previous.y,t.bottom),t.top),a.controlPointNextX=Math.max(Math.min(r.next.x,t.right),t.left),a.controlPointNextY=Math.max(Math.min(r.next.y,t.bottom),t.top),n.pivot()})},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],n=t.custom||{},i=t._index,a=t._model;a.radius=n.hoverRadius?n.hoverRadius:o.valueAtIndexOrDefault(e.pointHoverRadius,i,this.chart.options.elements.point.hoverRadius),a.backgroundColor=n.hoverBackgroundColor?n.hoverBackgroundColor:o.valueAtIndexOrDefault(e.pointHoverBackgroundColor,i,o.getHoverColor(a.backgroundColor)),a.borderColor=n.hoverBorderColor?n.hoverBorderColor:o.valueAtIndexOrDefault(e.pointHoverBorderColor,i,o.getHoverColor(a.borderColor)),a.borderWidth=n.hoverBorderWidth?n.hoverBorderWidth:o.valueAtIndexOrDefault(e.pointHoverBorderWidth,i,a.borderWidth)},removeHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],n=t.custom||{},i=t._index,a=t._model,r=this.chart.options.elements.point;a.radius=n.radius?n.radius:o.valueAtIndexOrDefault(e.pointRadius,i,r.radius),a.backgroundColor=n.backgroundColor?n.backgroundColor:o.valueAtIndexOrDefault(e.pointBackgroundColor,i,r.backgroundColor),a.borderColor=n.borderColor?n.borderColor:o.valueAtIndexOrDefault(e.pointBorderColor,i,r.borderColor),a.borderWidth=n.borderWidth?n.borderWidth:o.valueAtIndexOrDefault(e.pointBorderWidth,i,r.borderWidth)}})}},{25:25,40:40,45:45}],21:[function(t,e,n){"use strict";t(25)._set("scatter",{hover:{mode:"single"},scales:{xAxes:[{id:"x-axis-1",type:"linear",position:"bottom"}],yAxes:[{id:"y-axis-1",type:"linear",position:"left"}]},showLines:!1,tooltips:{callbacks:{title:function(){return""},label:function(t){return"("+t.xLabel+", "+t.yLabel+")"}}}}),e.exports=function(t){t.controllers.scatter=t.controllers.line}},{25:25}],22:[function(t,e,n){"use strict";var i=t(25),a=t(26),o=t(45);i._set("global",{animation:{duration:1e3,easing:"easeOutQuart",onProgress:o.noop,onComplete:o.noop}}),e.exports=function(t){t.Animation=a.extend({chart:null,currentStep:0,numSteps:60,easing:"",render:null,onAnimationProgress:null,onAnimationComplete:null}),t.animationService={frameDuration:17,animations:[],dropFrames:0,request:null,addAnimation:function(t,e,n,i){var a,o,r=this.animations;for(e.chart=t,i||(t.animating=!0),a=0,o=r.length;a1&&(n=Math.floor(t.dropFrames),t.dropFrames=t.dropFrames%1),t.advance(1+n);var i=Date.now();t.dropFrames+=(i-e)/t.frameDuration,t.animations.length>0&&t.requestAnimationFrame()},advance:function(t){for(var e,n,i=this.animations,a=0;a=e.numSteps?(o.callback(e.onAnimationComplete,[e],n),n.animating=!1,i.splice(a,1)):++a}},Object.defineProperty(t.Animation.prototype,"animationObject",{get:function(){return this}}),Object.defineProperty(t.Animation.prototype,"chartInstance",{get:function(){return this.chart},set:function(t){this.chart=t}})}},{25:25,26:26,45:45}],23:[function(t,e,n){"use strict";var i=t(25),a=t(45),o=t(28),r=t(48);e.exports=function(t){function e(t){var e=(t=t||{}).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=a.configMerge(i.global,i[t.type],t.options||{}),t}function n(t){var e=t.options;e.scale?t.scale.options=e.scale:e.scales&&e.scales.xAxes.concat(e.scales.yAxes).forEach(function(e){t.scales[e.id].options=e}),t.tooltip._options=e.tooltips}function l(t){return"top"===t||"bottom"===t}var s=t.plugins;t.types={},t.instances={},t.controllers={},a.extend(t.prototype,{construct:function(n,i){var o=this;i=e(i);var l=r.acquireContext(n,i),s=l&&l.canvas,u=s&&s.height,d=s&&s.width;o.id=a.uid(),o.ctx=l,o.canvas=s,o.config=i,o.width=d,o.height=u,o.aspectRatio=u?d/u:null,o.options=i.options,o._bufferedRender=!1,o.chart=o,o.controller=o,t.instances[o.id]=o,Object.defineProperty(o,"data",{get:function(){return o.config.data},set:function(t){o.config.data=t}}),l&&s?(o.initialize(),o.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return s.notify(t,"beforeInit"),a.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.ensureScalesHaveIDs(),t.buildScales(),t.initToolTip(),s.notify(t,"afterInit"),t},clear:function(){return a.canvas.clear(this),this},stop:function(){return t.animationService.cancelAnimation(this),this},resize:function(t){var e=this,n=e.options,i=e.canvas,o=n.maintainAspectRatio&&e.aspectRatio||null,r=Math.max(0,Math.floor(a.getMaximumWidth(i))),l=Math.max(0,Math.floor(o?r/o:a.getMaximumHeight(i)));if((e.width!==r||e.height!==l)&&(i.width=e.width=r,i.height=e.height=l,i.style.width=r+"px",i.style.height=l+"px",a.retinaScale(e,n.devicePixelRatio),!t)){var u={width:r,height:l};s.notify(e,"resize",[u]),e.options.onResize&&e.options.onResize(e,u),e.stop(),e.update(e.options.responsiveAnimationDuration)}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},n=t.scale;a.each(e.xAxes,function(t,e){t.id=t.id||"x-axis-"+e}),a.each(e.yAxes,function(t,e){t.id=t.id||"y-axis-"+e}),n&&(n.id=n.id||"scale")},buildScales:function(){var e=this,n=e.options,i=e.scales={},o=[];n.scales&&(o=o.concat((n.scales.xAxes||[]).map(function(t){return{options:t,dtype:"category",dposition:"bottom"}}),(n.scales.yAxes||[]).map(function(t){return{options:t,dtype:"linear",dposition:"left"}}))),n.scale&&o.push({options:n.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),a.each(o,function(n){var o=n.options,r=a.valueOrDefault(o.type,n.dtype),s=t.scaleService.getScaleConstructor(r);if(s){l(o.position)!==l(n.dposition)&&(o.position=n.dposition);var u=new s({id:o.id,options:o,ctx:e.ctx,chart:e});i[u.id]=u,u.mergeTicksOptions(),n.isDefault&&(e.scale=u)}}),t.scaleService.addScalesToLayout(this)},buildOrUpdateControllers:function(){var e=this,n=[],i=[];return a.each(e.data.datasets,function(a,o){var r=e.getDatasetMeta(o),l=a.type||e.config.type;if(r.type&&r.type!==l&&(e.destroyDatasetMeta(o),r=e.getDatasetMeta(o)),r.type=l,n.push(r.type),r.controller)r.controller.updateIndex(o);else{var s=t.controllers[r.type];if(void 0===s)throw new Error('"'+r.type+'" is not a chart type.');r.controller=new s(e,o),i.push(r.controller)}},e),i},resetElements:function(){var t=this;a.each(t.data.datasets,function(e,n){t.getDatasetMeta(n).controller.reset()},t)},reset:function(){this.resetElements(),this.tooltip.initialize()},update:function(t){var e=this;if(t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]}),n(e),!1!==s.notify(e,"beforeUpdate")){e.tooltip._data=e.data;var i=e.buildOrUpdateControllers();a.each(e.data.datasets,function(t,n){e.getDatasetMeta(n).controller.buildOrUpdateElements()},e),e.updateLayout(),a.each(i,function(t){t.reset()}),e.updateDatasets(),e.tooltip.initialize(),e.lastActive=[],s.notify(e,"afterUpdate"),e._bufferedRender?e._bufferedRequest={duration:t.duration,easing:t.easing,lazy:t.lazy}:e.render(t)}},updateLayout:function(){var e=this;!1!==s.notify(e,"beforeLayout")&&(t.layoutService.update(this,this.width,this.height),s.notify(e,"afterScaleUpdate"),s.notify(e,"afterLayout"))},updateDatasets:function(){var t=this;if(!1!==s.notify(t,"beforeDatasetsUpdate")){for(var e=0,n=t.data.datasets.length;e=0;--n)e.isDatasetVisible(n)&&e.drawDataset(n,t);s.notify(e,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var n=this,i=n.getDatasetMeta(t),a={meta:i,index:t,easingValue:e};!1!==s.notify(n,"beforeDatasetDraw",[a])&&(i.controller.draw(e),s.notify(n,"afterDatasetDraw",[a]))},_drawTooltip:function(t){var e=this,n=e.tooltip,i={tooltip:n,easingValue:t};!1!==s.notify(e,"beforeTooltipDraw",[i])&&(n.draw(),s.notify(e,"afterTooltipDraw",[i]))},getElementAtEvent:function(t){return o.modes.single(this,t)},getElementsAtEvent:function(t){return o.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return o.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,n){var i=o.modes[e];return"function"==typeof i?i(this,t,n):[]},getDatasetAtEvent:function(t){return o.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this,n=e.data.datasets[t];n._meta||(n._meta={});var i=n._meta[e.id];return i||(i=n._meta[e.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null}),i},getVisibleDatasetCount:function(){for(var t=0,e=0,n=this.data.datasets.length;e0||(a.forEach(function(e){delete t[e]}),delete t._chartjs)}}var a=["push","pop","shift","splice","unshift"];t.DatasetController=function(t,e){this.initialize(t,e)},i.extend(t.DatasetController.prototype,{datasetElementType:null,dataElementType:null,initialize:function(t,e){var n=this;n.chart=t,n.index=e,n.linkScales(),n.addElements()},updateIndex:function(t){this.index=t},linkScales:function(){var t=this,e=t.getMeta(),n=t.getDataset();null===e.xAxisID&&(e.xAxisID=n.xAxisID||t.chart.options.scales.xAxes[0].id),null===e.yAxisID&&(e.yAxisID=n.yAxisID||t.chart.options.scales.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},reset:function(){this.update(!0)},destroy:function(){this._data&&n(this._data,this)},createMetaDataset:function(){var t=this,e=t.datasetElementType;return e&&new e({_chart:t.chart,_datasetIndex:t.index})},createMetaData:function(t){var e=this,n=e.dataElementType;return n&&new n({_chart:e.chart,_datasetIndex:e.index,_index:t})},addElements:function(){var t,e,n=this,i=n.getMeta(),a=n.getDataset().data||[],o=i.data;for(t=0,e=a.length;ti&&t.insertElements(i,a-i)},insertElements:function(t,e){for(var n=0;n=n[e].length&&n[e].push({}),!n[e][r].type||s.type&&s.type!==n[e][r].type?o.merge(n[e][r],[t.scaleService.getScaleDefaults(l),s]):o.merge(n[e][r],s)}else o._merger(e,n,i,a)}})},o.where=function(t,e){if(o.isArray(t)&&Array.prototype.filter)return t.filter(e);var n=[];return o.each(t,function(t){e(t)&&n.push(t)}),n},o.findIndex=Array.prototype.findIndex?function(t,e,n){return t.findIndex(e,n)}:function(t,e,n){n=void 0===n?t:n;for(var i=0,a=t.length;i=0;i--){var a=t[i];if(e(a))return a}},o.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},o.almostEquals=function(t,e,n){return Math.abs(t-e)t},o.max=function(t){return t.reduce(function(t,e){return isNaN(e)?t:Math.max(t,e)},Number.NEGATIVE_INFINITY)},o.min=function(t){return t.reduce(function(t,e){return isNaN(e)?t:Math.min(t,e)},Number.POSITIVE_INFINITY)},o.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0==(t=+t)||isNaN(t)?t:t>0?1:-1},o.log10=Math.log10?function(t){return Math.log10(t)}:function(t){return Math.log(t)/Math.LN10},o.toRadians=function(t){return t*(Math.PI/180)},o.toDegrees=function(t){return t*(180/Math.PI)},o.getAngleFromPoint=function(t,e){var n=e.x-t.x,i=e.y-t.y,a=Math.sqrt(n*n+i*i),o=Math.atan2(i,n);return o<-.5*Math.PI&&(o+=2*Math.PI),{angle:o,distance:a}},o.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},o.aliasPixel=function(t){return t%2==0?0:.5},o.splineCurve=function(t,e,n,i){var a=t.skip?e:t,o=e,r=n.skip?e:n,l=Math.sqrt(Math.pow(o.x-a.x,2)+Math.pow(o.y-a.y,2)),s=Math.sqrt(Math.pow(r.x-o.x,2)+Math.pow(r.y-o.y,2)),u=l/(l+s),d=s/(l+s),c=i*(u=isNaN(u)?0:u),h=i*(d=isNaN(d)?0:d);return{previous:{x:o.x-c*(r.x-a.x),y:o.y-c*(r.y-a.y)},next:{x:o.x+h*(r.x-a.x),y:o.y+h*(r.y-a.y)}}},o.EPSILON=Number.EPSILON||1e-14,o.splineCurveMonotone=function(t){var e,n,i,a,r=(t||[]).map(function(t){return{model:t._model,deltaK:0,mK:0}}),l=r.length;for(e=0;e0?r[e-1]:null,(a=e0?r[e-1]:null,a=e=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},o.previousItem=function(t,e,n){return n?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},o.niceNum=function(t,e){var n=Math.floor(o.log10(t)),i=t/Math.pow(10,n);return(e?i<1.5?1:i<3?2:i<7?5:10:i<=1?1:i<=2?2:i<=5?5:10)*Math.pow(10,n)},o.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},o.getRelativePosition=function(t,e){var n,i,a=t.originalEvent||t,r=t.currentTarget||t.srcElement,l=r.getBoundingClientRect(),s=a.touches;s&&s.length>0?(n=s[0].clientX,i=s[0].clientY):(n=a.clientX,i=a.clientY);var u=parseFloat(o.getStyle(r,"padding-left")),d=parseFloat(o.getStyle(r,"padding-top")),c=parseFloat(o.getStyle(r,"padding-right")),h=parseFloat(o.getStyle(r,"padding-bottom")),f=l.right-l.left-u-c,g=l.bottom-l.top-d-h;return n=Math.round((n-l.left-u)/f*r.width/e.currentDevicePixelRatio),i=Math.round((i-l.top-d)/g*r.height/e.currentDevicePixelRatio),{x:n,y:i}},o.getConstraintWidth=function(t){return r(t,"max-width","clientWidth")},o.getConstraintHeight=function(t){return r(t,"max-height","clientHeight")},o.getMaximumWidth=function(t){var e=t.parentNode;if(!e)return t.clientWidth;var n=parseInt(o.getStyle(e,"padding-left"),10),i=parseInt(o.getStyle(e,"padding-right"),10),a=e.clientWidth-n-i,r=o.getConstraintWidth(t);return isNaN(r)?a:Math.min(a,r)},o.getMaximumHeight=function(t){var e=t.parentNode;if(!e)return t.clientHeight;var n=parseInt(o.getStyle(e,"padding-top"),10),i=parseInt(o.getStyle(e,"padding-bottom"),10),a=e.clientHeight-n-i,r=o.getConstraintHeight(t);return isNaN(r)?a:Math.min(a,r)},o.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},o.retinaScale=function(t,e){var n=t.currentDevicePixelRatio=e||window.devicePixelRatio||1;if(1!==n){var i=t.canvas,a=t.height,o=t.width;i.height=a*n,i.width=o*n,t.ctx.scale(n,n),i.style.height=a+"px",i.style.width=o+"px"}},o.fontString=function(t,e,n){return e+" "+t+"px "+n},o.longestText=function(t,e,n,i){var a=(i=i||{}).data=i.data||{},r=i.garbageCollect=i.garbageCollect||[];i.font!==e&&(a=i.data={},r=i.garbageCollect=[],i.font=e),t.font=e;var l=0;o.each(n,function(e){void 0!==e&&null!==e&&!0!==o.isArray(e)?l=o.measureText(t,a,r,l,e):o.isArray(e)&&o.each(e,function(e){void 0===e||null===e||o.isArray(e)||(l=o.measureText(t,a,r,l,e))})});var s=r.length/2;if(s>n.length){for(var u=0;ui&&(i=o),i},o.numberOfLabelLines=function(t){var e=1;return o.each(t,function(t){o.isArray(t)&&t.length>e&&(e=t.length)}),e},o.color=i?function(t){return t instanceof CanvasGradient&&(t=a.global.defaultColor),i(t)}:function(t){return console.error("Color.js not found!"),t},o.getHoverColor=function(t){return t instanceof CanvasPattern?t:o.color(t).saturate(.5).darken(.1).rgbString()}}},{25:25,3:3,45:45}],28:[function(t,e,n){"use strict";function i(t,e){return t.native?{x:t.x,y:t.y}:u.getRelativePosition(t,e)}function a(t,e){var n,i,a,o,r;for(i=0,o=t.data.datasets.length;i0&&(u=t.getDatasetMeta(u[0]._datasetIndex).data),u},"x-axis":function(t,e){return s(t,e,{intersect:!1})},point:function(t,e){return o(t,i(e,t))},nearest:function(t,e,n){var a=i(e,t);n.axis=n.axis||"xy";var o=l(n.axis),s=r(t,a,n.intersect,o);return s.length>1&&s.sort(function(t,e){var n=t.getArea()-e.getArea();return 0===n&&(n=t._datasetIndex-e._datasetIndex),n}),s.slice(0,1)},x:function(t,e,n){var o=i(e,t),r=[],l=!1;return a(t,function(t){t.inXRange(o.x)&&r.push(t),t.inRange(o.x,o.y)&&(l=!0)}),n.intersect&&!l&&(r=[]),r},y:function(t,e,n){var o=i(e,t),r=[],l=!1;return a(t,function(t){t.inYRange(o.y)&&r.push(t),t.inRange(o.x,o.y)&&(l=!0)}),n.intersect&&!l&&(r=[]),r}}}},{45:45}],29:[function(t,e,n){"use strict";t(25)._set("global",{responsive:!0,responsiveAnimationDuration:0,maintainAspectRatio:!0,events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,defaultColor:"rgba(0,0,0,0.1)",defaultFontColor:"#666",defaultFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",defaultFontSize:12,defaultFontStyle:"normal",showLines:!0,elements:{},layout:{padding:{top:0,right:0,bottom:0,left:0}}}),e.exports=function(){var t=function(t,e){return this.construct(t,e),this};return t.Chart=t,t}},{25:25}],30:[function(t,e,n){"use strict";var i=t(45);e.exports=function(t){function e(t,e){return i.where(t,function(t){return t.position===e})}function n(t,e){t.forEach(function(t,e){return t._tmpIndex_=e,t}),t.sort(function(t,n){var i=e?n:t,a=e?t:n;return i.weight===a.weight?i._tmpIndex_-a._tmpIndex_:i.weight-a.weight}),t.forEach(function(t){delete t._tmpIndex_})}t.layoutService={defaults:{},addBox:function(t,e){t.boxes||(t.boxes=[]),e.fullWidth=e.fullWidth||!1,e.position=e.position||"top",e.weight=e.weight||0,t.boxes.push(e)},removeBox:function(t,e){var n=t.boxes?t.boxes.indexOf(e):-1;-1!==n&&t.boxes.splice(n,1)},configure:function(t,e,n){for(var i,a=["fullWidth","position","weight"],o=a.length,r=0;rh&&st.maxHeight){s--;break}s++,c=u*d}t.labelRotation=s},afterCalculateTickRotation:function(){l.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){l.callback(this.options.beforeFit,[this])},fit:function(){var t=this,a=t.minSize={width:0,height:0},o=i(t._ticks),r=t.options,u=r.ticks,d=r.scaleLabel,c=r.gridLines,h=r.display,f=t.isHorizontal(),g=n(u),p=r.gridLines.tickMarkLength;if(a.width=f?t.isFullWidth()?t.maxWidth-t.margins.left-t.margins.right:t.maxWidth:h&&c.drawTicks?p:0,a.height=f?h&&c.drawTicks?p:0:t.maxHeight,d.display&&h){var v=s(d)+l.options.toPadding(d.padding).height;f?a.height+=v:a.width+=v}if(u.display&&h){var m=l.longestText(t.ctx,g.font,o,t.longestTextCache),b=l.numberOfLabelLines(o),x=.5*g.size,y=t.options.ticks.padding;if(f){t.longestLabelWidth=m;var k=l.toRadians(t.labelRotation),w=Math.cos(k),M=Math.sin(k)*m+g.size*b+x*(b-1)+x;a.height=Math.min(t.maxHeight,a.height+M+y),t.ctx.font=g.font;var S=e(t.ctx,o[0],g.font),C=e(t.ctx,o[o.length-1],g.font);0!==t.labelRotation?(t.paddingLeft="bottom"===r.position?w*S+3:w*x+3,t.paddingRight="bottom"===r.position?w*x+3:w*C+3):(t.paddingLeft=S/2+3,t.paddingRight=C/2+3)}else u.mirror?m=0:m+=y+x,a.width=Math.min(t.maxWidth,a.width+m),t.paddingTop=g.size/2,t.paddingBottom=g.size/2}t.handleMargins(),t.width=a.width,t.height=a.height},handleMargins:function(){var t=this;t.margins&&(t.paddingLeft=Math.max(t.paddingLeft-t.margins.left,0),t.paddingTop=Math.max(t.paddingTop-t.margins.top,0),t.paddingRight=Math.max(t.paddingRight-t.margins.right,0),t.paddingBottom=Math.max(t.paddingBottom-t.margins.bottom,0))},afterFit:function(){l.callback(this.options.afterFit,[this])},isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(l.isNullOrUndef(t))return NaN;if("number"==typeof t&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},getLabelForIndex:l.noop,getPixelForValue:l.noop,getValueForPixel:l.noop,getPixelForTick:function(t){var e=this,n=e.options.offset;if(e.isHorizontal()){var i=(e.width-(e.paddingLeft+e.paddingRight))/Math.max(e._ticks.length-(n?0:1),1),a=i*t+e.paddingLeft;n&&(a+=i/2);var o=e.left+Math.round(a);return o+=e.isFullWidth()?e.margins.left:0}var r=e.height-(e.paddingTop+e.paddingBottom);return e.top+t*(r/(e._ticks.length-1))},getPixelForDecimal:function(t){var e=this;if(e.isHorizontal()){var n=(e.width-(e.paddingLeft+e.paddingRight))*t+e.paddingLeft,i=e.left+Math.round(n);return i+=e.isFullWidth()?e.margins.left:0}return e.top+t*e.height},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this,e=t.min,n=t.max;return t.beginAtZero?0:e<0&&n<0?n:e>0&&n>0?e:0},_autoSkip:function(t){var e,n,i,a,o=this,r=o.isHorizontal(),s=o.options.ticks.minor,u=t.length,d=l.toRadians(o.labelRotation),c=Math.cos(d),h=o.longestLabelWidth*c,f=[];for(s.maxTicksLimit&&(a=s.maxTicksLimit),r&&(e=!1,(h+s.autoSkipPadding)*u>o.width-(o.paddingLeft+o.paddingRight)&&(e=1+Math.floor((h+s.autoSkipPadding)*u/(o.width-(o.paddingLeft+o.paddingRight)))),a&&u>a&&(e=Math.max(e,Math.floor(u/a)))),n=0;n1&&n%e>0||n%e==0&&n+e>=u)&&n!==u-1&&delete i.label,f.push(i);return f},draw:function(t){var e=this,i=e.options;if(i.display){var r=e.ctx,u=o.global,d=i.ticks.minor,c=i.ticks.major||d,h=i.gridLines,f=i.scaleLabel,g=0!==e.labelRotation,p=e.isHorizontal(),v=d.autoSkip?e._autoSkip(e.getTicks()):e.getTicks(),m=l.valueOrDefault(d.fontColor,u.defaultFontColor),b=n(d),x=l.valueOrDefault(c.fontColor,u.defaultFontColor),y=n(c),k=h.drawTicks?h.tickMarkLength:0,w=l.valueOrDefault(f.fontColor,u.defaultFontColor),M=n(f),S=l.options.toPadding(f.padding),C=l.toRadians(e.labelRotation),_=[],D="right"===i.position?e.left:e.right-k,I="right"===i.position?e.left+k:e.right,P="bottom"===i.position?e.top:e.bottom-k,A="bottom"===i.position?e.top+k:e.bottom;if(l.each(v,function(n,o){if(!l.isNullOrUndef(n.label)){var r,s,c,f,m=n.label;o===e.zeroLineIndex&&i.offset===h.offsetGridLines?(r=h.zeroLineWidth,s=h.zeroLineColor,c=h.zeroLineBorderDash,f=h.zeroLineBorderDashOffset):(r=l.valueAtIndexOrDefault(h.lineWidth,o),s=l.valueAtIndexOrDefault(h.color,o),c=l.valueOrDefault(h.borderDash,u.borderDash),f=l.valueOrDefault(h.borderDashOffset,u.borderDashOffset));var b,x,y,w,M,S,T,F,O,R,L="middle",z="middle",B=d.padding;if(p){var W=k+B;"bottom"===i.position?(z=g?"middle":"top",L=g?"right":"center",R=e.top+W):(z=g?"middle":"bottom",L=g?"left":"center",R=e.bottom-W);var N=a(e,o,h.offsetGridLines&&v.length>1);N1);H0)n=t.stepSize;else{var o=i.niceNum(e.max-e.min,!1);n=i.niceNum(o/(t.maxTicks-1),!0)}var r=Math.floor(e.min/n)*n,l=Math.ceil(e.max/n)*n;t.min&&t.max&&t.stepSize&&i.almostWhole((t.max-t.min)/t.stepSize,n/1e3)&&(r=t.min,l=t.max);var s=(l-r)/n;s=i.almostEquals(s,Math.round(s),n/1e3)?Math.round(s):Math.ceil(s),a.push(void 0!==t.min?t.min:r);for(var u=1;u3?n[2]-n[1]:n[1]-n[0];Math.abs(a)>1&&t!==Math.floor(t)&&(a=t-Math.floor(t));var o=i.log10(Math.abs(a)),r="";if(0!==t){var l=-1*Math.floor(o);l=Math.max(Math.min(l,20),0),r=t.toFixed(l)}else r="0";return r},logarithmic:function(t,e,n){var a=t/Math.pow(10,Math.floor(i.log10(t)));return 0===t?"0":1===a||2===a||5===a||0===e||e===n.length-1?t.toExponential():""}}}},{45:45}],35:[function(t,e,n){"use strict";var i=t(25),a=t(26),o=t(45);i._set("global",{tooltips:{enabled:!0,custom:null,mode:"nearest",position:"average",intersect:!0,backgroundColor:"rgba(0,0,0,0.8)",titleFontStyle:"bold",titleSpacing:2,titleMarginBottom:6,titleFontColor:"#fff",titleAlign:"left",bodySpacing:2,bodyFontColor:"#fff",bodyAlign:"left",footerFontStyle:"bold",footerSpacing:2,footerMarginTop:6,footerFontColor:"#fff",footerAlign:"left",yPadding:6,xPadding:6,caretPadding:2,caretSize:5,cornerRadius:6,multiKeyBackground:"#fff",displayColors:!0,borderColor:"rgba(0,0,0,0)",borderWidth:0,callbacks:{beforeTitle:o.noop,title:function(t,e){var n="",i=e.labels,a=i?i.length:0;if(t.length>0){var o=t[0];o.xLabel?n=o.xLabel:a>0&&o.indexi.height-e.height&&(r="bottom");var l,s,u,d,c,h=(a.left+a.right)/2,f=(a.top+a.bottom)/2;"center"===r?(l=function(t){return t<=h},s=function(t){return t>h}):(l=function(t){return t<=e.width/2},s=function(t){return t>=i.width-e.width/2}),u=function(t){return t+e.width>i.width},d=function(t){return t-e.width<0},c=function(t){return t<=f?"top":"bottom"},l(n.x)?(o="left",u(n.x)&&(o="center",r=c(n.y))):s(n.x)&&(o="right",d(n.x)&&(o="center",r=c(n.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:o,yAlign:g.yAlign?g.yAlign:r}}function d(t,e,n){var i=t.x,a=t.y,o=t.caretSize,r=t.caretPadding,l=t.cornerRadius,s=n.xAlign,u=n.yAlign,d=o+r,c=l+r;return"right"===s?i-=e.width:"center"===s&&(i-=e.width/2),"top"===u?a+=d:a-="bottom"===u?e.height+d:e.height/2,"center"===u?"left"===s?i+=d:"right"===s&&(i-=d):"left"===s?i-=c:"right"===s&&(i+=c),{x:i,y:a}}t.Tooltip=a.extend({initialize:function(){this._model=l(this._options),this._lastActive=[]},getTitle:function(){var t=this,e=t._options.callbacks,i=e.beforeTitle.apply(t,arguments),a=e.title.apply(t,arguments),o=e.afterTitle.apply(t,arguments),r=[];return r=n(r,i),r=n(r,a),r=n(r,o)},getBeforeBody:function(){var t=this._options.callbacks.beforeBody.apply(this,arguments);return o.isArray(t)?t:void 0!==t?[t]:[]},getBody:function(t,e){var i=this,a=i._options.callbacks,r=[];return o.each(t,function(t){var o={before:[],lines:[],after:[]};n(o.before,a.beforeLabel.call(i,t,e)),n(o.lines,a.label.call(i,t,e)),n(o.after,a.afterLabel.call(i,t,e)),r.push(o)}),r},getAfterBody:function(){var t=this._options.callbacks.afterBody.apply(this,arguments);return o.isArray(t)?t:void 0!==t?[t]:[]},getFooter:function(){var t=this,e=t._options.callbacks,i=e.beforeFooter.apply(t,arguments),a=e.footer.apply(t,arguments),o=e.afterFooter.apply(t,arguments),r=[];return r=n(r,i),r=n(r,a),r=n(r,o)},update:function(e){var n,i,a=this,c=a._options,h=a._model,f=a._model=l(c),g=a._active,p=a._data,v={xAlign:h.xAlign,yAlign:h.yAlign},m={x:h.x,y:h.y},b={width:h.width,height:h.height},x={x:h.caretX,y:h.caretY};if(g.length){f.opacity=1;var y=[],k=[];x=t.Tooltip.positioners[c.position].call(a,g,a._eventPosition);var w=[];for(n=0,i=g.length;n0&&i.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var n={width:e.width,height:e.height},i={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,o=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&o&&(this.drawBackground(i,e,t,n,a),i.x+=e.xPadding,i.y+=e.yPadding,this.drawTitle(i,e,t,a),this.drawBody(i,e,t,a),this.drawFooter(i,e,t,a))}},handleEvent:function(t){var e=this,n=e._options,i=!1;if(e._lastActive=e._lastActive||[],"mouseout"===t.type?e._active=[]:e._active=e._chart.getElementsAtEventForMode(t,n.mode,n),!(i=!o.arrayEquals(e._active,e._lastActive)))return!1;if(e._lastActive=e._active,n.enabled||n.custom){e._eventPosition={x:t.x,y:t.y};var a=e._model;e.update(!0),e.pivot(),i|=a.x!==e._model.x||a.y!==e._model.y}return i}}),t.Tooltip.positioners={average:function(t){if(!t.length)return!1;var e,n,i=0,a=0,o=0;for(e=0,n=t.length;es;)a-=2*Math.PI;for(;a=l&&a<=s,d=r>=n.innerRadius&&r<=n.outerRadius;return u&&d}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,n=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,n=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},draw:function(){var t=this._chart.ctx,e=this._view,n=e.startAngle,i=e.endAngle;t.beginPath(),t.arc(e.x,e.y,e.outerRadius,n,i),t.arc(e.x,e.y,e.innerRadius,i,n,!0),t.closePath(),t.strokeStyle=e.borderColor,t.lineWidth=e.borderWidth,t.fillStyle=e.backgroundColor,t.fill(),t.lineJoin="bevel",e.borderWidth&&t.stroke()}})},{25:25,26:26,45:45}],37:[function(t,e,n){"use strict";var i=t(25),a=t(26),o=t(45),r=i.global;i._set("global",{elements:{line:{tension:.4,backgroundColor:r.defaultColor,borderWidth:3,borderColor:r.defaultColor,borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",capBezierPoints:!0,fill:!0}}}),e.exports=a.extend({draw:function(){var t,e,n,i,a=this,l=a._view,s=a._chart.ctx,u=l.spanGaps,d=a._children.slice(),c=r.elements.line,h=-1;for(a._loop&&d.length&&d.push(d[0]),s.save(),s.lineCap=l.borderCapStyle||c.borderCapStyle,s.setLineDash&&s.setLineDash(l.borderDash||c.borderDash),s.lineDashOffset=l.borderDashOffset||c.borderDashOffset,s.lineJoin=l.borderJoinStyle||c.borderJoinStyle,s.lineWidth=l.borderWidth||c.borderWidth,s.strokeStyle=l.borderColor||r.defaultColor,s.beginPath(),h=-1,t=0;te?1:-1,r=1,l=u.borderSkipped||"left"):(e=u.x-u.width/2,n=u.x+u.width/2,i=u.y,o=1,r=(a=u.base)>i?1:-1,l=u.borderSkipped||"bottom"),d){var c=Math.min(Math.abs(e-n),Math.abs(i-a)),h=(d=d>c?c:d)/2,f=e+("left"!==l?h*o:0),g=n+("right"!==l?-h*o:0),p=i+("top"!==l?h*r:0),v=a+("bottom"!==l?-h*r:0);f!==g&&(i=p,a=v),p!==v&&(e=f,n=g)}s.beginPath(),s.fillStyle=u.backgroundColor,s.strokeStyle=u.borderColor,s.lineWidth=d;var m=[[e,a],[e,i],[n,i],[n,a]],b=["bottom","left","top","right"].indexOf(l,0);-1===b&&(b=0);var x=t(0);s.moveTo(x[0],x[1]);for(var y=1;y<4;y++)x=t(y),s.lineTo(x[0],x[1]);s.fill(),d&&s.stroke()},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){var n=!1;if(this._view){var i=a(this);n=t>=i.left&&t<=i.right&&e>=i.top&&e<=i.bottom}return n},inLabelRange:function(t,e){var n=this;if(!n._view)return!1;var o=a(n);return i(n)?t>=o.left&&t<=o.right:e>=o.top&&e<=o.bottom},inXRange:function(t){var e=a(this);return t>=e.left&&t<=e.right},inYRange:function(t){var e=a(this);return t>=e.top&&t<=e.bottom},getCenterPoint:function(){var t,e,n=this._view;return i(this)?(t=n.x,e=(n.y+n.base)/2):(t=(n.x+n.base)/2,e=n.y),{x:t,y:e}},getArea:function(){var t=this._view;return t.width*Math.abs(t.y-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}})},{25:25,26:26}],40:[function(t,e,n){"use strict";e.exports={},e.exports.Arc=t(36),e.exports.Line=t(37),e.exports.Point=t(38),e.exports.Rectangle=t(39)},{36:36,37:37,38:38,39:39}],41:[function(t,e,n){"use strict";var i=t(42),n=e.exports={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,n,i,a,o){if(o){var r=Math.min(o,i/2),l=Math.min(o,a/2);t.moveTo(e+r,n),t.lineTo(e+i-r,n),t.quadraticCurveTo(e+i,n,e+i,n+l),t.lineTo(e+i,n+a-l),t.quadraticCurveTo(e+i,n+a,e+i-r,n+a),t.lineTo(e+r,n+a),t.quadraticCurveTo(e,n+a,e,n+a-l),t.lineTo(e,n+l),t.quadraticCurveTo(e,n,e+r,n)}else t.rect(e,n,i,a)},drawPoint:function(t,e,n,i,a){var o,r,l,s,u,d;if(!e||"object"!=typeof e||"[object HTMLImageElement]"!==(o=e.toString())&&"[object HTMLCanvasElement]"!==o){if(!(isNaN(n)||n<=0)){switch(e){default:t.beginPath(),t.arc(i,a,n,0,2*Math.PI),t.closePath(),t.fill();break;case"triangle":t.beginPath(),u=(r=3*n/Math.sqrt(3))*Math.sqrt(3)/2,t.moveTo(i-r/2,a+u/3),t.lineTo(i+r/2,a+u/3),t.lineTo(i,a-2*u/3),t.closePath(),t.fill();break;case"rect":d=1/Math.SQRT2*n,t.beginPath(),t.fillRect(i-d,a-d,2*d,2*d),t.strokeRect(i-d,a-d,2*d,2*d);break;case"rectRounded":var c=n/Math.SQRT2,h=i-c,f=a-c,g=Math.SQRT2*n;t.beginPath(),this.roundedRect(t,h,f,g,g,n/2),t.closePath(),t.fill();break;case"rectRot":d=1/Math.SQRT2*n,t.beginPath(),t.moveTo(i-d,a),t.lineTo(i,a+d),t.lineTo(i+d,a),t.lineTo(i,a-d),t.closePath(),t.fill();break;case"cross":t.beginPath(),t.moveTo(i,a+n),t.lineTo(i,a-n),t.moveTo(i-n,a),t.lineTo(i+n,a),t.closePath();break;case"crossRot":t.beginPath(),l=Math.cos(Math.PI/4)*n,s=Math.sin(Math.PI/4)*n,t.moveTo(i-l,a-s),t.lineTo(i+l,a+s),t.moveTo(i-l,a+s),t.lineTo(i+l,a-s),t.closePath();break;case"star":t.beginPath(),t.moveTo(i,a+n),t.lineTo(i,a-n),t.moveTo(i-n,a),t.lineTo(i+n,a),l=Math.cos(Math.PI/4)*n,s=Math.sin(Math.PI/4)*n,t.moveTo(i-l,a-s),t.lineTo(i+l,a+s),t.moveTo(i-l,a+s),t.lineTo(i+l,a-s),t.closePath();break;case"line":t.beginPath(),t.moveTo(i-n,a),t.lineTo(i+n,a),t.closePath();break;case"dash":t.beginPath(),t.moveTo(i,a),t.lineTo(i+n,a),t.closePath()}t.stroke()}}else t.drawImage(e,i-e.width/2,a-e.height/2,e.width,e.height)},clipArea:function(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()},unclipArea:function(t){t.restore()},lineTo:function(t,e,n,i){if(n.steppedLine)return"after"===n.steppedLine&&!i||"after"!==n.steppedLine&&i?t.lineTo(e.x,n.y):t.lineTo(n.x,e.y),void t.lineTo(n.x,n.y);n.tension?t.bezierCurveTo(i?e.controlPointPreviousX:e.controlPointNextX,i?e.controlPointPreviousY:e.controlPointNextY,i?n.controlPointNextX:n.controlPointPreviousX,i?n.controlPointNextY:n.controlPointPreviousY,n.x,n.y):t.lineTo(n.x,n.y)}};i.clear=n.clear,i.drawRoundedRectangle=function(t){t.beginPath(),n.roundedRect.apply(n,arguments),t.closePath()}},{42:42}],42:[function(t,e,n){"use strict";var i={noop:function(){},uid:function(){var t=0;return function(){return t++}}(),isNullOrUndef:function(t){return null===t||void 0===t},isArray:Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)},isObject:function(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)},valueOrDefault:function(t,e){return void 0===t?e:t},valueAtIndexOrDefault:function(t,e,n){return i.valueOrDefault(i.isArray(t)?t[e]:t,n)},callback:function(t,e,n){if(t&&"function"==typeof t.call)return t.apply(n,e)},each:function(t,e,n,a){var o,r,l;if(i.isArray(t))if(r=t.length,a)for(o=r-1;o>=0;o--)e.call(n,t[o],o);else for(o=0;o=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),-i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n))},easeOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),i*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/n)+1)},easeInOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:2==(t/=.5)?1:(n||(n=.45),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),t<1?i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*-.5:i*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:function(t){return 1-a.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*a.easeInBounce(2*t):.5*a.easeOutBounce(2*t-1)+.5}};e.exports={effects:a},i.easingEffects=a},{42:42}],44:[function(t,e,n){"use strict";var i=t(42);e.exports={toLineHeight:function(t,e){var n=(""+t).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);if(!n||"normal"===n[1])return 1.2*e;switch(t=+n[2],n[3]){case"px":return t;case"%":t/=100}return e*t},toPadding:function(t){var e,n,a,o;return i.isObject(t)?(e=+t.top||0,n=+t.right||0,a=+t.bottom||0,o=+t.left||0):e=n=a=o=+t||0,{top:e,right:n,bottom:a,left:o,height:e+a,width:o+n}},resolve:function(t,e,n){var a,o,r;for(a=0,o=t.length;a
';var a=e.childNodes[0],r=e.childNodes[1];e._reset=function(){a.scrollLeft=1e6,a.scrollTop=1e6,r.scrollLeft=1e6,r.scrollTop=1e6};var l=function(){e._reset(),t()};return o(a,"scroll",l.bind(a,"expand")),o(r,"scroll",l.bind(r,"shrink")),e}function c(t,e){var n=t[m]||(t[m]={}),i=n.renderProxy=function(t){t.animationName===y&&e()};v.each(k,function(e){o(t,e,i)}),n.reflow=!!t.offsetParent,t.classList.add(x)}function h(t){var e=t[m]||{},n=e.renderProxy;n&&(v.each(k,function(e){r(t,e,n)}),delete e.renderProxy),t.classList.remove(x)}function f(t,e,n){var i=t[m]||(t[m]={}),a=i.resizer=d(u(function(){if(i.resizer)return e(l("resize",n))}));c(t,function(){if(i.resizer){var e=t.parentNode;e&&e!==a.parentNode&&e.insertBefore(a,e.firstChild),a._reset()}})}function g(t){var e=t[m]||{},n=e.resizer;delete e.resizer,h(t),n&&n.parentNode&&n.parentNode.removeChild(n)}function p(t,e){var n=t._style||document.createElement("style");t._style||(t._style=n,e="/* Chart.js */\n"+e,n.setAttribute("type","text/css"),document.getElementsByTagName("head")[0].appendChild(n)),n.appendChild(document.createTextNode(e))}var v=t(45),m="$chartjs",b="chartjs-",x=b+"render-monitor",y=b+"render-animation",k=["animationstart","webkitAnimationStart"],w={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},M=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}()&&{passive:!0};e.exports={_enabled:"undefined"!=typeof window&&"undefined"!=typeof document,initialize:function(){var t="from{opacity:0.99}to{opacity:1}";p(this,"@-webkit-keyframes "+y+"{"+t+"}@keyframes "+y+"{"+t+"}."+x+"{-webkit-animation:"+y+" 0.001s;animation:"+y+" 0.001s;}")},acquireContext:function(t,e){"string"==typeof t?t=document.getElementById(t):t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas);var n=t&&t.getContext&&t.getContext("2d");return n&&n.canvas===t?(a(t,e),n):null},releaseContext:function(t){var e=t.canvas;if(e[m]){var n=e[m].initial;["height","width"].forEach(function(t){var i=n[t];v.isNullOrUndef(i)?e.removeAttribute(t):e.setAttribute(t,i)}),v.each(n.style||{},function(t,n){e.style[n]=t}),e.width=e.width,delete e[m]}},addEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=n[m]||(n[m]={});o(i,e,(a.proxies||(a.proxies={}))[t.id+"_"+e]=function(e){n(s(e,t))})}else f(i,n,t)},removeEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=((n[m]||{}).proxies||{})[t.id+"_"+e];a&&r(i,e,a)}else g(i)}},v.addEvent=o,v.removeEvent=r},{45:45}],48:[function(t,e,n){"use strict";var i=t(45),a=t(46),o=t(47),r=o._enabled?o:a;e.exports=i.extend({initialize:function(){},acquireContext:function(){},releaseContext:function(){},addEventListener:function(){},removeEventListener:function(){}},r)},{45:45,46:46,47:47}],49:[function(t,e,n){"use strict";var i=t(25),a=t(40),o=t(45);i._set("global",{plugins:{filler:{propagate:!0}}}),e.exports=function(){function t(t,e,n){var i,a=t._model||{},o=a.fill;if(void 0===o&&(o=!!a.backgroundColor),!1===o||null===o)return!1;if(!0===o)return"origin";if(i=parseFloat(o,10),isFinite(i)&&Math.floor(i)===i)return"-"!==o[0]&&"+"!==o[0]||(i=e+i),!(i===e||i<0||i>=n)&&i;switch(o){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return o;default:return!1}}function e(t){var e,n=t.el._model||{},i=t.el._scale||{},a=t.fill,o=null;if(isFinite(a))return null;if("start"===a?o=void 0===n.scaleBottom?i.bottom:n.scaleBottom:"end"===a?o=void 0===n.scaleTop?i.top:n.scaleTop:void 0!==n.scaleZero?o=n.scaleZero:i.getBasePosition?o=i.getBasePosition():i.getBasePixel&&(o=i.getBasePixel()),void 0!==o&&null!==o){if(void 0!==o.x&&void 0!==o.y)return o;if("number"==typeof o&&isFinite(o))return e=i.isHorizontal(),{x:e?o:null,y:e?null:o}}return null}function n(t,e,n){var i,a=t[e].fill,o=[e];if(!n)return a;for(;!1!==a&&-1===o.indexOf(a);){if(!isFinite(a))return a;if(!(i=t[a]))return!1;if(i.visible)return a;o.push(a),a=i.fill}return!1}function r(t){var e=t.fill,n="dataset";return!1===e?null:(isFinite(e)||(n="boundary"),d[n](t))}function l(t){return t&&!t.skip}function s(t,e,n,i,a){var r;if(i&&a){for(t.moveTo(e[0].x,e[0].y),r=1;r0;--r)o.canvas.lineTo(t,n[r],n[r-1],!0)}}function u(t,e,n,i,a,o){var r,u,d,c,h,f,g,p=e.length,v=i.spanGaps,m=[],b=[],x=0,y=0;for(t.beginPath(),r=0,u=p+!!o;r');for(var n=0;n'),t.data.datasets[n].label&&e.push(t.data.datasets[n].label),e.push("");return e.push(""),e.join("")}}),e.exports=function(t){function e(t,e){return t.usePointStyle?e*Math.SQRT2:t.boxWidth}function n(e,n){var i=new t.Legend({ctx:e.ctx,options:n,chart:e});r.configure(e,i,n),r.addBox(e,i),e.legend=i}var r=t.layoutService,l=o.noop;return t.Legend=a.extend({initialize:function(t){o.extend(this,t),this.legendHitBoxes=[],this.doughnutMode=!1},beforeUpdate:l,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:l,beforeSetDimensions:l,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:l,beforeBuildLabels:l,buildLabels:function(){var t=this,e=t.options.labels||{},n=o.callback(e.generateLabels,[t.chart],t)||[];e.filter&&(n=n.filter(function(n){return e.filter(n,t.chart.data)})),t.options.reverse&&n.reverse(),t.legendItems=n},afterBuildLabels:l,beforeFit:l,fit:function(){var t=this,n=t.options,a=n.labels,r=n.display,l=t.ctx,s=i.global,u=o.valueOrDefault,d=u(a.fontSize,s.defaultFontSize),c=u(a.fontStyle,s.defaultFontStyle),h=u(a.fontFamily,s.defaultFontFamily),f=o.fontString(d,c,h),g=t.legendHitBoxes=[],p=t.minSize,v=t.isHorizontal();if(v?(p.width=t.maxWidth,p.height=r?10:0):(p.width=r?10:0,p.height=t.maxHeight),r)if(l.font=f,v){var m=t.lineWidths=[0],b=t.legendItems.length?d+a.padding:0;l.textAlign="left",l.textBaseline="top",o.each(t.legendItems,function(n,i){var o=e(a,d)+d/2+l.measureText(n.text).width;m[m.length-1]+o+a.padding>=t.width&&(b+=d+a.padding,m[m.length]=t.left),g[i]={left:0,top:0,width:o,height:d},m[m.length-1]+=o+a.padding}),p.height+=b}else{var x=a.padding,y=t.columnWidths=[],k=a.padding,w=0,M=0,S=d+x;o.each(t.legendItems,function(t,n){var i=e(a,d)+d/2+l.measureText(t.text).width;M+S>p.height&&(k+=w+a.padding,y.push(w),w=0,M=0),w=Math.max(w,i),M+=S,g[n]={left:0,top:0,width:i,height:d}}),k+=w,y.push(w),p.width+=k}t.width=p.width,t.height=p.height},afterFit:l,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,n=t.options,a=n.labels,r=i.global,l=r.elements.line,s=t.width,u=t.lineWidths;if(n.display){var d,c=t.ctx,h=o.valueOrDefault,f=h(a.fontColor,r.defaultFontColor),g=h(a.fontSize,r.defaultFontSize),p=h(a.fontStyle,r.defaultFontStyle),v=h(a.fontFamily,r.defaultFontFamily),m=o.fontString(g,p,v);c.textAlign="left",c.textBaseline="middle",c.lineWidth=.5,c.strokeStyle=f,c.fillStyle=f,c.font=m;var b=e(a,g),x=t.legendHitBoxes,y=function(t,e,i){if(!(isNaN(b)||b<=0)){c.save(),c.fillStyle=h(i.fillStyle,r.defaultColor),c.lineCap=h(i.lineCap,l.borderCapStyle),c.lineDashOffset=h(i.lineDashOffset,l.borderDashOffset),c.lineJoin=h(i.lineJoin,l.borderJoinStyle),c.lineWidth=h(i.lineWidth,l.borderWidth),c.strokeStyle=h(i.strokeStyle,r.defaultColor);var a=0===h(i.lineWidth,l.borderWidth);if(c.setLineDash&&c.setLineDash(h(i.lineDash,l.borderDash)),n.labels&&n.labels.usePointStyle){var s=g*Math.SQRT2/2,u=s/Math.SQRT2,d=t+u,f=e+u;o.canvas.drawPoint(c,i.pointStyle,s,d,f)}else a||c.strokeRect(t,e,b,g),c.fillRect(t,e,b,g);c.restore()}},k=function(t,e,n,i){var a=g/2,o=b+a+t,r=e+a;c.fillText(n.text,o,r),n.hidden&&(c.beginPath(),c.lineWidth=2,c.moveTo(o,r),c.lineTo(o+i,r),c.stroke())},w=t.isHorizontal();d=w?{x:t.left+(s-u[0])/2,y:t.top+a.padding,line:0}:{x:t.left+a.padding,y:t.top+a.padding,line:0};var M=g+a.padding;o.each(t.legendItems,function(e,n){var i=c.measureText(e.text).width,o=b+g/2+i,r=d.x,l=d.y;w?r+o>=s&&(l=d.y+=M,d.line++,r=d.x=t.left+(s-u[d.line])/2):l+M>t.bottom&&(r=d.x=r+t.columnWidths[d.line]+a.padding,l=d.y=t.top+a.padding,d.line++),y(r,l,e),x[n].left=r,x[n].top=l,k(r,l,e,i),w?d.x+=o+a.padding:d.y+=M})}},handleEvent:function(t){var e=this,n=e.options,i="mouseup"===t.type?"click":t.type,a=!1;if("mousemove"===i){if(!n.onHover)return}else{if("click"!==i)return;if(!n.onClick)return}var o=t.x,r=t.y;if(o>=e.left&&o<=e.right&&r>=e.top&&r<=e.bottom)for(var l=e.legendHitBoxes,s=0;s=u.left&&o<=u.left+u.width&&r>=u.top&&r<=u.top+u.height){if("click"===i){n.onClick.call(e,t.native,e.legendItems[s]),a=!0;break}if("mousemove"===i){n.onHover.call(e,t.native,e.legendItems[s]),a=!0;break}}}return a}}),{id:"legend",beforeInit:function(t){var e=t.options.legend;e&&n(t,e)},beforeUpdate:function(t){var e=t.options.legend,a=t.legend;e?(o.mergeIf(e,i.global.legend),a?(r.configure(t,a,e),a.options=e):n(t,e)):a&&(r.removeBox(t,a),delete t.legend)},afterEvent:function(t,e){var n=t.legend;n&&n.handleEvent(e)}}}},{25:25,26:26,45:45}],51:[function(t,e,n){"use strict";var i=t(25),a=t(26),o=t(45);i._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,lineHeight:1.2,padding:10,position:"top",text:"",weight:2e3}}),e.exports=function(t){function e(e,i){var a=new t.Title({ctx:e.ctx,options:i,chart:e});n.configure(e,a,i),n.addBox(e,a),e.titleBlock=a}var n=t.layoutService,r=o.noop;return t.Title=a.extend({initialize:function(t){var e=this;o.extend(e,t),e.legendHitBoxes=[]},beforeUpdate:r,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:r,beforeSetDimensions:r,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:r,beforeBuildLabels:r,buildLabels:r,afterBuildLabels:r,beforeFit:r,fit:function(){var t=this,e=o.valueOrDefault,n=t.options,a=n.display,r=e(n.fontSize,i.global.defaultFontSize),l=t.minSize,s=o.isArray(n.text)?n.text.length:1,u=o.options.toLineHeight(n.lineHeight,r),d=a?s*u+2*n.padding:0;t.isHorizontal()?(l.width=t.maxWidth,l.height=d):(l.width=d,l.height=t.maxHeight),t.width=l.width,t.height=l.height},afterFit:r,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,n=o.valueOrDefault,a=t.options,r=i.global;if(a.display){var l,s,u,d=n(a.fontSize,r.defaultFontSize),c=n(a.fontStyle,r.defaultFontStyle),h=n(a.fontFamily,r.defaultFontFamily),f=o.fontString(d,c,h),g=o.options.toLineHeight(a.lineHeight,d),p=g/2+a.padding,v=0,m=t.top,b=t.left,x=t.bottom,y=t.right;e.fillStyle=n(a.fontColor,r.defaultFontColor),e.font=f,t.isHorizontal()?(s=b+(y-b)/2,u=m+p,l=y-b):(s="left"===a.position?b+p:y-p,u=m+(x-m)/2,l=x-m,v=Math.PI*("left"===a.position?-.5:.5)),e.save(),e.translate(s,u),e.rotate(v),e.textAlign="center",e.textBaseline="middle";var k=a.text;if(o.isArray(k))for(var w=0,M=0;Me.max&&(e.max=i))})});e.min=isFinite(e.min)&&!isNaN(e.min)?e.min:0,e.max=isFinite(e.max)&&!isNaN(e.max)?e.max:1,this.handleTickRangeOptions()},getTickLimit:function(){var t,e=this,n=e.options.ticks;if(e.isHorizontal())t=Math.min(n.maxTicksLimit?n.maxTicksLimit:11,Math.ceil(e.width/50));else{var o=a.valueOrDefault(n.fontSize,i.global.defaultFontSize);t=Math.min(n.maxTicksLimit?n.maxTicksLimit:11,Math.ceil(e.height/(2*o)))}return t},handleDirectionalChanges:function(){this.isHorizontal()||this.ticks.reverse()},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},getPixelForValue:function(t){var e,n=this,i=n.start,a=+n.getRightValue(t),o=n.end-i;return n.isHorizontal()?(e=n.left+n.width/o*(a-i),Math.round(e)):(e=n.bottom-n.height/o*(a-i),Math.round(e))},getValueForPixel:function(t){var e=this,n=e.isHorizontal(),i=n?e.width:e.height,a=(n?t-e.left:e.bottom-t)/i;return e.start+(e.end-e.start)*a},getPixelForTick:function(t){return this.getPixelForValue(this.ticksAsNumbers[t])}});t.scaleService.registerScaleType("linear",n,e)}},{25:25,34:34,45:45}],54:[function(t,e,n){"use strict";var i=t(45),a=t(34);e.exports=function(t){var e=i.noop;t.LinearScaleBase=t.Scale.extend({getRightValue:function(e){return"string"==typeof e?+e:t.Scale.prototype.getRightValue.call(this,e)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var n=i.sign(t.min),a=i.sign(t.max);n<0&&a<0?t.max=0:n>0&&a>0&&(t.min=0)}var o=void 0!==e.min||void 0!==e.suggestedMin,r=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),o!==r&&t.min>=t.max&&(o?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:e,handleDirectionalChanges:e,buildTicks:function(){var t=this,e=t.options.ticks,n=t.getTickLimit(),o={maxTicks:n=Math.max(2,n),min:e.min,max:e.max,stepSize:i.valueOrDefault(e.fixedStepSize,e.stepSize)},r=t.ticks=a.generators.linear(o,t);t.handleDirectionalChanges(),t.max=i.max(r),t.min=i.min(r),e.reverse?(r.reverse(),t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max)},convertTicksToLabels:function(){var e=this;e.ticksAsNumbers=e.ticks.slice(),e.zeroLineIndex=e.ticks.indexOf(0),t.Scale.prototype.convertTicksToLabels.call(e)}})}},{34:34,45:45}],55:[function(t,e,n){"use strict";var i=t(45),a=t(34);e.exports=function(t){var e={position:"left",ticks:{callback:a.formatters.logarithmic}},n=t.Scale.extend({determineDataLimits:function(){function t(t){return s?t.xAxisID===e.id:t.yAxisID===e.id}var e=this,n=e.options,a=n.ticks,o=e.chart,r=o.data.datasets,l=i.valueOrDefault,s=e.isHorizontal();e.min=null,e.max=null,e.minNotZero=null;var u=n.stacked;if(void 0===u&&i.each(r,function(e,n){if(!u){var i=o.getDatasetMeta(n);o.isDatasetVisible(n)&&t(i)&&void 0!==i.stack&&(u=!0)}}),n.stacked||u){var d={};i.each(r,function(a,r){var l=o.getDatasetMeta(r),s=[l.type,void 0===n.stacked&&void 0===l.stack?r:"",l.stack].join(".");o.isDatasetVisible(r)&&t(l)&&(void 0===d[s]&&(d[s]=[]),i.each(a.data,function(t,i){var a=d[s],o=+e.getRightValue(t);isNaN(o)||l.data[i].hidden||(a[i]=a[i]||0,n.relativePoints?a[i]=100:a[i]+=o)}))}),i.each(d,function(t){var n=i.min(t),a=i.max(t);e.min=null===e.min?n:Math.min(e.min,n),e.max=null===e.max?a:Math.max(e.max,a)})}else i.each(r,function(n,a){var r=o.getDatasetMeta(a);o.isDatasetVisible(a)&&t(r)&&i.each(n.data,function(t,n){var i=+e.getRightValue(t);isNaN(i)||r.data[n].hidden||(null===e.min?e.min=i:ie.max&&(e.max=i),0!==i&&(null===e.minNotZero||ia?{start:e-n-5,end:e}:{start:e,end:e+n+5}}function s(t){var i,o,s,u=n(t),d=Math.min(t.height/2,t.width/2),c={r:t.width,l:0,t:t.height,b:0},h={};t.ctx.font=u.font,t._pointLabelSizes=[];var f=e(t);for(i=0;ic.r&&(c.r=v.end,h.r=g),m.startc.b&&(c.b=m.end,h.b=g)}t.setReductions(d,c,h)}function u(t){var e=Math.min(t.height/2,t.width/2);t.drawingArea=Math.round(e),t.setCenterPoint(0,0,0,0)}function d(t){return 0===t||180===t?"center":t<180?"left":"right"}function c(t,e,n,i){if(a.isArray(e))for(var o=n.y,r=1.5*i,l=0;l270||t<90)&&(n.y-=e.h)}function f(t){var i=t.ctx,o=a.valueOrDefault,r=t.options,l=r.angleLines,s=r.pointLabels;i.lineWidth=l.lineWidth,i.strokeStyle=l.color;var u=t.getDistanceFromCenterForValue(r.ticks.reverse?t.min:t.max),f=n(t);i.textBaseline="top";for(var g=e(t)-1;g>=0;g--){if(l.display){var p=t.getPointPosition(g,u);i.beginPath(),i.moveTo(t.xCenter,t.yCenter),i.lineTo(p.x,p.y),i.stroke(),i.closePath()}if(s.display){var m=t.getPointPosition(g,u+5),b=o(s.fontColor,v.defaultFontColor);i.font=f.font,i.fillStyle=b;var x=t.getIndexAngle(g),y=a.toDegrees(x);i.textAlign=d(y),h(y,t._pointLabelSizes[g],m),c(i,t.pointLabels[g]||"",m,f.size)}}}function g(t,n,i,o){var r=t.ctx;if(r.strokeStyle=a.valueAtIndexOrDefault(n.color,o-1),r.lineWidth=a.valueAtIndexOrDefault(n.lineWidth,o-1),t.options.gridLines.circular)r.beginPath(),r.arc(t.xCenter,t.yCenter,i,0,2*Math.PI),r.closePath(),r.stroke();else{var l=e(t);if(0===l)return;r.beginPath();var s=t.getPointPosition(0,i);r.moveTo(s.x,s.y);for(var u=1;u0&&n>0?e:0)},draw:function(){var t=this,e=t.options,n=e.gridLines,i=e.ticks,o=a.valueOrDefault;if(e.display){var r=t.ctx,l=this.getIndexAngle(0),s=o(i.fontSize,v.defaultFontSize),u=o(i.fontStyle,v.defaultFontStyle),d=o(i.fontFamily,v.defaultFontFamily),c=a.fontString(s,u,d);a.each(t.ticks,function(e,a){if(a>0||i.reverse){var u=t.getDistanceFromCenterForValue(t.ticksAsNumbers[a]);if(n.display&&0!==a&&g(t,n,u,a),i.display){var d=o(i.fontColor,v.defaultFontColor);if(r.font=c,r.save(),r.translate(t.xCenter,t.yCenter),r.rotate(l),i.showLabelBackdrop){var h=r.measureText(e).width;r.fillStyle=i.backdropColor,r.fillRect(-h/2-i.backdropPaddingX,-u-s/2-i.backdropPaddingY,h+2*i.backdropPaddingX,s+2*i.backdropPaddingY)}r.textAlign="center",r.textBaseline="middle",r.fillStyle=d,r.fillText(e,0,-u),r.restore()}}}),(e.angleLines.display||e.pointLabels.display)&&f(t)}}});t.scaleService.registerScaleType("radialLinear",b,m)}},{25:25,34:34,45:45}],57:[function(t,e,n){"use strict";function i(t,e){return t-e}function a(t){var e,n,i,a={},o=[];for(e=0,n=t.length;ee&&l=0&&r<=l;){if(i=r+l>>1,a=t[i-1]||null,o=t[i],!a)return{lo:null,hi:o};if(o[e]n))return{lo:a,hi:o};l=i-1}}return{lo:o,hi:null}}function l(t,e,n,i){var a=r(t,e,n),o=a.lo?a.hi?a.lo:t[t.length-2]:t[0],l=a.lo?a.hi?a.hi:t[t.length-1]:t[1],s=l[e]-o[e],u=s?(n-o[e])/s:0,d=(l[i]-o[i])*u;return o[i]+d}function s(t,e){var n=e.parser,i=e.parser||e.format;return"function"==typeof n?n(t):"string"==typeof t&&"string"==typeof i?m(t,i):(t instanceof m||(t=m(t)),t.isValid()?t:"function"==typeof i?i(t):t)}function u(t,e){if(x.isNullOrUndef(t))return null;var n=e.options.time,i=s(e.getRightValue(t),n);return i.isValid()?(n.round&&i.startOf(n.round),i.valueOf()):null}function d(t,e,n,i){var a,o,r,l=e-t,s=w[n],u=s.size,d=s.steps;if(!d)return Math.ceil(l/((i||1)*u));for(a=0,o=d.length;a=M.indexOf(e);a--)if(o=M[a],w[o].common&&r.as(o)>=t.length)return o;return M[e?M.indexOf(e):0]}function f(t){for(var e=M.indexOf(t)+1,n=M.length;e1?e[1]:i,r=e[0],s=(l(t,"time",o,"pos")-l(t,"time",r,"pos"))/2),a.time.max||(o=e[e.length-1],r=e.length>1?e[e.length-2]:n,u=(l(t,"time",o,"pos")-l(t,"time",r,"pos"))/2)),{left:s,right:u}}function v(t,e){var n,i,a,o,r=[];for(n=0,i=t.length;n=a&&n<=r&&c.push(n);return i.min=a,i.max=r,i._unit=s.unit||h(c,s.minUnit,i.min,i.max),i._majorUnit=f(i._unit),i._table=o(i._timestamps.data,a,r,l.distribution),i._offsets=p(i._table,c,a,r,l),v(c,i._majorUnit)},getLabelForIndex:function(t,e){var n=this,i=n.chart.data,a=n.options.time,o=i.labels&&t=0&&t dict: if self.dir_cache_outdated: @@ -106,23 +138,26 @@ class LocalStorage: conn = sqlite3.connect(self.db_path) c = conn.cursor() - c.execute("SELECT id, path, enabled FROM Directory") + c.execute("SELECT id, path, enabled, name FROM Directory") db_directories = c.fetchall() - c.execute("SELECT name, directory_id FROM Option") + c.execute("SELECT key, value, directory_id, id FROM Option") db_options = c.fetchall() for db_dir in db_directories: options = [] - directory = Directory(db_dir[1], db_dir[2], options) + directory = Directory(db_dir[1], db_dir[2], options, db_dir[3]) for db_opt in db_options: - if db_opt[1] == db_dir[0]: - options.append(db_opt[0]) + if db_opt[2] == db_dir[0]: + options.append(Option(db_opt[0], db_opt[1], db_opt[2], db_opt[3])) - self.cached_dirs[directory.path] = directory - self.dir_cache_outdated = False - return self.cached_dirs + directory.id = db_dir[0] + + self.cached_dirs[directory.id] = directory + + self.dir_cache_outdated = False + return self.cached_dirs else: return self.cached_dirs @@ -135,15 +170,15 @@ class LocalStorage: try: conn = sqlite3.connect(self.db_path) c = conn.cursor() - c.execute("PRAGMA FOREIGN_KEYS = ON;") c.execute("INSERT INTO User (username, password, is_admin) VALUES (?,?,?)", (user.username, user.hashed_password, user.admin)) + c.close() conn.commit() conn.close() except sqlite3.IntegrityError: - raise DuplicateDirectoryException("Duplicate username: " + user.username) + raise DuplicateUserException("Duplicate username: " + user.username) - def users(self): + def users(self) -> dict: """Get user list""" if self.user_cache_outdated: @@ -157,7 +192,7 @@ class LocalStorage: db_users = c.fetchall() for db_user in db_users: - self.cached_users[db_user[0]] = User(db_user[0], "", db_user[1]) + self.cached_users[db_user[0]] = User(db_user[0], b"", bool(db_user[1])) conn.close() @@ -180,4 +215,72 @@ class LocalStorage: return False + def update_user(self, user: User) -> None: + """Updates an user. Will have no effect if the user does not exist""" + + self.user_cache_outdated = True + + conn = sqlite3.connect(self.db_path) + c = conn.cursor() + c.execute("UPDATE User SET is_admin=? WHERE username=?", (user.admin, user.username)) + + c.close() + conn.commit() + conn.close() + + def remove_user(self, username: str): + """Remove an user from the database""" + + self.user_cache_outdated = True + + conn = sqlite3.connect(self.db_path) + c = conn.cursor() + c.execute("DELETE FROM User WHERE username=?", (username,)) + + c.close() + conn.commit() + conn.close() + + def update_directory(self, directory): + """Updated a directory (Options are left untouched). Will have no effect if the directory does not exist""" + + self.dir_cache_outdated = True + + conn = sqlite3.connect(self.db_path) + c = conn.cursor() + c.execute("UPDATE Directory SET name=?, path=? WHERE id=?", (directory.name, directory.path, directory.id)) + + c.close() + conn.commit() + conn.close() + + def save_option(self, option: Option): + + self.dir_cache_outdated = True + + conn = sqlite3.connect(self.db_path) + c = conn.cursor() + c.execute("INSERT INTO Option (key, value, directory_id) VALUES (?, ?, ?)", (option.key, option.value, option.dir_id)) + c.execute("SELECT last_insert_rowid()") + + opt_id = c.fetchone()[0] + c.close() + conn.commit() + conn.close() + + return opt_id + + def del_option(self, opt_id): + """Delete an option from the database""" + + self.dir_cache_outdated = True + + conn = sqlite3.connect(self.db_path) + c = conn.cursor() + c.execute("DELETE FROM Option WHERE id=?", (opt_id, )) + + conn.commit() + conn.close() + + diff --git a/templates/dashboard.html b/templates/dashboard.html new file mode 100644 index 0000000..f035319 --- /dev/null +++ b/templates/dashboard.html @@ -0,0 +1,230 @@ +{% extends "layout.html" %} + +{% block body %} + +
+
+
+
+
FSE Info
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Version
1.0a
Total thumbnail size
652.08 Mb
Total document count
1258902
Total document size
4.7 TB
Folder count
4
User count
1
SQLite database path./local_storage.db
+
+
+ +
+
Elasticsearch info
+
+ + + + + + + + + + + + + + + + + + + + + +
Total index size
3.7 GB
HTTP port
9200
Version
6.2.1
Lucene version
7.2.1
Path
./elasticsearch/
+
+
+ +
+
+
+
+
+
Thumbnail cache size
+
+ + +
+
+
+
+
+
+
+
Document size
+
+ + + +
+
+
+
+
+
+
+
Document count
+
{# todo padding 8px 10px 5px 10px #} + + +
+
+
+
+
+
+
+ +{% endblock body %} \ No newline at end of file diff --git a/templates/directory.html b/templates/directory.html new file mode 100644 index 0000000..8f7ae2f --- /dev/null +++ b/templates/directory.html @@ -0,0 +1,56 @@ +{% extends "layout.html" %} + +{% block title %}An Excellent title{% endblock title %} + +{% block body %} +
+
+
An excellent form
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
An excellent list
+
+ + + + + + + + + + + {% for dir in directories %} + + + + + + + + {% endfor %} +
Display NamePathEnabledLast indexedAction
{{ directories[dir].name }}
{{ directories[dir].path }}
2018-02-21Manage
+ +
+
+ + + + +
+{% endblock body %} \ No newline at end of file diff --git a/templates/directory_manage.html b/templates/directory_manage.html new file mode 100644 index 0000000..84560d1 --- /dev/null +++ b/templates/directory_manage.html @@ -0,0 +1,108 @@ +{% extends "layout.html" %} + +{% block title %}An excellent title{% endblock title %} + +{% block body %} + +
+ +
+ +
An excellent summary
+
+ + + + + + + + + + + + + + + + +
Display name{{ directory.name }}
Path
{{ directory.path }}
Enabled
+
+ +
+ +
+
An excellent option list
+
+ + + + + + + {% for option in directory.options %} + + + + + + + + {% endfor %} +
KeyValueAction
{{ option.key }}{{ option.value }}Remove
+ +
+ +
+ +
+
+ +
+ + +
+ +
+ +
+ + +
+ +
+
+ + +
+ + +{% endblock body %} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html deleted file mode 100644 index 56fa48f..0000000 --- a/templates/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Title - - -
- - - - -
- - - \ No newline at end of file diff --git a/templates/layout.html b/templates/layout.html index 9383979..06df9af 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -1,13 +1,78 @@ - + - - Layout Title + + {% block title %}Default title{% endblock title %} + + + + + + + + + + + + + + + + + - + + + + {% block body %} {% endblock body %} + - \ No newline at end of file + diff --git a/templates/search.html b/templates/search-old.html similarity index 100% rename from templates/search.html rename to templates/search-old.html diff --git a/templates/task.html b/templates/task.html new file mode 100644 index 0000000..566549b --- /dev/null +++ b/templates/task.html @@ -0,0 +1,10 @@ + + + + + Title + + + + + \ No newline at end of file