Codebase list python-faraday / 3f5f506
Imported Upstream version 1.0.15 Sophie Brun 8 years ago
78 changed file(s) with 6482 addition(s) and 579 deletion(s). Raw diff Collapse all Expand all
88
99 New features in the latest update
1010 =====================================
11
12 TBA:
13 ---
14 * Continuous Scanning Tool cscan added to ./scripts/cscan
15 * Fix for saving objects without parent
16 * Hosts and Services views now have pagination and search
17 * Updates version number on Faraday Start
18 * Visual fixes on Firefox
19 * Migrate graphs from D3.js to Chart.js
20 * Added Services columns to Status Report
21 * Added sections of Commercial versions
22 * Converted references to links in Status Report. Support for CVE, CWE, Exploit Database and Open Source Vulnerability Database
23 * Added Pippingtom, SSHdefaultscan and pasteAnalyzer plugins
24 * Fixed Debian install
1125
1226 Sep 10, 2015:
1327 ---
0 1.0.14
0 1.0.15
+0
-34
cleanXML.py less more
0 #!/usr/bin/env python2.7
1 '''
2 Faraday Penetration Test IDE
3 Copyright (C) 2014 Infobyte LLC (http://www.infobytesec.com/)
4 See the file 'doc/LICENSE' for the license information
5
6 '''
7 import argparse
8 from bs4 import BeautifulSoup
9
10 def main():
11 parser = argparse.ArgumentParser(prog='cleanXML', epilog="Example: ./%(prog)s.py")
12
13 parser.add_argument('-i', '--input', action='store', type=str,
14 dest='infile', help='XML File to read from',
15 required=True)
16 parser.add_argument('-o', '--output', action='store', type=str,
17 dest='outfile', help='Filename to write output',
18 default="clean.xml")
19
20 args = parser.parse_args()
21
22 xml = open(args.infile, 'r')
23 soup = BeautifulSoup(xml.read(), 'xml')
24
25 out = open(args.outfile, 'w')
26 out.write(soup.encode('utf-8'))
27 out.flush()
28 out.close()
29
30 xml.close()
31
32 if __name__ == "__main__":
33 main()
11 <faraday>
22
33 <appname>Faraday - Penetration Test IDE</appname>
4 <version>1.0.14</version>
4 <version>1.0.15</version>
55 <debug_status>0</debug_status>
66 <font>-Misc-Fixed-medium-r-normal-*-12-100-100-100-c-70-iso8859-1</font>
77 <home_path>~/</home_path>
55 '''
66
77 CONST_REQUIREMENTS_FILE = 'requirements.txt'
8 CONST_CONFIG = 'views/reports/_attachments/scripts/config/config.json'
89 CONST_FARADAY_HOME_PATH = '~/.faraday'
910 CONST_FARADAY_PLUGINS_PATH = 'plugins'
1011 CONST_FARADAY_PLUGINS_REPO_PATH = 'plugins/repo'
1717 import platform
1818 import subprocess
1919 import pip
20 import json
2021
2122 from utils.logs import getLogger, setUpLogger
2223 sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + '/external_libs/lib/python2.7/dist-packages')
2324 from config.configuration import getInstanceConfiguration
2425 from config.globals import *
2526 from utils.profilehooks import profile
27 from utils.user_input import query_yes_no
2628
2729
2830
539541 # Non fatal error
540542 pass
541543
544 def checkVersion():
545 try:
546 f = open(CONST_VERSION_FILE)
547 f_version = f.read().strip()
548 if not args.update:
549 if getInstanceConfiguration().getVersion() != None and getInstanceConfiguration().getVersion() != f_version:
550 logger.warning("You have different version of Faraday since your last run.\nRun ./faraday.py --update to update configuration!")
551 if query_yes_no('Do you want to close Faraday?', 'yes'):
552 exit(-1)
553
554 getInstanceConfiguration().setVersion(f_version)
555 f.close()
556
557 doc = {"ver": getInstanceConfiguration().getVersion()}
558
559 if os.path.isfile(CONST_CONFIG):
560 os.remove(CONST_CONFIG)
561 with open(CONST_CONFIG, "w") as doc_file:
562 json.dump(doc, doc_file)
563 except Exception as e:
564 getLogger("launcher").error("It seems that something's wrong with your version\nPlease contact customer support")
565 exit(-1)
566
542567
543568 def init():
544569 """Initializes what is needed before starting.
569594 checkConfiguration()
570595 setConf()
571596 checkCouchUrl()
597 checkVersion()
572598 setUpLogger()
573599 update()
574600 checkUpdates()
0 #!/usr/bin/env python2.7
1 '''
2 Faraday Penetration Test IDE
3 Copyright (C) 2014 Infobyte LLC (http://www.infobytesec.com/)
4 See the file 'doc/LICENSE' for the license information
5
6 '''
7 '''
8 This script fixes invalid XMLs.
9 '''
10
11 import argparse
12 from bs4 import BeautifulSoup
13
14 def main():
15 parser = argparse.ArgumentParser(prog='cleanXML', epilog="Example: ./%(prog)s.py")
16
17 parser.add_argument('-i', '--input', action='store', type=str,
18 dest='infile', help='XML File to read from',
19 required=True)
20 parser.add_argument('-o', '--output', action='store', type=str,
21 dest='outfile', help='Filename to write output',
22 default="clean.xml")
23
24 args = parser.parse_args()
25
26 xml = open(args.infile, 'r')
27 soup = BeautifulSoup(xml.read(), 'xml')
28
29 out = open(args.outfile, 'w')
30 out.write(soup.encode('utf-8'))
31 out.flush()
32 out.close()
33
34 xml.close()
35
36 if __name__ == "__main__":
37 main()
0 #!/usr/bin/env python2.7
1 '''
2 Faraday Penetration Test IDE
3 Copyright (C) 2014 Infobyte LLC (http://www.infobytesec.com/)
4 See the file 'doc/LICENSE' for the license information
5
6 '''
7 '''
8 This script either updates or removes Interfaces, Services and Vulnerabilities in case their parent property is null.
9 If the property is null but a parent is found in Couch, the document is updated.
10 If the parent is not found in Couch the document is deleted, since it is an invalid one.
11 '''
12
13 import argparse
14 import json
15 import requests
16 import os
17 from pprint import pprint
18
19 def main():
20 #arguments parser
21 parser = argparse.ArgumentParser(prog='fixBrokenChildren', epilog="Example: ./%(prog)s.py")
22 parser.add_argument('-c', '--couchdburi', action='store', type=str,
23 dest='couchdb',default="http://127.0.0.1:5984",
24 help='Couchdb URL (default http://127.0.0.1:5984)')
25 parser.add_argument('-d', '--db', action='store', type=str,
26 dest='db', help='DB to process')
27
28 #arguments put in variables
29 args = parser.parse_args()
30 dbs = list()
31
32 #default value from ENV COUCHDB
33 couchdb = os.environ.get('COUCHDB')
34 #Else from argument
35 if not couchdb:
36 couchdb = args.couchdb
37
38 if args.db:
39 dbs.append(args.db)
40
41 if len(dbs) == 0:
42 dbs = requests.get(couchdb + '/_all_dbs')
43 dbs = dbs.json()
44 dbs = filter(lambda x: not x.startswith('_') and x != 'cwe' and x != 'reports', dbs)
45
46 for db in dbs:
47 fixDb(couchdb, db)
48
49 def fixDb(couchdb, db):
50 couchdb = str(couchdb)
51 db = str(db)
52
53 #get all broken elements from CouchDB
54 headers = {'Content-Type': 'application/json'}
55 payload = { "map" : """function(doc) { if(doc.type == \"Interface\" ||
56 doc.type == \"Service\" ||
57 doc.type == \"Vulnerability\" ||
58 doc.type == \"VulnerabilityWeb\"){ if(doc.parent == null) emit(doc.parent, 1); }}""" }
59
60 r = requests.post(couchdb + '/' + db + '/_temp_view', headers=headers, data=json.dumps(payload))
61 response_code = r.status_code
62
63 if response_code == 200:
64 response = r.json()
65 rows = response['rows']
66 rows = sorted(rows, key=lambda x: x['id'])
67
68 if len(rows) > 0:
69 print " [*[ Processing " + str(len(rows)) + " documents for " + db + " ]*]"
70
71 for row in rows:
72 id = str(row['id'])
73 parent = str(id[:id.rfind('.')])
74
75 parent_response = requests.get(couchdb + '/' + db + '/' + parent)
76 parent_code = parent_response.status_code
77
78 child_response = requests.get(couchdb + '/' + db + '/' + id)
79 child = child_response.json()
80
81 #object parent exists in Couch
82 #update parent field in obj
83 if parent_code == 200:
84 print " - Updating " + child['type'] + " \"" + child['name'] + "\" with ID " + id
85 child['parent'] = parent
86 #print doc['parent']
87 update = requests.put(couchdb + '/' + db + '/' + id, headers=headers, data=json.dumps(child))
88 print " -- " + update.reason + " (" + str(update.status_code) + ")"
89
90 #object has no valid parent
91 #delete obj
92 elif parent_code == 404:
93 # delete vuln
94 print " - Deleting " + child['type'] + " \"" + child['name'] + "\" with ID " + id
95 delete = requests.delete(couchdb + '/' + db + '/' + id + '?rev=' + child['_rev'])
96 print " -- " + delete.reason + " (" + str(delete.status_code) + ")"
97 elif parent_code == 401:
98 print " Autorization required, make sure to add user:pwd to Couch URI using --couchdburi"
99 else:
100 print " Fail"
101 else:
102 print "Congratz, " + db + " is just fine!"
103 elif response_code == 401:
104 print " Autorization required to access " + db + ", make sure to add user:pwd to Couch URI using --couchdburi"
105
106 if __name__ == "__main__":
107 main()
0 #!/usr/bin/env python2.7
1 '''
2 Faraday Penetration Test IDE
3 Copyright (C) 2014 Infobyte LLC (http://www.infobytesec.com/)
4 See the file 'doc/LICENSE' for the license information
5
6 '''
7 '''
8 This script upload a Vulnerability database to Couch.
9 It takes the content of the DB from data/cwe.csv
10 '''
11 import argparse
12 import os
13 from couchdbkit import Server, designer
14 import json
15 import csv
16
17
18 def main():
19
20 #arguments parser
21 parser = argparse.ArgumentParser(prog='pushExecutiveReports', epilog="Example: ./%(prog)s.py")
22 parser.add_argument('-c', '--couchdburi', action='store', type=str,
23 dest='couchdb',default="http://127.0.0.1:5984",
24 help='Couchdb URL (default http://127.0.0.1:5984)')
25
26 #arguments put in variables
27 args = parser.parse_args()
28
29 #default value from ENV COUCHDB
30 couchdb = os.environ.get('COUCHDB')
31 #Else from argument
32 if not couchdb:
33 couchdb = args.couchdb
34 __serv = Server(uri = couchdb)
35
36 # reports = os.path.join(os.getcwd(), "views", "reports")
37 workspace = __serv.get_or_create_db("cwe")
38 # designer.push(reports, workspace, atomic = False)
39
40 with open('data/cwe.csv', 'r') as csvfile:
41 cwereader = csv.reader(csvfile, delimiter=',')
42 header = cwereader.next()
43 for cwe in cwereader:
44 cwe_doc = dict(zip(header, cwe))
45 workspace.save_doc(cwe_doc)
46
47 if __name__ == "__main__":
48 main()
6565 # Bug: https://bugs.launchpad.net/ubuntu/+source/python-pip/+bug/1306991
6666 wget https://raw.github.com/pypa/pip/master/contrib/get-pip.py
6767 python get-pip.py
68 elif [[ "$os" =~ "Debian 7".*|"Debian 8".* ]]; then
68 elif [[ "$os" =~ "Debian 7".*|"Debian 8".*|"stretch/sid".* ]]; then
6969 version="ubuntu13-10-$arch"
7070 down=1
7171 wget https://raw.github.com/pypa/pip/master/contrib/get-pip.py
447447 old_obj = dataMapper.find(obj.getID())
448448 if old_obj:
449449 if not old_obj.needs_merge(obj):
450 #the object is exactly the same,
450 # the object is exactly the same,
451451 # so return and do nothing
452452 return True
453453 if not self.addUpdate(old_obj, obj):
458458 object_parent = self.mappers_manager.find(parent_id)
459459 if object_parent:
460460 object_parent.addChild(obj)
461 # we have to make sure that certain objects have to have a parent
462 if (obj.class_signature in
463 [model.hosts.Interface.class_signature,
464 model.hosts.Service.class_signature,
465 model.common.ModelObjectNote.class_signature,
466 model.common.ModelObjectVuln.class_signature,
467 model.common.ModelObjectVulnWeb.class_signature,
468 model.common.ModelObjectCred.class_signature] and object_parent is None):
469 # TODO: refactor log module. We need to log twice to see it in
470 # qt and in the terminal. Ugly.
471 msg = "A parent is needed for %s objects" % obj.class_signature
472 getLogger(self).error(msg)
473 model.api.log(msg)
474 return False
461475 dataMapper.save(obj)
462476 self.treeWordsTries.addWord(obj.getName())
463477 if obj.class_signature == model.hosts.Host.class_signature:
878892 hosts = self.mappers_manager.getMapper(
879893 model.hosts.Host.__name__).getAll()
880894 return hosts
881
895
882896 def getWebVulns(self):
883897 return self.mappers_manager.getMapper(
884898 model.common.ModelObjectVulnWeb.class_signature).getAll()
0 '''
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 '''
6
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 #Author: @EzequielTBH
4
5 from plugins import core
6 import json
7 import re
8
9 __author__ = "@EzequielTBH"
10 __copyright__ = "Copyright 2015, @EzequielTBH"
11 __credits__ = "@EzequielTBH"
12 __license__ = "GPL v3"
13 __version__ = "1.0.0"
14
15 class pasteAnalyzerPlugin(core.PluginBase):
16
17 def __init__(self):
18 core.PluginBase.__init__(self)
19 self.id = "pasteAnalyzer"
20 self.name = "pasteAnalyzer JSON Output Plugin"
21 self.plugin_version = "1.0.0"
22 self.command_string = ""
23 self.current_path = ""
24 self._command_regex = re.compile(
25 r'^(pasteAnalyzer|python pasteAnalyzer.py|\./pasteAnalyzer.py|sudo python pasteAnalyzer.py|sudo \./pasteAnalyzer.py).*?')
26
27
28 def parseOutputString(self, output, debug = False):
29
30 print("[*]Parsing Output...")
31
32 #Generating file name with full path.
33 indexStart = self.command_string.find("-j") + 3
34
35 fileJson = self.command_string [ indexStart :
36 self.command_string.find(" ", indexStart) ]
37
38 fileJson = self.current_path + "/" + fileJson
39
40 try:
41 with open(fileJson,"r") as fileJ:
42 results = json.loads( fileJ.read() )
43
44 except Exception as e:
45 print("\n[!]Exception opening file\n" + str(e) )
46 return
47
48 if results == []:
49 return
50
51 print("[*]Results loaded...")
52
53 #Configuration initial.
54 hostId = self.createAndAddHost("pasteAnalyzer")
55 interfaceId = self.createAndAddInterface(hostId, "Results")
56 serviceId = self.createAndAddServiceToInterface(
57 hostId,
58 interfaceId,
59 "Web",
60 "TcpHTTP",
61 ['80']
62 )
63 print("[*]Initial Configuration ready....")
64
65 #Loading results.
66 for i in range(0, len(results), 2 ):
67
68 data = results[i + 1]
69 description = ""
70
71 for element in data:
72
73 #Is Category
74 if type(element) == str or type(element) == unicode:
75 description += element +": "
76
77 #Is a list with results!
78 else:
79 for element2 in element:
80 description += "\n" + element2
81
82 self.createAndAddVulnWebToService(
83 hostId,
84 serviceId,
85 results[i],
86 description
87 )
88
89 print("[*]Parse finished, API faraday called...")
90
91 def processCommandString(self, username, current_path, command_string):
92
93 print("[*]pasteAnalyzer Plugin running...")
94
95 if command_string.find("-j") < 0:
96 command_string += " -j JSON_OUTPUT "
97
98 self.command_string = command_string
99 self.current_path = current_path
100
101 return command_string
102
103 def createPlugin():
104 return pasteAnalyzerPlugin()
105
106
107
0 '''
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 '''
6
0 # -*- coding: utf-8 -*-
1 """
2 Faraday Penetration Test IDE
3 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
4 See the file 'doc/LICENSE' for the license information
5 """
6
7 import re
8 import socket
9 from os import path
10 from plugins import core
11 from urlparse import urlparse
12
13 __author__ = "Andres Tarantini"
14 __copyright__ = "Copyright (c) 2015 Andres Tarantini"
15 __credits__ = ["Andres Tarantini"]
16 __license__ = "MIT"
17 __version__ = "0.0.1"
18 __maintainer__ = "Andres Tarantini"
19 __email__ = "[email protected]"
20 __status__ = "Development"
21
22
23 class PeepingTomPlugin(core.PluginBase):
24 """
25 Handle PeepingTom (https://bitbucket.org/LaNMaSteR53/peepingtom) output
26 """
27 def __init__(self):
28 core.PluginBase.__init__(self)
29 self.id = "peepingtom"
30 self.name = "PeepingTom"
31 self.plugin_version = "0.0.1"
32 self.version = "02.19.15"
33 self._command_regex = re.compile(r'^(python peepingtom.py|\./peepingtom.py).*?')
34 self._path = None
35
36 def parseOutputString(self, output):
37 # Find data path
38 data_path_search = re.search(r"in '(.*)\/'", output)
39 print data_path_search
40 if not data_path_search:
41 # No data path found
42 return True
43
44 # Parse "peepingtom.html" report and extract results
45 data_path = data_path_search.groups()[0]
46 html = open(path.join(self._path, data_path, "peepingtom.html")).read()
47 for url in re.findall(r'href=[\'"]?([^\'" >]+)', html):
48 if "://" in url:
49 url_parsed = urlparse(url)
50 address = socket.gethostbyname(url_parsed.netloc)
51 host = self.createAndAddHost(address)
52 iface = self.createAndAddInterface(host, address, ipv4_address=address)
53 service = self.createAndAddServiceToInterface(
54 host, iface, "http", protocol="tcp", ports=80
55 )
56 note = self.createAndAddNoteToService(
57 host,
58 service,
59 'screenshot',
60 path.join(
61 self._path,
62 data_path_search.groups()[0],
63 "{}.png".format(url.replace("://", "").replace("/", "").replace(".", ""))
64 )
65 )
66
67 return True
68
69 def processCommandString(self, username, current_path, command_string):
70 self._path = current_path
71 return None
72
73
74 def createPlugin():
75 return PeepingTomPlugin()
76
0 '''
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 '''
6
0 # -*- coding: utf-8 -*-
1 """
2 Faraday Penetration Test IDE
3 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
4 See the file 'doc/LICENSE' for the license information
5 """
6
7 from plugins import core
8 import re
9
10 __author__ = "Andres Tarantini"
11 __copyright__ = "Copyright (c) 2015 Andres Tarantini"
12 __credits__ = ["Andres Tarantini"]
13 __license__ = "MIT"
14 __version__ = "0.0.1"
15 __maintainer__ = "Andres Tarantini"
16 __email__ = "[email protected]"
17 __status__ = "Development"
18
19
20 class SSHDefaultScanPlugin(core.PluginBase):
21 """
22 Handle sshdefaultscan (https://github.com/atarantini/sshdefaultscan) output
23 using --batch and --batch-template; supports --username and --password
24 """
25 def __init__(self):
26 core.PluginBase.__init__(self)
27 self.id = "sshdefaultscan"
28 self.name = "sshdefaultscan"
29 self.plugin_version = "0.0.1"
30 self.version = "1.0.0"
31 self._command_regex = re.compile(r'^(python sshdefaultscan.py|\./sshdefaultscan.py).*?')
32 self._completition = {"--fast": "Fast scan mode"}
33
34 def parseOutputString(self, output, debug=False):
35 for line in [l.strip() for l in output.split("\n")]:
36 output_rexeg_match = re.match(r".*:.*@\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", line)
37 if output_rexeg_match:
38 credentials, address = line.split("@")
39 host = self.createAndAddHost(address)
40 iface = self.createAndAddInterface(host, address, ipv4_address=address)
41 service = self.createAndAddServiceToInterface(
42 host, iface, "ssh", protocol="tcp", ports=22
43 )
44 username, password = credentials.split(":")
45 cred = self.createAndAddCredToService(host, service, username, password)
46 vuln = self.createAndAddVulnToService(
47 host,
48 service,
49 "Default credentials",
50 desc="The SSH server have default credentials ({username}:{password})".format(
51 username=username,
52 password=password
53 ),
54 severity=3
55 )
56
57 return True
58
59 def processCommandString(self, username, current_path, command_string):
60 if "--batch" not in command_string:
61 return "{command} --batch --batch-template {template}".format(
62 command=command_string,
63 template="{username}:{password}@{host}"
64 )
65
66 return None
67
68
69 def createPlugin():
70 return SSHDefaultScanPlugin()
+0
-45
pushCwe.py less more
0 #!/usr/bin/env python2.7
1 '''
2 Faraday Penetration Test IDE
3 Copyright (C) 2014 Infobyte LLC (http://www.infobytesec.com/)
4 See the file 'doc/LICENSE' for the license information
5
6 '''
7 import argparse
8 import os
9 from couchdbkit import Server, designer
10 import json
11 import csv
12
13
14 def main():
15
16 #arguments parser
17 parser = argparse.ArgumentParser(prog='pushExecutiveReports', epilog="Example: ./%(prog)s.py")
18 parser.add_argument('-c', '--couchdburi', action='store', type=str,
19 dest='couchdb',default="http://127.0.0.1:5984",
20 help='Couchdb URL (default http://127.0.0.1:5984)')
21
22 #arguments put in variables
23 args = parser.parse_args()
24
25 #default value from ENV COUCHDB
26 couchdb = os.environ.get('COUCHDB')
27 #Else from argument
28 if not couchdb:
29 couchdb = args.couchdb
30 __serv = Server(uri = couchdb)
31
32 # reports = os.path.join(os.getcwd(), "views", "reports")
33 workspace = __serv.get_or_create_db("cwe")
34 # designer.push(reports, workspace, atomic = False)
35
36 with open('data/cwe.csv', 'r') as csvfile:
37 cwereader = csv.reader(csvfile, delimiter=',')
38 header = cwereader.next()
39 for cwe in cwereader:
40 cwe_doc = dict(zip(header, cwe))
41 workspace.save_doc(cwe_doc)
42
43 if __name__ == "__main__":
44 main()
0 * v1.0.0 (09/23/15):
1 * First release cscan tool
0 # cscan
1 Faraday Continuous Scanning
2
3 More information:
4 [http://blog.infobytesec.com/2015/09/faraday-continuous-scanning.html] (http://blog.infobytesec.com/2015/09/faraday-continuous-scanning.html)
0 #!/usr/bin/env python
1 ###
2 ## Faraday Penetration Test IDE
3 ## Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
4 ## See the file 'doc/LICENSE' for the license information
5 ###
6
7 config = {
8 #NMAP
9 'CS_NMAP' : "nmap",
10 #OPENVAS
11 'CS_OPENVAS_USER' : 'admin',
12 'CS_OPENVAS_PASSWORD' : 'openvas',
13 'CS_OPENVAS_SCAN_CONFIG' : "Full and fast",
14 'CS_OPENVAS_ALIVE_TEST' : "ICMP, TCP-ACK Service &amp; ARP Ping",
15 'CS_OPENVAS' : 'omp',
16 #BURP
17 'CS_BURP' : '/root/tools/burpsuite_pro_v1.6.26.jar',
18 #NIKTO
19 'CS_NIKTO' : "nikto",
20 #W3AF
21 'CS_W3AF' : "/root/tools/w3af/w3af_api",
22 'CS_W3AF_PROFILE' : "/root/tools/w3af/profiles/fast_scan.pw3af",
23 #ZAP
24 'CS_ZAP' : "/root/tools/zap/ZAP_D-2015-08-24/zap.sh",
25 #NESSUS
26 'CS_NESSUS_URL' : "https://127.0.0.1:8834",
27 'CS_NESSUS_USER' : "nessus",
28 'CS_NESSUS_PASS' : "nessus",
29 'CS_NESSUS_PROFILE' : "Basic Network Scan",
30 }
31
0 #!/usr/bin/env python
1 ###
2 ## Faraday Penetration Test IDE
3 ## Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
4 ## See the file 'doc/LICENSE' for the license information
5 ###
6
7 import subprocess
8 import os
9 import argparse
10 import time
11 from pprint import pprint
12 from config import config
13
14 def lockFile(lockfile):
15
16 if os.path.isfile(lockfile):
17 return False
18 else:
19 f = open(lockfile, 'w')
20 f.close()
21 return True
22
23 def main():
24
25 lockf = ".lock.pod"
26 if not lockFile(lockf):
27 print "You can run only one instance of cscan (%s)" % lockf
28 exit(0)
29
30 my_env = os.environ
31 env = config.copy()
32 env.update(my_env)
33 #Parser argument in command line
34 parser = argparse.ArgumentParser(description='continues scanning on Faraday')
35 parser.add_argument('-p','--plugin', help='Scan only the following plugin ej: ./cscan.py -p nmap.sh', required=False)
36 args = parser.parse_args()
37
38 for dirpath, dnames, fnames in os.walk("./scripts/web/"):
39 for f in fnames:
40 if args.plugin and args.plugin != f:
41 continue
42 script = os.path.join(dirpath, f)
43 cmd = "%s websites.txt output/" % (script)
44 print "Running: %s" % cmd
45 proc = subprocess.call(cmd, shell=True, stdin=None, stderr=subprocess.PIPE, env=dict(env))
46
47 for dirpath, dnames, fnames in os.walk("./scripts/network/"):
48 for f in fnames:
49 if args.plugin and args.plugin != f:
50 continue
51 script = os.path.join(dirpath, f)
52 cmd = "%s ips.txt output/" % (script)
53 print "Running: %s" % cmd
54 proc = subprocess.call(cmd, shell=True, stdin=None, stderr=subprocess.PIPE, env=dict(env))
55
56 #Remove lockfile
57 os.remove(lockf)
58
59 if __name__ == "__main__":
60 main()
0 <?xml version="1.0" encoding="UTF-8"?>
1 <!DOCTYPE nmaprun>
2 <?xml-stylesheet href="file:///usr/bin/../share/nmap/nmap.xsl" type="text/xsl"?>
3 <!-- Nmap 6.49BETA4 scan initiated Wed Sep 23 02:45:41 2015 as: nmap -iL ips.txt -oX output/nmap_1442987141.xml -->
4 <nmaprun scanner="nmap" args="nmap -iL ips.txt -oX output/nmap_1442987141.xml" start="1442987141" startstr="Wed Sep 23 02:45:41 2015" version="6.49BETA4" xmloutputversion="1.04">
5 <scaninfo type="syn" protocol="tcp" numservices="1000" services="1,3-4,6-7,9,13,17,19-26,30,32-33,37,42-43,49,53,70,79-85,88-90,99-100,106,109-111,113,119,125,135,139,143-144,146,161,163,179,199,211-212,222,254-256,259,264,280,301,306,311,340,366,389,406-407,416-417,425,427,443-445,458,464-465,481,497,500,512-515,524,541,543-545,548,554-555,563,587,593,616-617,625,631,636,646,648,666-668,683,687,691,700,705,711,714,720,722,726,749,765,777,783,787,800-801,808,843,873,880,888,898,900-903,911-912,981,987,990,992-993,995,999-1002,1007,1009-1011,1021-1100,1102,1104-1108,1110-1114,1117,1119,1121-1124,1126,1130-1132,1137-1138,1141,1145,1147-1149,1151-1152,1154,1163-1166,1169,1174-1175,1183,1185-1187,1192,1198-1199,1201,1213,1216-1218,1233-1234,1236,1244,1247-1248,1259,1271-1272,1277,1287,1296,1300-1301,1309-1311,1322,1328,1334,1352,1417,1433-1434,1443,1455,1461,1494,1500-1501,1503,1521,1524,1533,1556,1580,1583,1594,1600,1641,1658,1666,1687-1688,1700,1717-1721,1723,1755,1761,1782-1783,1801,1805,1812,1839-1840,1862-1864,1875,1900,1914,1935,1947,1971-1972,1974,1984,1998-2010,2013,2020-2022,2030,2033-2035,2038,2040-2043,2045-2049,2065,2068,2099-2100,2103,2105-2107,2111,2119,2121,2126,2135,2144,2160-2161,2170,2179,2190-2191,2196,2200,2222,2251,2260,2288,2301,2323,2366,2381-2383,2393-2394,2399,2401,2492,2500,2522,2525,2557,2601-2602,2604-2605,2607-2608,2638,2701-2702,2710,2717-2718,2725,2800,2809,2811,2869,2875,2909-2910,2920,2967-2968,2998,3000-3001,3003,3005-3007,3011,3013,3017,3030-3031,3052,3071,3077,3128,3168,3211,3221,3260-3261,3268-3269,3283,3300-3301,3306,3322-3325,3333,3351,3367,3369-3372,3389-3390,3404,3476,3493,3517,3527,3546,3551,3580,3659,3689-3690,3703,3737,3766,3784,3800-3801,3809,3814,3826-3828,3851,3869,3871,3878,3880,3889,3905,3914,3918,3920,3945,3971,3986,3995,3998,4000-4006,4045,4111,4125-4126,4129,4224,4242,4279,4321,4343,4443-4446,4449,4550,4567,4662,4848,4899-4900,4998,5000-5004,5009,5030,5033,5050-5051,5054,5060-5061,5080,5087,5100-5102,5120,5190,5200,5214,5221-5222,5225-5226,5269,5280,5298,5357,5405,5414,5431-5432,5440,5500,5510,5544,5550,5555,5560,5566,5631,5633,5666,5678-5679,5718,5730,5800-5802,5810-5811,5815,5822,5825,5850,5859,5862,5877,5900-5904,5906-5907,5910-5911,5915,5922,5925,5950,5952,5959-5963,5987-5989,5998-6007,6009,6025,6059,6100-6101,6106,6112,6123,6129,6156,6346,6389,6502,6510,6543,6547,6565-6567,6580,6646,6666-6669,6689,6692,6699,6779,6788-6789,6792,6839,6881,6901,6969,7000-7002,7004,7007,7019,7025,7070,7100,7103,7106,7200-7201,7402,7435,7443,7496,7512,7625,7627,7676,7741,7777-7778,7800,7911,7920-7921,7937-7938,7999-8002,8007-8011,8021-8022,8031,8042,8045,8080-8090,8093,8099-8100,8180-8181,8192-8194,8200,8222,8254,8290-8292,8300,8333,8383,8400,8402,8443,8500,8600,8649,8651-8652,8654,8701,8800,8873,8888,8899,8994,9000-9003,9009-9011,9040,9050,9071,9080-9081,9090-9091,9099-9103,9110-9111,9200,9207,9220,9290,9415,9418,9485,9500,9502-9503,9535,9575,9593-9595,9618,9666,9876-9878,9898,9900,9917,9929,9943-9944,9968,9998-10004,10009-10010,10012,10024-10025,10082,10180,10215,10243,10566,10616-10617,10621,10626,10628-10629,10778,11110-11111,11967,12000,12174,12265,12345,13456,13722,13782-13783,14000,14238,14441-14442,15000,15002-15004,15660,15742,16000-16001,16012,16016,16018,16080,16113,16992-16993,17877,17988,18040,18101,18988,19101,19283,19315,19350,19780,19801,19842,20000,20005,20031,20221-20222,20828,21571,22939,23502,24444,24800,25734-25735,26214,27000,27352-27353,27355-27356,27715,28201,30000,30718,30951,31038,31337,32768-32785,33354,33899,34571-34573,35500,38292,40193,40911,41511,42510,44176,44442-44443,44501,45100,48080,49152-49161,49163,49165,49167,49175-49176,49400,49999-50003,50006,50300,50389,50500,50636,50800,51103,51493,52673,52822,52848,52869,54045,54328,55055-55056,55555,55600,56737-56738,57294,57797,58080,60020,60443,61532,61900,62078,63331,64623,64680,65000,65129,65389"/>
6 <verbose level="0"/>
7 <debugging level="0"/>
8 <host starttime="1442987141" endtime="1442987142"><status state="up" reason="localhost-response" reason_ttl="0"/>
9 <address addr="127.0.0.1" addrtype="ipv4"/>
10 <hostnames>
11 <hostname name="localhost" type="PTR"/>
12 </hostnames>
13 <ports><extraports state="closed" count="998">
14 <extrareasons reason="resets" count="998"/>
15 </extraports>
16 <port protocol="tcp" portid="22"><state state="open" reason="syn-ack" reason_ttl="64"/><service name="ssh" method="table" conf="3"/></port>
17 <port protocol="tcp" portid="5432"><state state="open" reason="syn-ack" reason_ttl="64"/><service name="postgresql" method="table" conf="3"/></port>
18 </ports>
19 <times srtt="3" rttvar="1" to="100000"/>
20 </host>
21 <runstats><finished time="1442987142" timestr="Wed Sep 23 02:45:42 2015" elapsed="1.69" summary="Nmap done at Wed Sep 23 02:45:42 2015; 1 IP address (1 host up) scanned in 1.69 seconds" exit="success"/><hosts up="1" down="0" total="1"/>
22 </runstats>
23 </nmaprun>
0 # Created by Blake Cornell, CTO, Integris Security LLC
1 # Integris Security Carbonator - Beta Version - v1.2
2 # Released under GPL Version 2 license.
3 #
4 # See the INSTALL file for installation instructions.
5 #
6 # For more information contact us at carbonator at integrissecurity dot com
7 # Or visit us at https://www.integrissecurity.com/
8 from burp import IBurpExtender
9 from burp import IHttpListener
10 from burp import IScannerListener
11 from java.net import URL
12 from java.io import File
13
14 import time
15
16 class BurpExtender(IBurpExtender, IHttpListener, IScannerListener):
17 def registerExtenderCallbacks(self, callbacks):
18 self._callbacks = callbacks
19 self._callbacks.setExtensionName("Carbonator")
20 self._helpers = self._callbacks.getHelpers()
21 self.clivars = None
22
23 self.spider_results=[]
24 self.scanner_results=[]
25 self.packet_timeout=5
26
27 self.last_packet_seen= int(time.time()) #initialize the start of the spider/scan
28
29 if not self.processCLI():
30 return None
31 else:
32 self.clivars = True
33
34 print "Initiating Carbonator Against: ", str(self.url)
35 #add to scope if not already in there.
36 if self._callbacks.isInScope(self.url) == 0:
37 self._callbacks.includeInScope(self.url)
38
39 #added to ensure that the root directory is scanned
40 base_request = str.encode(str("GET "+self.path+" HTTP/1.1\nHost: "+self.fqdn+"\n\n"))
41 if(self.scheme == 'HTTPS'):
42 print self._callbacks.doActiveScan(self.fqdn,self.port,1,base_request)
43 else:
44 print self._callbacks.doActiveScan(self.fqdn,self.port,0,base_request)
45
46 self._callbacks.sendToSpider(self.url)
47 self._callbacks.registerHttpListener(self)
48 self._callbacks.registerScannerListener(self)
49
50 while int(time.time())-self.last_packet_seen <= self.packet_timeout:
51 time.sleep(1)
52 print "No packets seen in the last", self.packet_timeout, "seconds."
53 print "Removing Listeners"
54 self._callbacks.removeHttpListener(self)
55 self._callbacks.removeScannerListener(self)
56 self._callbacks.excludeFromScope(self.url)
57
58 print "Generating Report"
59 self.generateReport(self.rtype)
60 print "Report Generated"
61 print "Closing Burp in", self.packet_timeout, "seconds."
62 time.sleep(self.packet_timeout)
63
64 if self.clivars:
65 self._callbacks.exitSuite(False)
66
67 return
68
69 def processHttpMessage(self, tool_flag, isRequest, current):
70 self.last_packet_seen = int(time.time())
71 if tool_flag == self._callbacks.TOOL_SPIDER and isRequest: #if is a spider request then send to scanner
72 self.spider_results.append(current)
73 print "Sending new URL to Vulnerability Scanner: URL #",len(self.spider_results)
74 if self.scheme == 'https':
75 self._callbacks.doActiveScan(self.fqdn,self.port,1,current.getRequest()) #returns scan queue, push to array
76 else:
77 self._callbacks.doActiveScan(self.fqdn,self.port,0,current.getRequest()) #returns scan queue, push to array
78 return
79
80 def newScanIssue(self, issue):
81 self.scanner_results.append(issue)
82 print "New issue identified: Issue #",len(self.scanner_results);
83 return
84
85 def generateReport(self, format):
86 if format != 'XML':
87 format = 'HTML'
88
89 file_name = self.output
90 self._callbacks.generateScanReport(format,self.scanner_results,File(file_name))
91
92 time.sleep(5)
93 return
94
95 def processCLI(self):
96 cli = self._callbacks.getCommandLineArguments()
97 if len(cli) < 0:
98 print "Incomplete target information provided."
99 return False
100 elif not cli:
101 print "Integris Security Carbonator is now loaded."
102 print "If Carbonator was loaded through the BApp store then you can run in headless mode simply adding the `-Djava.awt.headless=true` flag from within your shell. Note: If burp doesn't close at the conclusion of a scan then disable Automatic Backup on Exit."
103 print "For questions or feature requests contact us at carbonator at integris security dot com."
104 print "Visit carbonator at https://www.integrissecurity.com/Carbonator"
105 return False
106 else:
107 self.url = URL(cli[0])
108 self.rtype = cli[1]
109 self.output = cli[2]
110
111 self.scheme = self.url.getProtocol()
112 self.fqdn = self.url.getHost()
113 self.port1 = self.url.getPort()
114 if self.port1 == -1 and self.scheme == 'http':
115 self.port = 80
116 elif self.port1 == -1 and self.scheme == 'https':
117 self.port = 443
118 else:
119 self.port = self.port1
120 self.path = self.url.getFile()
121 print "self.url: " + str(self.url) + "\n"
122 print "Scheme: " + self.scheme + "\n"
123 print "FQDN: " + self.fqdn + "\n"
124 print "Port: " + str(self.port1) + "\n"
125 print "Path: " + self.path + "\n"
126 return True
0 #!/usr/bin/env python
1 ###
2 ## Faraday Penetration Test IDE
3 ## Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
4 ## See the file 'doc/LICENSE' for the license information
5 ###
6 import requests
7 import json
8 import time
9 import sys
10 import argparse
11 import os
12
13 my_env = os.environ
14
15 url = my_env["CS_NESSUS_URL"] if 'CS_NESSUS_URL' in my_env else "https://127.0.0.1:8834"
16 username = my_env["CS_NESSUS_USER"] if 'CS_NESSUS_USER' in my_env else "nessus"
17 password = my_env["CS_NESSUS_PASS"] if 'CS_NESSUS_PASS' in my_env else "nessus"
18 profile = my_env["CS_NESSUS_PROFILE"] if 'CS_NESSUS_PROFILE' in my_env else "'Basic Network Scan'"
19
20 verify = False
21 token = ''
22
23 def build_url(resource):
24 return '{0}{1}'.format(url, resource)
25
26
27 def connect(method, resource, data=None):
28 """
29 Send a request
30
31 Send a request to Nessus based on the specified data. If the session token
32 is available add it to the request. Specify the content type as JSON and
33 convert the data to JSON format.
34 """
35 headers = {'X-Cookie': 'token={0}'.format(token),
36 'content-type': 'application/json'}
37
38 data = json.dumps(data)
39
40 if method == 'POST':
41 r = requests.post(build_url(resource), data=data, headers=headers, verify=verify)
42 elif method == 'PUT':
43 r = requests.put(build_url(resource), data=data, headers=headers, verify=verify)
44 elif method == 'DELETE':
45 r = requests.delete(build_url(resource), data=data, headers=headers, verify=verify)
46 else:
47 r = requests.get(build_url(resource), params=data, headers=headers, verify=verify)
48
49 # Exit if there is an error.
50 if r.status_code != 200:
51 e = r.json()
52 print e['error']
53 sys.exit()
54
55 # When downloading a scan we need the raw contents not the JSON data.
56 if 'download' in resource:
57 return r.content
58 else:
59 try:
60 return r.json()
61 except:
62 pass
63
64
65 def login(usr, pwd):
66 """
67 Login to nessus.
68 """
69
70 login = {'username': usr, 'password': pwd}
71 data = connect('POST', '/session', data=login)
72
73 return data['token']
74
75
76 def logout():
77 """
78 Logout of nessus.
79 """
80
81 connect('DELETE', '/session')
82
83
84 def get_policies():
85 """
86 Get scan policies
87
88 Get all of the scan policies but return only the title and the uuid of
89 each policy.
90 """
91
92 data = connect('GET', '/editor/policy/templates')
93
94 return dict((p['title'], p['uuid']) for p in data['templates'])
95
96
97 def get_history_ids(sid):
98 """
99 Get history ids
100
101 Create a dictionary of scan uuids and history ids so we can lookup the
102 history id by uuid.
103 """
104 data = connect('GET', '/scans/{0}'.format(sid))
105
106 return dict((h['uuid'], h['history_id']) for h in data['history'])
107
108
109 def get_scan_history(sid, hid):
110 """
111 Scan history details
112
113 Get the details of a particular run of a scan.
114 """
115 params = {'history_id': hid}
116 data = connect('GET', '/scans/{0}'.format(sid), params)
117
118 return data['info']
119
120
121 def add(name, desc, targets, pid):
122 """
123 Add a new scan
124
125 Create a new scan using the policy_id, name, description and targets. The
126 scan will be created in the default folder for the user. Return the id of
127 the newly created scan.
128 """
129
130 scan = {'uuid': pid,
131 'settings': {
132 'name': name,
133 'description': desc,
134 'text_targets': targets}
135 }
136
137 data = connect('POST', '/scans', data=scan)
138
139 return data['scan']
140
141
142 def update(scan_id, name, desc, targets, pid=None):
143 """
144 Update a scan
145
146 Update the name, description, targets, or policy of the specified scan. If
147 the name and description are not set, then the policy name and description
148 will be set to None after the update. In addition the targets value must
149 be set or you will get an "Invalid 'targets' field" error.
150 """
151
152 scan = {}
153 scan['settings'] = {}
154 scan['settings']['name'] = name
155 scan['settings']['desc'] = desc
156 scan['settings']['text_targets'] = targets
157
158 if pid is not None:
159 scan['uuid'] = pid
160
161 data = connect('PUT', '/scans/{0}'.format(scan_id), data=scan)
162
163 return data
164
165
166 def launch(sid):
167 """
168 Launch a scan
169
170 Launch the scan specified by the sid.
171 """
172
173 data = connect('POST', '/scans/{0}/launch'.format(sid))
174
175 return data['scan_uuid']
176
177
178 def status(sid, hid):
179 """
180 Check the status of a scan run
181
182 Get the historical information for the particular scan and hid. Return
183 the status if available. If not return unknown.
184 """
185
186 d = get_scan_history(sid, hid)
187 return d['status']
188
189
190 def export_status(sid, fid):
191 """
192 Check export status
193
194 Check to see if the export is ready for download.
195 """
196
197 data = connect('GET', '/scans/{0}/export/{1}/status'.format(sid, fid))
198
199 return data['status'] == 'ready'
200
201
202 def export(sid, hid):
203 """
204 Make an export request
205
206 Request an export of the scan results for the specified scan and
207 historical run. In this case the format is hard coded as nessus but the
208 format can be any one of nessus, html, pdf, csv, or db. Once the request
209 is made, we have to wait for the export to be ready.
210 """
211
212 data = {'history_id': hid,
213 'format': 'nessus'}
214
215 data = connect('POST', '/scans/{0}/export'.format(sid), data=data)
216
217 fid = data['file']
218
219 while export_status(sid, fid) is False:
220 time.sleep(5)
221
222 return fid
223
224
225 def download(sid, fid, output):
226 """
227 Download the scan results
228
229 Download the scan results stored in the export file specified by fid for
230 the scan specified by sid.
231 """
232
233 data = connect('GET', '/scans/{0}/export/{1}/download'.format(sid, fid))
234
235 print('Saving scan results to {0}.'.format(output))
236 with open(output, 'w') as f:
237 f.write(data)
238
239
240 def delete(sid):
241 """
242 Delete a scan
243
244 This deletes a scan and all of its associated history. The scan is not
245 moved to the trash folder, it is deleted.
246 """
247
248 connect('DELETE', '/scans/{0}'.format(scan_id))
249
250
251 def history_delete(sid, hid):
252 """
253 Delete a historical scan.
254
255 This deletes a particular run of the scan and not the scan itself. the
256 scan run is defined by the history id.
257 """
258
259 connect('DELETE', '/scans/{0}/history/{1}'.format(sid, hid))
260
261 if __name__ == "__main__":
262 parser = argparse.ArgumentParser(description='nessus_client is develop for automating security testing')
263 parser.add_argument('-t','--target', help='Network or Host for scan', required=False)
264 parser.add_argument('-o','--output', help='Output file', required=False)
265 args = parser.parse_args()
266
267 # Review de Command input
268 if args.target == None or args.output == None:
269 print "Argument errors check -h"
270 exit(0)
271
272 print('Login')
273 try:
274 token = login(username, password)
275 except:
276 print "Unexpected error:", sys.exc_info()[0]
277 raise
278
279
280 print('Adding new scan.' + token)
281 print args.target
282
283 policies = get_policies()
284 policy_id = policies[profile]
285 scan_data = add('CScan nessus', 'Create a new scan with API', args.target, policy_id)
286 scan_id = scan_data['id']
287
288 print('Launching new scan.')
289 scan_uuid = launch(scan_id)
290 history_ids = get_history_ids(scan_id)
291 history_id = history_ids[scan_uuid]
292 while status(scan_id, history_id) not in ('completed','canceled'):
293 time.sleep(5)
294
295
296 print('Exporting the completed scan.')
297 file_id = export(scan_id, history_id)
298 download(scan_id, file_id, args.output)
299
300 print('Deleting the scan.')
301 history_delete(scan_id, history_id)
302 delete(scan_id)
303
304 print('Logout')
305 logout()
0 #!/usr/bin/env python
1 ###
2 ## Faraday Penetration Test IDE
3 ## Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
4 ## See the file 'doc/LICENSE' for the license information
5 ###
6
7 from w3af_api_client import Connection, Scan
8 import subprocess
9 import os
10 import argparse
11 import time
12 import signal
13 from pprint import pprint
14 import atexit
15 child_pid = None
16
17 def kill_child():
18 global child_pid
19 if child_pid is None:
20 pass
21 else:
22 os.kill(child_pid, signal.SIGTERM)
23
24 def main():
25 atexit.register(kill_child)
26
27 my_env = os.environ
28 cmd = my_env["CS_W3AF"] if 'CS_W3AF' in my_env else "/root/tools/w3af/w3af_api"
29 profile = my_env["CS_W3AF_PROFILE"] if 'CS_W3AF_PROFILE' in my_env else "/root/tools/w3af/profiles/fast_scan.pw3af"
30
31 #Parser argument in command line
32 parser = argparse.ArgumentParser(description='w3af_client is develop for automating security testing')
33 parser.add_argument('-t','--target', help='Network or Host for scan', required=False)
34 parser.add_argument('-o','--output', help='Output file', required=False)
35 args = parser.parse_args()
36
37 if args.target == None or args.output == None:
38 print "Argument errors check -h"
39 exit(0)
40
41 print 'Starting w3af api ...'
42 global child_pid
43 proc = subprocess.Popen([cmd])
44 child_pid = proc.pid
45
46 print 'Waiting for W3af to load, 5 seconds ...'
47 time.sleep(5)
48
49 # Connect to the REST API and get it's version
50 conn = Connection('http://127.0.0.1:5000/')
51 print conn.get_version()
52
53 # Define the target and configuration
54 #scan_profile = file('/root/tools/w3af/profiles/fast_scan_xml.pw3af').read()
55 scan_profile = file(profile).read()
56 scan_profile = "[output.xml_file]\noutput_file = %s\n%s\n" % (args.output, scan_profile )
57 # scan_profile = file('/root/tools/w3af/profiles/fast_scan.pw3af').read()
58
59 target_urls = [args.target]
60
61 scan = Scan(conn)
62 s = scan.start(scan_profile, target_urls)
63 time.sleep(2)
64
65 # Wait some time for the scan to start and then
66 scan.get_urls()
67 scan.get_log()
68 scan.get_findings()
69
70 while(scan.get_status()['status'] == "Running"):
71 print 'Scan progress: %s' + str(scan.get_status()['rpm'])
72 time.sleep(2)
73
74 if __name__ == "__main__":
75 main()
0 #!/usr/bin/env python
1 ###
2 ## Faraday Penetration Test IDE
3 ## Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
4 ## See the file 'doc/LICENSE' for the license information
5 ###
6
7 '''
8 By tartamar
9 '''
10 import argparse
11 import time
12 import re
13 from pprint import pprint
14 from zapv2 import ZAPv2
15 import subprocess
16 import os
17 import signal
18 import atexit
19 child_pid = None
20
21 def kill_child():
22 global child_pid
23 if child_pid is None:
24 pass
25 else:
26 os.kill(child_pid, signal.SIGTERM)
27
28 def is_http_url(page):
29 """
30 Returns true if s is valid http url, else false
31 Arguments:
32 - `page`:
33 """
34 if re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', page):
35 return True
36 else:
37 return False
38
39 def exportfile(filename,zap):
40 #Output for XML Report
41 print 'Generating XML Report...'
42 filex=open(filename, 'w')
43 filex.write(zap.core.xmlreport)
44 filex.close()
45
46 def main():
47
48 atexit.register(kill_child)
49
50 my_env = os.environ
51 cmd = my_env["CS_ZAP"] if 'CS_ZAP' in my_env else "/usr/share/zaproxy/zap.sh"
52
53 #Parser argument in command line
54 parser = argparse.ArgumentParser(description='PyZap is develop for automating security testing')
55 parser.add_argument('-t','--target', help='Network or Host for scan', required=False)
56 parser.add_argument('-o','--output', help='Output file', required=False)
57 args = parser.parse_args()
58
59 # Review de Command input
60 if args.target == None:
61 # Do nothing
62 # Input data for test
63 target = raw_input('[+] Enter your target: ')
64 if is_http_url(target) == True:
65 print '[-] Target selected: ', target
66 else:
67 print '[w] Please type a correct URL address'
68 quit()
69 else:
70 # Check for valid URL addres
71 if is_http_url(args.target) == True:
72 target = args.target
73 print '[-] Target selected: ', target
74 else:
75 print '[w] Please type a correct URL Address'
76 quit()
77 print 'Starting ZAP ...'
78
79 global child_pid
80 proc = subprocess.Popen([cmd,'-daemon'])
81 child_pid = proc.pid
82
83 print 'Waiting for ZAP to load, 10 seconds ...'
84 time.sleep(10)
85 zap = ZAPv2()
86 # Use the line below if ZAP is not listening on 8090
87 zap = ZAPv2(proxies={'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080'})
88
89 # do stuff
90 print 'Accessing target %s' % target
91 # try have a unique enough session...
92 zap.urlopen(target)
93 # Give the sites tree a chance to get updated
94 time.sleep(2)
95
96 print 'Spidering target %s' % target
97 print target
98 zap.spider.scan(target)
99 # Give the Spider a chance to start
100 time.sleep(2)
101 #print 'Status %s' % zap.spider.status
102 while(int(zap.spider.status) < 100):
103 print 'Spider progress %: ' + zap.spider.status
104 time.sleep(2)
105
106 print 'Spider completed'
107 # Give the passive scanner a chance to finish
108 time.sleep(5)
109
110 print 'Scanning target %s' % target
111 zap.ascan.scan(target)
112 while(int(zap.ascan.status) < 100):
113 print 'Scan progress %: ' + zap.ascan.status
114 time.sleep(5)
115
116 print 'Scan completed'
117
118 # Report the results
119
120 print 'Hosts: ' + ', '.join(zap.core.hosts)
121 # print 'Alerts: '
122 # pprint (zap.core.alerts())
123 #pprint (zap.core.xmlreport())
124 exportfile(args.output,zap)
125
126 print 'Shutting down ZAP ...'
127 zap.core.shutdown
128 #EOF
129
130 if __name__ == "__main__":
131 main()
0 #!/bin/bash
1 ###
2 ## Faraday Penetration Test IDE
3 ## Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
4 ## See the file 'doc/LICENSE' for the license information
5 ###
6
7 while read h; do
8 NAME="nessus_$(date +%s).xml"
9 echo plugin/nessus.py --target $h --output $2$NAME
10 ./plugin/nessus.py --target $h --output $2$NAME
11 done <$1
0 #!/bin/bash
1 ###
2 ## Faraday Penetration Test IDE
3 ## Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
4 ## See the file 'doc/LICENSE' for the license information
5 ###
6
7 NAME="nmap_$(date +%s).xml"
8 ${CS_NMAP:=nmap} -iL $1 -oX $2$NAME
0 #!/bin/bash
1 ###
2 ## Faraday Penetration Test IDE
3 ## Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
4 ## See the file 'doc/LICENSE' for the license information
5 ###
6
7 # -------------
8 # CONFIG PARAMS
9 # -------------
10 # Your Dashboard login data
11 USER_NAME=${CS_OPENVAS_USER:=admin}
12 # If you set this to None, you will be asked on startup
13 USER_PASSWORD=${CS_OPENVAS_PASSWORD:=openvas}
14
15 # Your targets, seperated by space
16 #TARGET_SRVS="localhost"
17 # The name of the OpenVAS preset for the scan
18 # The following configs are available by default:
19 # Discovery
20 # empty
21 # Full and fast
22 # Full and fast ultimate
23 # Full and very deep
24 # Full and very deep ultimate
25 # Host Discovery
26 # System Discovery
27 SCAN_CONFIG=${CS_OPENVAS_SCAN_CONFIG:='Full and fast'}
28 # A valid "alive_test" parameter
29 # Defines how it is determined if the targets are alive
30 # Currently, valid values are the following:
31 # Scan Config Default
32 # ICMP, TCP-ACK Service & ARP Ping
33 # TCP-ACK Service & ARP Ping
34 # ICMP & ARP Ping
35 # ICMP & TCP-ACK Service Ping
36 # ARP Ping
37 # TCP-ACK Service Ping
38 # TCP-SYN Service Ping
39 # ICMP Ping
40 # Consider Alive
41 ALIVE_TEST=${CS_OPENVAS_ALIVE_TEST:='ICMP, TCP-ACK Service &amp; ARP Ping'}
42
43 CS_OMP=${CS_OPENVAS:=omp}
44
45
46 function fail {
47 echo "There was an error during execution! Current action: $1"
48 exit 1
49 }
50
51 function sindex {
52 x="${1%%$2*}"
53 [[ $x = $1 ]] && echo -1 || echo ${#x}
54 }
55
56
57 THREAT=0
58 ADDRS=""
59 SRV=$1
60
61 while read h; do
62
63 echo "Processing target $h..."
64
65 #echo $CS_OMP -u $USER_NAME -w $USER_PASSWORD --xml=\
66 "<create_target>\
67 <name>TARG$(date +%s)</name><hosts>$h</hosts>\
68 <alive_tests>$ALIVE_TEST</alive_tests>\
69 </create_target>"
70
71 TARGET_RETURN=$($CS_OMP -u $USER_NAME -w $USER_PASSWORD --xml=\
72 "<create_target>\
73 <name>TARG$(date +%s)</name><hosts>$h</hosts>\
74 <alive_tests>$ALIVE_TEST</alive_tests>\
75 </create_target>")
76 echo "$TARGET_RETURN" | grep -m1 'resource created' || fail 'creating target'
77
78 T_ID_INDEX=$(sindex "$TARGET_RETURN" "id=")
79 T_ID_INDEX=$((T_ID_INDEX + 4))
80 T_ID=${TARGET_RETURN:T_ID_INDEX:36}
81 echo "> Target has ID $T_ID"
82
83 C_ID=$($CS_OMP -u $USER_NAME -w $USER_PASSWORD -g | grep -i "$TEST_CONFIG")
84 if [ $? -ne 0 ]; then fail 'getting configs'; fi
85
86 C_ID=${C_ID:0:36}
87 echo "> Config $TEST_CONFIG has ID $C_ID"
88
89 J_ID=$($CS_OMP -u $USER_NAME -w $USER_PASSWORD -C -n "CScan openvas" \
90 --target="$T_ID" --config="$C_ID")
91 if [ $? -ne 0 ]; then fail 'creating job'; fi
92 echo "> Created job with ID $J_ID"
93
94 R_ID=$($CS_OMP -u $USER_NAME -w $USER_PASSWORD -S "$J_ID")
95 if [ $? -ne 0 ]; then fail 'starting job'; fi
96 echo "> Started job, report gets ID $R_ID"
97
98 while true; do
99 RET=$($CS_OMP -u $USER_NAME -w $USER_PASSWORD -G)
100 if [ $? -ne 0 ]; then fail 'querying jobs'; fi
101 RET=$(echo "$RET" | grep -m1 "$J_ID")
102 echo $RET
103 echo "$RET" | grep -m1 -i "fail" && fail 'running job'
104 echo "$RET" | grep -m1 -i -E "done|Stopped" && break
105 sleep 1
106 done
107
108 echo "> Job done, generating report..."
109
110 FILENAME=${h// /_}
111 FILENAME="openvas_${FILENAME//[^a-zA-Z0-9_\.\-]/}_$(date +%s)"
112 $CS_OMP -u $USER_NAME -w $USER_PASSWORD -R "$R_ID" > $2$FILENAME.xml
113 if [ $? -ne 0 ]; then fail 'getting report'; fi
114
115 echo "Scan done"
116
117 echo "Remove task"
118 $CS_OMP -u $USER_NAME -w $USER_PASSWORD -D "$J_ID"
119
120
121 done <$1
0 #!/bin/bash
1 ###
2 ## Faraday Penetration Test IDE
3 ## Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
4 ## See the file 'doc/LICENSE' for the license information
5 ###
6 CMD=${CS_BURP:=/root/tools/burpsuite_pro_v1.6.26.jar}
7 while read h; do
8 NAME="burp_$(date +%s).xml"
9 echo java -jar -Xmx1g -Djava.awt.headless=true $CMD $h XML $2$NAME
10 java -jar -Xmx1g -Djava.awt.headless=true $CMD $h XML $2$NAME
11 done <$1
0 #!/bin/bash
1 ###
2 ## Faraday Penetration Test IDE
3 ## Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
4 ## See the file 'doc/LICENSE' for the license information
5 ###
6 while read h; do
7 NAME="nikto_$(date +%s).xml"
8 ${CS_NIKTO:=nikto} -host $h -output $2$NAME -Format XML
9 done <$1
0 #!/bin/bash
1 ###
2 ## Faraday Penetration Test IDE
3 ## Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
4 ## See the file 'doc/LICENSE' for the license information
5 ###
6
7
8 while read h; do
9 NAME="w3af_$(date +%s).xml"
10 echo plugin/w3af.py --target $h --output $2$NAME
11 ./plugin/w3af.py --target $h --output $2$NAME
12 done <$1
13 #fix zombie w3af
14 kill -9 $(ps aux | grep w3af_api | awk -F" " {'print $2'}) 2> /dev/null
0 #!/bin/bash
1 ###
2 ## Faraday Penetration Test IDE
3 ## Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
4 ## See the file 'doc/LICENSE' for the license information
5 ###
6
7 while read h; do
8 NAME="zap_$(date +%s).xml"
9 echo ./plugin/zap.py --target $h --output $2$NAME
10 ./plugin/zap.py --target $h --output $2$NAME
11 done <$1
0 http://localhost:80
318318 /* Home Menu grande > Community */
319319 .home-list.community .item:nth-child(4) {clear:left;}
320320 /* Home Menu grande > Professional */
321 .home-list.professional .item:nth-child(4) {clear:left;}
321 .home-list.professional .item:nth-child(5) {clear:left;}
322322 /* Home Menu grande > Corporate */
323 .home-list.corporate .item:nth-child(5) {clear:left;}
323 .home-list.corporate .item:nth-child(5), .home-list.corporate .item:nth-child(9) {clear:left;}
324324
325325
326326 .item:nth-child(0) {-webkit-animation-delay: 0s;}
331331 .item:nth-child(5) {-webkit-animation-delay: 0.5s;}
332332 .item:nth-child(6) {-webkit-animation-delay: 0.6s;}
333333 .item:nth-child(7) {-webkit-animation-delay: 0.7s;}
334
334 .item:nth-child(8) {-webkit-animation-delay: 0.8s;}
335 .item:nth-child(9) {-webkit-animation-delay: 0.9s;}
335336 /* Icons on Home */
336337 .icons-color-home{color: #B3B4B5;}
337338 .fa.host{font-size: 2.6em;}
10041005 -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
10051006 box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
10061007 }
1008 #bar .chart-container, #doughnut .chart-container{height: 130px}
1009 div.form-group.input-accordion{margin: 0px!important}
1010 /* Firefox */
1011 @-moz-document url-prefix() {
1012 .severities > button > span{margin-top: -10px;}
1013 }
1014 span.bold{font-weight: bold}
1015 div.jumbotron.jumbotron-font h1{font-size: 54px}
1016 /* Normalize datepicker */
1017 div.datepicker ul.dropdown-menu{width: 288px;}
1018 div.datepicker > p > ul > li > div > table{width: 97%}
1019 div.datepicker .btn-sm, .btn-group-sm > .btn{padding: 5px 7px}
0 <?xml version="1.0" encoding="iso-8859-1"?>
1 <!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
2 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4 width="32px" height="32px" viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
5 <g id="xdEhD1.tif_9_">
6 <g id="xIXYAg_6_">
7 <g>
8 <path style="fill:#FFFFFF;" d="M31.989,29.6c-1.376-0.59-2.654-1.138-3.932-1.686c-0.536-0.23-1.062-0.611-1.61-0.645
9 c-0.538-0.033-1.094,0.302-1.65,0.448c-4.203,1.098-8.183,0.686-11.798-1.864c-1.645-1.161-2.821-2.709-3.243-4.721
10 c-0.545-2.6,0.325-4.781,2.147-6.611c1.731-1.739,3.882-2.698,6.27-3.121c3.21-0.568,6.298-0.185,9.183,1.393
11 c1.84,1.006,3.321,2.39,4.113,4.377c1.026,2.574,0.526,4.929-1.177,7.052c-0.248,0.309-0.256,0.534-0.118,0.868
12 C30.777,26.553,31.358,28.026,31.989,29.6z"/>
13 <path style="fill:#FFFFFF;" d="M26.982,10.704c-1.219-0.322-2.303-0.705-3.42-0.886c-4.469-0.724-8.604,0.071-12.15,3.02
14 c-2.238,1.861-3.504,4.258-3.397,7.24c0.027,0.765,0.223,1.524,0.348,2.328c-0.347-0.125-0.74-0.244-1.112-0.411
15 c-0.253-0.113-0.457-0.114-0.715-0.001c-2.021,0.881-4.05,1.744-6.077,2.612c-0.123,0.053-0.25,0.097-0.442,0.171
16 c0.147-0.379,0.267-0.694,0.392-1.008c0.629-1.577,1.254-3.156,1.896-4.727c0.096-0.235,0.063-0.377-0.098-0.567
17 c-1.64-1.94-2.467-4.149-2.134-6.713c0.296-2.283,1.436-4.119,3.114-5.639c2.184-1.978,4.798-3.055,7.676-3.5
18 c3.027-0.468,6.001-0.209,8.868,0.909c2.619,1.022,4.829,2.586,6.276,5.06C26.391,9.248,26.651,9.977,26.982,10.704z"/>
19 </g>
20 </g>
21 </g>
22 </svg>
0 <?xml version="1.0" encoding="iso-8859-1"?>
1 <!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
2 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4 width="48px" height="48px" viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
5 <g id="xdEhD1.tif_9_">
6 <g id="xIXYAg_6_">
7 <g>
8 <path style="fill:#B3B4B5;" d="M47.983,44.4c-2.063-0.885-3.98-1.707-5.898-2.529c-0.804-0.344-1.594-0.916-2.416-0.967
9 c-0.807-0.05-1.641,0.454-2.475,0.671c-6.305,1.647-12.275,1.03-17.698-2.796c-2.468-1.741-4.231-4.064-4.864-7.081
10 c-0.818-3.9,0.488-7.171,3.221-9.917c2.597-2.608,5.823-4.047,9.405-4.681c4.815-0.852,9.447-0.278,13.775,2.089
11 c2.76,1.509,4.981,3.586,6.17,6.565c1.54,3.861,0.79,7.393-1.765,10.578c-0.372,0.464-0.384,0.801-0.177,1.302
12 C46.166,39.83,47.037,42.039,47.983,44.4z"/>
13 <path style="fill:#B3B4B5;" d="M40.473,16.056c-1.828-0.483-3.455-1.058-5.131-1.33c-6.703-1.086-12.906,0.106-18.225,4.529
14 c-3.357,2.792-5.256,6.387-5.095,10.86c0.041,1.147,0.334,2.286,0.521,3.492c-0.52-0.188-1.11-0.366-1.668-0.616
15 c-0.379-0.17-0.686-0.171-1.072-0.002c-3.032,1.321-6.075,2.617-9.116,3.919c-0.184,0.079-0.374,0.145-0.664,0.256
16 c0.221-0.569,0.401-1.042,0.588-1.511c0.944-2.365,1.881-4.733,2.845-7.091c0.144-0.353,0.095-0.565-0.147-0.851
17 c-2.459-2.91-3.7-6.224-3.201-10.07c0.445-3.425,2.154-6.179,4.671-8.459c3.276-2.967,7.197-4.583,11.515-5.25
18 c4.541-0.702,9.002-0.314,13.302,1.364c3.929,1.533,7.243,3.878,9.415,7.589C39.587,13.871,39.977,14.966,40.473,16.056z"/>
19 </g>
20 </g>
21 </g>
22 </svg>
0 <?xml version="1.0" encoding="iso-8859-1"?>
1 <!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
2 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4 width="32px" height="32px" viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
5 <g id="_x38_ELw6c_1_">
6 <g>
7 <path style="fill:#FFFFFF;" d="M0,9.499c0.041-0.104,0.083-0.207,0.124-0.311c0.306-0.765,0.87-1.26,1.685-1.292
8 C3.19,7.842,4.575,7.881,5.98,7.881c0,6.992,0,13.972,0,20.979c-0.098,0.005-0.198,0.014-0.298,0.014
9 c-1.177,0.001-2.353,0.001-3.53,0.001c-1.09-0.001-1.769-0.504-2.1-1.552C0.044,27.294,0.018,27.273,0,27.249
10 C0,21.332,0,15.416,0,9.499z"/>
11 <path style="fill:#FFFFFF;" d="M32,27.249c-0.044,0.114-0.086,0.228-0.133,0.34c-0.316,0.743-0.875,1.233-1.676,1.264
12 c-1.381,0.054-2.766,0.015-4.17,0.015c0-6.994,0-13.973,0-20.996c0.12,0,0.241,0,0.362,0c1.145,0,2.291,0.002,3.436,0.001
13 c1.116-0.001,1.794,0.492,2.13,1.553C31.956,9.454,31.982,9.475,32,9.499C32,15.416,32,21.332,32,27.249z"/>
14 <path style="fill:#FFFFFF;" d="M20.749,7.874c1.431,0,2.822,0,4.233,0c0,7.003,0,13.984,0,20.983c-5.989,0-11.97,0-17.967,0
15 c0-6.986,0-13.959,0-20.968c1.394,0,2.792,0,4.235,0c0-0.145,0-0.254,0-0.364c0-0.719-0.008-1.437,0.001-2.155
16 c0.016-1.256,0.991-2.236,2.249-2.244c1.676-0.011,3.353-0.01,5.029,0c1.215,0.007,2.201,0.992,2.218,2.208
17 C20.759,6.166,20.749,6.998,20.749,7.874z M19.245,7.868c0-0.879,0.016-1.73-0.007-2.58c-0.01-0.391-0.353-0.662-0.772-0.663
18 c-1.634-0.004-3.268-0.004-4.902,0c-0.509,0.001-0.809,0.308-0.813,0.821c-0.004,0.645-0.001,1.291-0.001,1.936
19 c0,0.161,0,0.322,0,0.486C14.934,7.868,17.067,7.868,19.245,7.868z"/>
20 </g>
21 </g>
22 </svg>
0 <?xml version="1.0" encoding="iso-8859-1"?>
1 <!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
2 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4 width="48px" height="48px" viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
5 <g id="_x38_ELw6c_1_">
6 <g>
7 <path style="fill:#B4B4B4;" d="M0,14.248c0.062-0.155,0.124-0.311,0.186-0.466c0.459-1.147,1.305-1.891,2.527-1.939
8 c2.072-0.081,4.149-0.022,6.256-0.022c0,10.488,0,20.958,0,31.468c-0.147,0.007-0.297,0.02-0.447,0.02
9 c-1.765,0.001-3.53,0.002-5.294,0.001c-1.635-0.001-2.653-0.755-3.149-2.328C0.066,40.942,0.027,40.909,0,40.873
10 C0,31.998,0,23.123,0,14.248z"/>
11 <path style="fill:#B4B4B4;" d="M48,40.873c-0.066,0.17-0.129,0.342-0.2,0.511c-0.474,1.115-1.313,1.85-2.514,1.896
12 c-2.072,0.081-4.149,0.022-6.256,0.022c0-10.49,0-20.96,0-31.493c0.18,0,0.361,0,0.542,0c1.718,0.001,3.436,0.004,5.154,0.001
13 c1.675-0.002,2.691,0.738,3.195,2.33c0.013,0.04,0.052,0.072,0.079,0.108C48,23.123,48,31.998,48,40.873z"/>
14 <path style="fill:#B4B4B4;" d="M31.124,11.811c2.146,0,4.233,0,6.349,0c0,10.505,0,20.976,0,31.474c-8.983,0-17.955,0-26.95,0
15 c0-10.479,0-20.939,0-31.452c2.09,0,4.189,0,6.353,0c0-0.217,0-0.382,0-0.547c0-1.078-0.013-2.156,0.001-3.233
16 c0.025-1.884,1.487-3.353,3.373-3.365c2.515-0.016,5.029-0.015,7.544,0c1.822,0.01,3.302,1.489,3.327,3.312
17 C31.138,9.248,31.124,10.497,31.124,11.811z M28.868,11.802c0-1.318,0.024-2.595-0.011-3.87c-0.016-0.587-0.53-0.993-1.158-0.994
18 c-2.451-0.006-4.902-0.006-7.353,0C19.583,6.939,19.132,7.4,19.126,8.17c-0.006,0.968-0.001,1.936-0.001,2.904
19 c0,0.241,0,0.483,0,0.729C22.402,11.802,25.601,11.802,28.868,11.802z"/>
20 </g>
21 </g>
22 </svg>
0 <?xml version="1.0" encoding="iso-8859-1"?>
1 <!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
2 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4 width="32px" height="32px" viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
5 <g id="eP0hGU_2_">
6 <g>
7 <path style="fill:#FFFFFF;" d="M15.993,28.598c-2.067,0-4.134,0.001-6.201-0.001c-0.759-0.001-1.482-0.149-2.133-0.559
8 c-0.967-0.609-1.476-1.513-1.535-2.631c-0.093-1.778,0.093-3.533,0.687-5.223c0.297-0.845,0.724-1.622,1.387-2.243
9 c0.77-0.722,1.703-1.031,2.746-1.021c0.131,0.001,0.272,0.08,0.39,0.152c0.358,0.219,0.709,0.45,1.059,0.682
10 c2.199,1.464,4.992,1.467,7.199,0.006c0.322-0.213,0.654-0.412,0.967-0.637c0.292-0.209,0.607-0.232,0.946-0.195
11 c1.487,0.16,2.522,0.958,3.223,2.247c0.499,0.916,0.766,1.904,0.942,2.923c0.189,1.092,0.267,2.192,0.2,3.297
12 c-0.101,1.665-1.277,2.919-2.928,3.144c-0.274,0.037-0.553,0.056-0.83,0.057C20.073,28.6,18.033,28.598,15.993,28.598z"/>
13 <path style="fill:#FFFFFF;" d="M21.391,12.527c-0.02,2.896-2.537,5.39-5.604,5.275c-2.856-0.107-5.215-2.52-5.195-5.429
14 c0.021-3.078,2.604-5.457,5.553-5.378C19.122,7.076,21.501,9.626,21.391,12.527z"/>
15 <path style="fill:#FFFFFF;" d="M10.801,15.963c-0.431,0.078-0.847,0.129-1.25,0.232c-0.94,0.242-1.705,0.773-2.353,1.486
16 c-0.049,0.054-0.131,0.11-0.197,0.11c-0.765-0.003-1.532,0.011-2.295-0.036c-0.636-0.04-1.208-0.289-1.672-0.744
17 c-0.335-0.329-0.513-0.744-0.524-1.201c-0.032-1.316,0.003-2.632,0.408-3.9c0.105-0.327,0.296-0.638,0.501-0.917
18 c0.114-0.156,0.33-0.247,0.515-0.337c0.179-0.087,0.36-0.041,0.545,0.043c0.664,0.303,1.319,0.632,2.006,0.873
19 c0.974,0.342,1.972,0.283,2.959,0.003c0.084-0.024,0.169-0.047,0.254-0.068c0.01-0.003,0.024,0.009,0.053,0.021
20 C9.537,13.136,9.895,14.621,10.801,15.963z"/>
21 <path style="fill:#FFFFFF;" d="M21.182,15.976c0.922-1.36,1.27-2.848,1.058-4.486c0.243,0.064,0.45,0.123,0.659,0.172
22 c1.112,0.257,2.193,0.143,3.237-0.304c0.447-0.191,0.889-0.398,1.316-0.63c0.359-0.195,0.666-0.119,0.96,0.102
23 c0.358,0.268,0.53,0.661,0.667,1.068c0.228,0.679,0.316,1.384,0.365,2.094c0.032,0.459,0.027,0.92,0.051,1.38
24 c0.075,1.41-0.762,2.1-1.936,2.341c-0.38,0.078-0.78,0.07-1.171,0.081c-0.44,0.013-0.881,0.01-1.32-0.002
25 c-0.094-0.003-0.213-0.05-0.276-0.117c-0.716-0.779-1.567-1.327-2.61-1.546C21.865,16.061,21.542,16.03,21.182,15.976z"/>
26 <path style="fill:#FFFFFF;" d="M20.507,6.975c0.016-2.002,1.608-3.579,3.606-3.574c2.028,0.005,3.607,1.625,3.587,3.678
27 c-0.019,1.922-1.656,3.528-3.589,3.519C22.092,10.589,20.49,8.979,20.507,6.975z"/>
28 <path style="fill:#FFFFFF;" d="M7.899,3.402c2.02,0.018,3.608,1.642,3.594,3.632c-0.014,1.907-1.593,3.57-3.608,3.563
29 C5.855,10.59,4.283,8.93,4.294,7.021C4.305,4.981,5.907,3.385,7.899,3.402z"/>
30 </g>
31 </g>
32 </svg>
0 <?xml version="1.0" encoding="iso-8859-1"?>
1 <!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
2 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4 width="48px" height="48px" viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
5 <g id="eP0hGU_2_">
6 <g>
7 <path style="fill:#B4B4B4;" d="M23.989,43.596c-3.215,0-6.431,0.002-9.647-0.001c-1.181-0.001-2.305-0.231-3.318-0.869
8 c-1.504-0.947-2.296-2.353-2.387-4.093c-0.145-2.766,0.144-5.495,1.07-8.125c0.462-1.314,1.127-2.524,2.157-3.49
9 c1.197-1.123,2.65-1.603,4.271-1.589c0.203,0.002,0.423,0.124,0.607,0.237c0.556,0.342,1.103,0.7,1.647,1.061
10 c3.421,2.277,7.766,2.281,11.198,0.008c0.5-0.331,1.016-0.641,1.505-0.99c0.455-0.325,0.945-0.36,1.472-0.304
11 c2.313,0.248,3.923,1.491,5.014,3.494c0.777,1.425,1.191,2.962,1.465,4.547c0.294,1.699,0.415,3.409,0.312,5.129
12 c-0.157,2.589-1.986,4.542-4.555,4.891c-0.427,0.058-0.861,0.087-1.291,0.088C30.335,43.6,27.162,43.596,23.989,43.596z"/>
13 <path style="fill:#B4B4B4;" d="M32.386,18.598c-0.031,4.505-3.946,8.385-8.718,8.206c-4.443-0.166-8.112-3.92-8.081-8.446
14 c0.034-4.788,4.051-8.489,8.637-8.365C28.856,10.118,32.558,14.085,32.386,18.598z"/>
15 <path style="fill:#B4B4B4;" d="M15.913,23.943c-0.671,0.122-1.318,0.201-1.944,0.361c-1.462,0.376-2.652,1.203-3.66,2.311
16 c-0.076,0.083-0.203,0.172-0.306,0.171c-1.19-0.005-2.384,0.018-3.57-0.056c-0.989-0.062-1.88-0.45-2.601-1.157
17 c-0.522-0.511-0.798-1.158-0.816-1.869C2.966,21.657,3.02,19.61,3.65,17.638c0.162-0.509,0.46-0.992,0.779-1.426
18 c0.177-0.242,0.513-0.385,0.802-0.525c0.278-0.135,0.561-0.064,0.847,0.067c1.032,0.471,2.052,0.984,3.12,1.358
19 c1.516,0.531,3.068,0.441,4.603,0.004c0.132-0.037,0.263-0.073,0.396-0.105c0.016-0.004,0.037,0.014,0.083,0.033
20 C13.946,19.545,14.504,21.855,15.913,23.943z"/>
21 <path style="fill:#B4B4B4;" d="M32.06,23.963c1.435-2.115,1.975-4.43,1.645-6.977c0.379,0.099,0.7,0.192,1.026,0.267
22 c1.73,0.399,3.411,0.223,5.036-0.473c0.695-0.298,1.383-0.619,2.048-0.98c0.558-0.302,1.035-0.185,1.494,0.159
23 c0.556,0.416,0.824,1.028,1.037,1.661c0.355,1.056,0.491,2.153,0.567,3.257c0.049,0.714,0.042,1.431,0.08,2.146
24 c0.117,2.193-1.185,3.267-3.012,3.641c-0.592,0.121-1.213,0.109-1.822,0.127c-0.684,0.02-1.37,0.015-2.054-0.004
25 c-0.147-0.004-0.331-0.077-0.428-0.183c-1.113-1.211-2.438-2.064-4.061-2.405C33.124,24.095,32.621,24.047,32.06,23.963z"/>
26 <path style="fill:#B4B4B4;" d="M31.01,9.962c0.025-3.114,2.5-5.567,5.608-5.559c3.155,0.008,5.611,2.527,5.579,5.721
27 c-0.03,2.99-2.577,5.487-5.582,5.474C33.476,15.583,30.984,13.078,31.01,9.962z"/>
28 <path style="fill:#B4B4B4;" d="M11.399,4.404c3.142,0.028,5.612,2.554,5.59,5.649c-0.021,2.966-2.478,5.553-5.613,5.543
29 c-3.157-0.011-5.602-2.594-5.586-5.564C5.808,6.86,8.301,4.376,11.399,4.404z"/>
30 </g>
31 </g>
32 </svg>
0 <?xml version="1.0" encoding="utf-8"?>
1 <!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
2 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4 width="32px" height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
5 <g>
6 <g id="isbxUX_1_">
7 <g>
8 <g>
9 <path fill="#FFFFFF" d="M11.346,16.465c-0.09-0.059-0.156-0.104-0.224-0.147c-1.024-0.644-2.049-1.287-3.073-1.93
10 c-0.243-0.153-0.348-0.353-0.302-0.574c0.075-0.36,0.465-0.524,0.792-0.324c0.476,0.292,0.946,0.592,1.418,0.889
11 c0.861,0.541,1.721,1.081,2.581,1.624c0.425,0.268,0.417,0.701-0.017,0.958c-1.321,0.783-2.642,1.565-3.965,2.345
12 c-0.338,0.199-0.694,0.071-0.797-0.279c-0.069-0.237,0.034-0.454,0.288-0.605c0.86-0.509,1.721-1.018,2.581-1.527
13 C10.858,16.757,11.089,16.619,11.346,16.465z"/>
14 </g>
15 </g>
16 <path fill="#FFFFFF" d="M18.495,18.806c0,0.326-0.267,0.593-0.593,0.593h-4.213c-0.326,0-0.593-0.267-0.593-0.593V18.76
17 c0-0.326,0.267-0.593,0.593-0.593h4.213c0.326,0,0.593,0.267,0.593,0.593V18.806z"/>
18 </g>
19 <path fill="#FFFFFF" d="M30.259,5.651H1.741C1.333,5.651,1,5.984,1,6.392v19.956h30V6.392C31,5.984,30.667,5.651,30.259,5.651z
20 M2.308,6.959h27.384v0.044v0.899H2.308V7.003V6.959z M2.308,24.081V9.21h27.384v14.872H2.308z"/>
21 </g>
22 </svg>
0 <?xml version="1.0" encoding="utf-8"?>
1 <!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
2 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4 width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
5 <g>
6 <g id="isbxUX_1_">
7 <g>
8 <g>
9 <path fill="#B3B4B5" d="M16.891,24.711c-0.137-0.09-0.239-0.159-0.343-0.225c-1.565-0.983-3.13-1.965-4.694-2.948
10 c-0.372-0.234-0.532-0.539-0.462-0.877c0.114-0.551,0.71-0.801,1.209-0.494c0.726,0.446,1.445,0.904,2.167,1.357
11 c1.314,0.826,2.63,1.651,3.942,2.48c0.649,0.41,0.636,1.07-0.026,1.463c-2.018,1.195-4.036,2.39-6.056,3.582
12 c-0.516,0.304-1.061,0.108-1.217-0.427c-0.106-0.362,0.051-0.694,0.44-0.923c1.314-0.778,2.629-1.554,3.943-2.332
13 C16.146,25.157,16.498,24.945,16.891,24.711z"/>
14 </g>
15 </g>
16 <path fill="#B3B4B5" d="M27.81,28.286c0,0.498-0.408,0.906-0.906,0.906h-6.435c-0.498,0-0.906-0.408-0.906-0.906v-0.069
17 c0-0.498,0.408-0.906,0.906-0.906h6.435c0.498,0,0.906,0.408,0.906,0.906V28.286z"/>
18 </g>
19 <path fill="#B3B4B5" d="M45.78,8.193H2.22c-0.624,0-1.132,0.508-1.132,1.132v30.483h45.824V9.325
20 C46.912,8.7,46.404,8.193,45.78,8.193z M3.086,10.191h41.828v0.067v1.373H3.086v-1.373V10.191z M3.086,36.344V13.628h41.828v22.716
21 H3.086z"/>
22 </g>
23 </svg>
0 <?xml version="1.0" encoding="iso-8859-1"?>
1 <!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
2 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4 width="32px" height="32px" viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
5 <g id="_x38_lzLR4.tif_1_">
6 <g>
7 <path style="fill:#FFFFFF;" d="M23.983,19.968c-2.237,0-4.43,0-6.68,0c0,0.35-0.011,0.693,0.004,1.036
8 c0.01,0.223-0.067,0.295-0.289,0.291c-0.206-0.003-0.315-0.043-0.304-0.279c0.015-0.335,0.004-0.672,0.004-1.034
9 c-2.019,0-4.017,0-6.062,0c0,0.557-0.017,1.112,0.007,1.666c0.012,0.282-0.067,0.387-0.355,0.418
10 c-0.296,0.031-0.583,0.146-0.934,0.239c0-0.173,0-0.315,0-0.457c0.001-0.96-0.005-1.921,0.009-2.881
11 c0.004-0.255-0.061-0.343-0.332-0.343c-2.901,0.008-5.803-0.001-8.705,0.009c-0.293,0.001-0.344-0.095-0.344-0.362
12 C0.009,12.532,0.01,6.792,0,1.052C-0.001,0.746,0.098,0.69,0.377,0.69C7.489,0.697,14.6,0.694,21.711,0.694
13 c0.19,0,0.382,0.021,0.569-0.001c0.291-0.033,0.351,0.094,0.35,0.361c-0.01,2.216-0.009,4.431-0.008,6.647
14 c0,0.453,0.023,0.907,0.015,1.36c-0.005,0.255,0.099,0.314,0.339,0.313c2.881-0.007,5.761,0.001,8.641-0.011
15 c0.311-0.001,0.38,0.091,0.38,0.389C31.99,15.472,31.989,21.192,32,26.91c0.001,0.328-0.087,0.399-0.405,0.398
16 c-2.838-0.012-5.677-0.009-8.515,0.001c-0.272,0.001-0.385-0.061-0.363-0.349c0.015-0.186-0.024-0.377-0.052-0.565
17 c-0.041-0.273,0.057-0.395,0.342-0.373c0.263,0.02,0.527,0.016,0.791,0.022c0.181,0.004,0.263-0.072,0.262-0.265
18 c-0.006-1.203,0.002-2.405-0.007-3.608c-0.004-0.61-0.033-1.219-0.051-1.829C23.999,20.228,23.991,20.115,23.983,19.968z
19 M7.953,7.326c2.249,0,4.477,0,6.722,0c0-0.919,0-1.818,0-2.734c-2.248,0-4.475,0-6.722,0C7.953,5.502,7.953,6.394,7.953,7.326z
20 M17.323,15.999c2.227,0,4.433,0,6.659,0c0-0.919,0-1.817,0-2.735c-2.227,0-4.433,0-6.659,0
21 C17.323,14.175,17.323,15.067,17.323,15.999z M23.983,16.694c-2.233,0-4.447,0-6.66,0c0,0.9,0,1.771,0,2.651
22 c2.233,0,4.447,0,6.66,0C23.983,18.444,23.983,17.574,23.983,16.694z M1.31,4.591c0,0.933,0,1.825,0,2.73c2.02,0,4.016,0,6.016,0
23 c0-0.924,0-1.823,0-2.73C5.312,4.591,3.323,4.591,1.31,4.591z M21.323,7.326c0-0.934,0-1.826,0-2.729c-2.017,0-4.013,0-6.017,0
24 c0,0.925,0,1.825,0,2.729C17.323,7.326,19.312,7.326,21.323,7.326z M10.675,15.994c2.015,0,4.011,0,6.017,0
25 c0-0.923,0-1.822,0-2.72c-2.025,0-4.021,0-6.017,0C10.675,14.196,10.675,15.088,10.675,15.994z M30.694,13.265
26 c-2.031,0-4.019,0-6.025,0c0,0.919,0,1.819,0,2.728c2.017,0,4.014,0,6.025,0C30.694,15.076,30.694,14.185,30.694,13.265z
27 M24.667,23.33c0,0.911,0,1.783,0,2.667c2.016,0,4.011,0,6.017,0c0-0.903,0-1.78,0-2.667C28.666,23.33,26.678,23.33,24.667,23.33z
28 M24.667,19.976c0,0.911,0,1.781,0,2.661c2.023,0,4.025,0,6.027,0c0-0.901,0-1.771,0-2.661
29 C28.679,19.976,26.683,19.976,24.667,19.976z M24.672,16.683c0,0.912,0,1.782,0,2.667c2.02,0,4.016,0,6.016,0
30 c0-0.903,0-1.781,0-2.667C28.674,16.683,26.685,16.683,24.672,16.683z M10.676,19.354c2.031,0,4.019,0,6.025,0
31 c0-0.898,0-1.777,0-2.664c-2.017,0-4.013,0-6.025,0C10.676,17.586,10.676,18.456,10.676,19.354z M1.307,17.328
32 c2.031,0,4.019,0,6.023,0c0-0.899,0-1.777,0-2.663c-2.018,0-4.014,0-6.023,0C1.307,15.562,1.307,16.432,1.307,17.328z
33 M7.331,11.303c-2.031,0-4.019,0-6.024,0c0,0.899,0,1.777,0,2.663c2.018,0,4.013,0,6.024,0C7.331,13.07,7.331,12.2,7.331,11.303z
34 M7.333,8.02c-2.022,0-4.025,0-6.027,0c0,0.901,0,1.771,0,2.651c2.023,0,4.025,0,6.027,0C7.333,9.77,7.333,8.9,7.333,8.02z
35 M7.954,10.675c0.491,0,0.939,0,1.423,0c0-0.339,0.015-0.654-0.005-0.967C9.357,9.452,9.431,9.36,9.703,9.365
36 c1.001,0.015,2.003,0.006,3.005,0.006c0.65,0,1.3,0,1.965,0c0-0.477,0-0.912,0-1.353c-2.251,0-4.478,0-6.719,0
37 C7.954,8.914,7.954,9.784,7.954,10.675z M15.298,8.01c0,0.469,0,0.897,0,1.331c2.023,0,4.025,0,6.025,0c0-0.458,0-0.886,0-1.331
38 C19.308,8.01,17.313,8.01,15.298,8.01z M9.359,14.658c-0.49,0-0.939,0-1.405,0c0,0.897,0,1.774,0,2.67c0.475,0,0.931,0,1.405,0
39 C9.359,16.432,9.359,15.555,9.359,14.658z M9.348,13.974c0-0.904,0-1.789,0-2.671c-0.479,0-0.927,0-1.384,0
40 c0,0.904,0,1.789,0,2.671C8.442,13.974,8.891,13.974,9.348,13.974z"/>
41 <path style="fill:#FFFFFF;" d="M2.021,24.64c-0.555,0-1.067-0.001-1.579,0c-0.43,0.001-0.43,0.003-0.431-0.436
42 c-0.001-0.837-0.001-0.837,0.848-0.837c0.781,0,1.561,0.006,2.341-0.005c0.127-0.002,0.264-0.057,0.379-0.119
43 c1.738-0.942,3.867-0.602,5.212,0.839c0.106,0.113,0.191,0.223,0.358,0.067c1.135-1.07,3.335-1.013,4.441,0.058
44 c0.014,0.013,0.037,0.017,0.094,0.042c0.077-0.073,0.167-0.155,0.253-0.241c1.355-1.362,3.319-1.675,5.037-0.817
45 c0.207,0.103,0.456,0.163,0.687,0.168c0.865,0.019,1.73,0.021,2.595,0.001c0.305-0.007,0.393,0.103,0.368,0.384
46 c-0.017,0.189-0.018,0.381-0.003,0.569c0.02,0.247-0.069,0.337-0.322,0.33c-0.535-0.016-1.072-0.005-1.672-0.005
47 c0.061,0.137,0.095,0.228,0.141,0.313c1.363,2.515-0.033,5.582-2.851,6.256c-1.87,0.447-4.002-0.598-4.801-2.381
48 c-0.532-1.189-0.545-2.387-0.022-3.581c0.091-0.209,0.059-0.343-0.081-0.507c-0.908-1.062-2.535-1.048-3.401,0.029
49 c-0.12,0.149-0.143,0.274-0.061,0.456c1.202,2.668-0.487,5.669-3.401,6.054c-2.435,0.321-4.728-1.661-4.774-4.115
50 c-0.015-0.787,0.13-1.528,0.516-2.218C1.936,24.865,1.964,24.776,2.021,24.64z M13.947,26.977
51 c0.017,1.697,1.292,3.057,3.065,3.085c1.648,0.026,3.067-1.387,3.044-3.043c-0.025-1.747-1.327-3.033-3.05-3.067
52 C15.355,23.918,13.949,25.33,13.947,26.977z M8.685,27.029c0.067-1.582-1.239-3.061-3.047-3.081
53 c-1.638-0.017-3.011,1.377-3.03,3.016c-0.019,1.668,1.291,3.1,3.045,3.09C7.521,30.043,8.712,28.556,8.685,27.029z"/>
54 </g>
55 </g>
56 </svg>
0 <?xml version="1.0" encoding="iso-8859-1"?>
1 <!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
2 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4 width="48px" height="48px" viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
5 <g id="_x38_lzLR4.tif_1_">
6 <g>
7 <path style="fill:#B3B4B5;" d="M35.975,29.953c-3.356,0-6.645,0-10.02,0c0,0.525-0.017,1.04,0.006,1.554
8 c0.015,0.334-0.101,0.442-0.434,0.437c-0.309-0.005-0.472-0.065-0.456-0.418c0.023-0.503,0.006-1.008,0.006-1.551
9 c-3.029,0-6.025,0-9.093,0c0,0.835-0.025,1.668,0.01,2.499c0.018,0.423-0.1,0.58-0.533,0.627
10 c-0.444,0.047-0.875,0.219-1.401,0.359c0-0.26,0-0.473,0-0.686c0.002-1.44-0.008-2.881,0.013-4.321
11 c0.006-0.383-0.091-0.515-0.498-0.514c-4.352,0.012-8.705-0.002-13.057,0.013c-0.439,0.002-0.516-0.142-0.516-0.543
12 C0.014,18.798,0.015,10.188,0,1.578C-0.001,1.12,0.147,1.036,0.566,1.036C11.233,1.046,21.9,1.042,32.567,1.041
13 c0.285,0,0.573,0.031,0.854-0.001c0.437-0.05,0.527,0.141,0.525,0.542c-0.015,3.324-0.013,6.647-0.012,9.971
14 c0,0.68,0.035,1.36,0.023,2.04c-0.007,0.382,0.149,0.471,0.509,0.47c4.321-0.011,8.641,0.001,12.962-0.017
15 c0.466-0.002,0.57,0.137,0.57,0.584C47.985,23.209,47.983,31.788,48,40.366c0.001,0.492-0.13,0.599-0.607,0.597
16 c-4.257-0.018-8.515-0.014-12.772,0.002c-0.408,0.001-0.578-0.092-0.544-0.524c0.022-0.279-0.036-0.566-0.078-0.847
17 c-0.061-0.409,0.085-0.592,0.513-0.559c0.394,0.03,0.79,0.024,1.186,0.033c0.271,0.006,0.394-0.108,0.393-0.398
18 c-0.009-1.804,0.003-3.608-0.01-5.412c-0.006-0.915-0.049-1.829-0.077-2.744C35.998,30.343,35.986,30.173,35.975,29.953z
19 M11.929,10.989c3.374,0,6.715,0,10.083,0c0-1.378,0-2.727,0-4.101c-3.372,0-6.713,0-10.083,0
20 C11.929,8.254,11.929,9.592,11.929,10.989z M25.985,23.999c3.341,0,6.65,0,9.988,0c0-1.378,0-2.726,0-4.102
21 c-3.34,0-6.649,0-9.988,0C25.985,21.263,25.985,22.601,25.985,23.999z M35.975,25.041c-3.349,0-6.67,0-9.99,0
22 c0,1.35,0,2.656,0,3.977c3.35,0,6.67,0,9.99,0C35.975,27.667,35.975,26.362,35.975,25.041z M1.965,6.887c0,1.4,0,2.738,0,4.095
23 c3.03,0,6.024,0,9.024,0c0-1.386,0-2.735,0-4.095C7.968,6.887,4.985,6.887,1.965,6.887z M31.984,10.99c0-1.401,0-2.739,0-4.093
24 c-3.026,0-6.02,0-9.025,0c0,1.388,0,2.737,0,4.093C25.985,10.99,28.968,10.99,31.984,10.99z M16.013,23.992
25 c3.023,0,6.017,0,9.026,0c0-1.385,0-2.733,0-4.08c-3.038,0-6.031,0-9.026,0C16.013,21.294,16.013,22.632,16.013,23.992z
26 M46.041,19.898c-3.046,0-6.029,0-9.037,0c0,1.379,0,2.728,0,4.092c3.026,0,6.021,0,9.037,0
27 C46.041,22.615,46.041,21.278,46.041,19.898z M37.001,34.995c0,1.367,0,2.674,0,4.001c3.024,0,6.017,0,9.025,0
28 c0-1.354,0-2.67,0-4.001C42.999,34.995,40.017,34.995,37.001,34.995z M37.001,29.964c0,1.366,0,2.672,0,3.991
29 c3.034,0,6.038,0,9.04,0c0-1.351,0-2.657,0-3.991C43.018,29.964,40.025,29.964,37.001,29.964z M37.008,25.025c0,1.368,0,2.673,0,4
30 c3.03,0,6.024,0,9.024,0c0-1.354,0-2.672,0-4C43.011,25.025,40.028,25.025,37.008,25.025z M16.014,29.032c3.046,0,6.029,0,9.037,0
31 c0-1.347,0-2.665,0-3.996c-3.026,0-6.02,0-9.037,0C16.014,26.38,16.014,27.685,16.014,29.032z M1.96,25.992
32 c3.047,0,6.029,0,9.035,0c0-1.348,0-2.666,0-3.995c-3.027,0-6.021,0-9.035,0C1.96,23.343,1.96,24.648,1.96,25.992z M10.997,16.955
33 c-3.047,0-6.029,0-9.036,0c0,1.348,0,2.666,0,3.995c3.027,0,6.02,0,9.036,0C10.997,19.605,10.997,18.3,10.997,16.955z
34 M10.999,12.031c-3.033,0-6.038,0-9.04,0c0,1.351,0,2.656,0,3.976c3.034,0,6.038,0,9.04,0
35 C10.999,14.656,10.999,13.35,10.999,12.031z M11.931,16.013c0.737,0,1.409,0,2.135,0c0-0.508,0.022-0.981-0.007-1.45
36 c-0.024-0.384,0.088-0.522,0.496-0.515c1.502,0.023,3.005,0.009,4.507,0.009c0.975,0,1.95,0,2.947,0c0-0.715,0-1.368,0-2.03
37 c-3.376,0-6.717,0-10.078,0C11.931,13.372,11.931,14.677,11.931,16.013z M22.947,12.015c0,0.704,0,1.345,0,1.997
38 c3.034,0,6.038,0,9.038,0c0-0.687,0-1.329,0-1.997C28.962,12.015,25.97,12.015,22.947,12.015z M14.038,21.987
39 c-0.735,0-1.408,0-2.108,0c0,1.345,0,2.661,0,4.005c0.713,0,1.396,0,2.108,0C14.038,24.648,14.038,23.333,14.038,21.987z
40 M14.022,20.961c0-1.356,0-2.683,0-4.007c-0.718,0-1.39,0-2.076,0c0,1.356,0,2.683,0,4.007
41 C12.663,20.961,13.336,20.961,14.022,20.961z"/>
42 <path style="fill:#B3B4B5;" d="M3.032,36.96c-0.833,0-1.601-0.002-2.369,0c-0.645,0.002-0.645,0.004-0.646-0.654
43 C0.016,35.05,0.016,35.05,1.289,35.05c1.171,0,2.342,0.009,3.512-0.007c0.191-0.003,0.396-0.085,0.568-0.178
44 c2.607-1.413,5.801-0.903,7.818,1.258c0.159,0.17,0.287,0.335,0.537,0.1c1.703-1.605,5.003-1.52,6.662,0.087
45 c0.021,0.02,0.056,0.026,0.141,0.063c0.115-0.109,0.251-0.232,0.38-0.361c2.032-2.043,4.979-2.512,7.555-1.225
46 c0.31,0.155,0.684,0.244,1.031,0.252c1.297,0.029,2.595,0.032,3.892,0.001c0.458-0.011,0.59,0.155,0.552,0.576
47 c-0.026,0.283-0.027,0.571-0.004,0.854c0.03,0.37-0.103,0.506-0.483,0.495c-0.803-0.024-1.608-0.007-2.508-0.007
48 c0.091,0.205,0.142,0.342,0.211,0.469c2.044,3.772-0.049,8.373-4.277,9.384c-2.805,0.671-6.003-0.897-7.201-3.572
49 c-0.798-1.783-0.818-3.581-0.033-5.371c0.137-0.313,0.089-0.515-0.121-0.76c-1.362-1.593-3.802-1.572-5.101,0.044
50 c-0.18,0.224-0.214,0.411-0.091,0.684c1.803,4.002-0.731,8.504-5.102,9.081c-3.652,0.482-7.092-2.492-7.161-6.172
51 c-0.022-1.181,0.195-2.292,0.774-3.327C2.904,37.298,2.946,37.165,3.032,36.96z M20.92,40.466
52 c0.025,2.546,1.938,4.585,4.597,4.627c2.472,0.039,4.601-2.08,4.566-4.565c-0.037-2.621-1.991-4.549-4.575-4.6
53 C23.032,35.878,20.924,37.995,20.92,40.466z M13.028,40.544c0.1-2.373-1.858-4.592-4.57-4.621
54 c-2.457-0.026-4.517,2.066-4.545,4.524c-0.028,2.502,1.936,4.65,4.568,4.635C11.281,45.065,13.068,42.834,13.028,40.544z"/>
55 </g>
56 </g>
57 </svg>
2424 <link rel="stylesheet" href="script/bootstrap-theme.min.css">
2525 <link rel="stylesheet" type="text/css" href="styles/font-awesome.css" />
2626 <link rel="stylesheet" type="text/css" href="styles/angular-hotkeys.css" />
27 <link rel="stylesheet" type="text/css" href="script/angular-chart.css" />
2728
2829 <!-- Icons -->
2930 <link href="favicon.ico" rel="shortcut icon">
4748 <script type="text/javascript" src="script/jquery.qtip.js"></script>
4849 <script type="text/javascript" src="script/cryptojs-sha1.js"></script>
4950 <script type="text/javascript" src="script/ZeroClipboard.min.js"></script>
51 <!-- angular chart -->
52 <script type="text/javascript" src="script/Chart.js"></script>
53 <script type="text/javascript" src="script/angular-chart.min.js"></script>
5054 </head>
5155
5256 <body>
5357 <div id="cont">
54 <div class="wrapper">
58 <div class="wrapper" ng-controller="indexCtrl">
5559 <header class="head">
56 <a href="#" class="ws-dashboard"><img class="logo animated fadeInDown" src="images/logo-faraday.svg" alt="Faraday home | WS Dashboard"/></a>
60 <a href="#" class="ws-dashboard"><img class="logo animated fadeInDown" title="{{version}}" src="images/logo-faraday.svg" alt="Faraday home | WS Dashboard"/></a>
5761 </header>
5862
5963 <div ng-controller="navigationCtrl" ng-include="'scripts/navigation/partials/leftBar.html'"></div>
6670 <script type="text/javascript" src="scripts/attachments/providers/attachments.js"></script>
6771 <script type="text/javascript" src="scripts/commons/directives/contenteditable.js"></script>
6872 <script type="text/javascript" src="scripts/commons/controllers/modal.js"></script>
73 <script type="text/javascript" src="scripts/commons/controllers/commercialCtrl.js"></script>
6974 <script type="text/javascript" src="scripts/commons/providers/commons.js"></script>
7075 <script type="text/javascript" src="scripts/commons/filters/decodeURIComponent.js"></script>
7176 <script type="text/javascript" src="scripts/commons/filters/encodeURIComponent.js"></script>
7378 <script type="text/javascript" src="scripts/commons/filters/orderObjectBy.js"></script>
7479 <script type="text/javascript" src="scripts/commons/filters/startFrom.js"></script>
7580 <script type="text/javascript" src="scripts/commons/filters/integer.js"></script>
81 <script type="text/javascript" src="scripts/config/services/config.js"></script>
7682 <script type="text/javascript" src="scripts/csv/providers/csv.js"></script>
7783 <script type="text/javascript" src="scripts/fileExporter/directives/download.js"></script>
7884 <script type="text/javascript" src="scripts/fileExporter/providers/blob.js"></script>
8490 <script type="text/javascript" src="scripts/hosts/controllers/hostsModalNew.js"></script>
8591 <script type="text/javascript" src="scripts/hosts/providers/host.js"></script>
8692 <script type="text/javascript" src="scripts/hosts/providers/hosts.js"></script>
93 <script type="text/javascript" src="scripts/index/controllers/indexCtrl.js"></script>
94 <script type="text/javascript" src="scripts/index/providers/index.js"></script>
8795 <script type="text/javascript" src="scripts/navigation/controllers/navigationCtrl.js"></script>
8896 <script type="text/javascript" src="scripts/notes/providers/notes.js"></script>
8997 <script type="text/javascript" src="scripts/services/controllers/serviceModalNew.js"></script>
0 /*! normalize.css v1.1.3 | MIT License | git.io/normalize */
1
2 /* ==========================================================================
3 HTML5 display definitions
4 ========================================================================== */
5
6 /**
7 * Correct `block` display not defined in IE 6/7/8/9 and Firefox 3.
0 /*! normalize.css v3.0.2 | MIT License | git.io/normalize */
1
2 /**
3 * 1. Set default font family to sans-serif.
4 * 2. Prevent iOS text size adjust after orientation change, without disabling
5 * user zoom.
6 */
7
8 html {
9 font-family: sans-serif; /* 1 */
10 -ms-text-size-adjust: 100%; /* 2 */
11 -webkit-text-size-adjust: 100%; /* 2 */
12 }
13
14 /**
15 * Remove default margin.
16 */
17
18 body {
19 margin: 0;
20 }
21
22 /* HTML5 display definitions
23 ========================================================================== */
24
25 /**
26 * Correct `block` display not defined for any HTML5 element in IE 8/9.
27 * Correct `block` display not defined for `details` or `summary` in IE 10/11
28 * and Firefox.
29 * Correct `block` display not defined for `main` in IE 11.
830 */
931
1032 article,
1638 header,
1739 hgroup,
1840 main,
41 menu,
1942 nav,
2043 section,
2144 summary {
22 display: block;
23 }
24
25 /**
26 * Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3.
45 display: block;
46 }
47
48 /**
49 * 1. Correct `inline-block` display not defined in IE 8/9.
50 * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
2751 */
2852
2953 audio,
3054 canvas,
55 progress,
3156 video {
32 display: inline-block;
33 *display: inline;
34 *zoom: 1;
57 display: inline-block; /* 1 */
58 vertical-align: baseline; /* 2 */
3559 }
3660
3761 /**
4064 */
4165
4266 audio:not([controls]) {
43 display: none;
44 height: 0;
45 }
46
47 /**
48 * Address styling not present in IE 7/8/9, Firefox 3, and Safari 4.
49 * Known issue: no IE 6 support.
50 */
51
52 [hidden] {
53 display: none;
54 }
55
56 /* ==========================================================================
57 Base
58 ========================================================================== */
59
60 /**
61 * 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using
62 * `em` units.
63 * 2. Prevent iOS text size adjust after orientation change, without disabling
64 * user zoom.
65 */
66
67 html {
68 font-size: 100%; /* 1 */
69 -ms-text-size-adjust: 100%; /* 2 */
70 -webkit-text-size-adjust: 100%; /* 2 */
71 }
72
73 /**
74 * Address `font-family` inconsistency between `textarea` and other form
75 * elements.
76 */
77
78 html,
79 button,
80 input,
81 select,
82 textarea {
83 font-family: sans-serif;
84 }
85
86 /**
87 * Address margins handled incorrectly in IE 6/7.
88 */
89
90 body {
91 margin: 0;
92 }
93
94 /* ==========================================================================
95 Links
96 ========================================================================== */
97
98 /**
99 * Address `outline` inconsistency between Chrome and other browsers.
100 */
101
102 a:focus {
103 outline: thin dotted;
67 display: none;
68 height: 0;
69 }
70
71 /**
72 * Address `[hidden]` styling not present in IE 8/9/10.
73 * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
74 */
75
76 [hidden],
77 template {
78 display: none;
79 }
80
81 /* Links
82 ========================================================================== */
83
84 /**
85 * Remove the gray background color from active links in IE 10.
86 */
87
88 a {
89 background-color: transparent;
10490 }
10591
10692 /**
10995
11096 a:active,
11197 a:hover {
112 outline: 0;
113 }
114
115 /* ==========================================================================
116 Typography
117 ========================================================================== */
118
119 /**
120 * Address font sizes and margins set differently in IE 6/7.
121 * Address font sizes within `section` and `article` in Firefox 4+, Safari 5,
122 * and Chrome.
123 */
124
125 h1 {
126 font-size: 2em;
127 margin: 0.67em 0;
128 }
129
130 h2 {
131 font-size: 1.5em;
132 margin: 0.83em 0;
133 }
134
135 h3 {
136 font-size: 1.17em;
137 margin: 1em 0;
138 }
139
140 h4 {
141 font-size: 1em;
142 margin: 1.33em 0;
143 }
144
145 h5 {
146 font-size: 0.83em;
147 margin: 1.67em 0;
148 }
149
150 h6 {
151 font-size: 0.67em;
152 margin: 2.33em 0;
153 }
154
155 /**
156 * Address styling not present in IE 7/8/9, Safari 5, and Chrome.
98 outline: 0;
99 }
100
101 /* Text-level semantics
102 ========================================================================== */
103
104 /**
105 * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
157106 */
158107
159108 abbr[title] {
160 border-bottom: 1px dotted;
161 }
162
163 /**
164 * Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome.
109 border-bottom: 1px dotted;
110 }
111
112 /**
113 * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
165114 */
166115
167116 b,
168117 strong {
169 font-weight: bold;
170 }
171
172 blockquote {
173 margin: 1em 40px;
174 }
175
176 /**
177 * Address styling not present in Safari 5 and Chrome.
118 font-weight: bold;
119 }
120
121 /**
122 * Address styling not present in Safari and Chrome.
178123 */
179124
180125 dfn {
181 font-style: italic;
126 font-style: italic;
127 }
128
129 /**
130 * Address variable `h1` font-size and margin within `section` and `article`
131 * contexts in Firefox 4+, Safari, and Chrome.
132 */
133
134 h1 {
135 font-size: 2em;
136 margin: 0.67em 0;
137 }
138
139 /**
140 * Address styling not present in IE 8/9.
141 */
142
143 mark {
144 background: #ff0;
145 color: #000;
146 }
147
148 /**
149 * Address inconsistent and variable font size in all browsers.
150 */
151
152 small {
153 font-size: 80%;
154 }
155
156 /**
157 * Prevent `sub` and `sup` affecting `line-height` in all browsers.
158 */
159
160 sub,
161 sup {
162 font-size: 75%;
163 line-height: 0;
164 position: relative;
165 vertical-align: baseline;
166 }
167
168 sup {
169 top: -0.5em;
170 }
171
172 sub {
173 bottom: -0.25em;
174 }
175
176 /* Embedded content
177 ========================================================================== */
178
179 /**
180 * Remove border when inside `a` element in IE 8/9/10.
181 */
182
183 img {
184 border: 0;
185 }
186
187 /**
188 * Correct overflow not hidden in IE 9/10/11.
189 */
190
191 svg:not(:root) {
192 overflow: hidden;
193 }
194
195 /* Grouping content
196 ========================================================================== */
197
198 /**
199 * Address margin not present in IE 8/9 and Safari.
200 */
201
202 figure {
203 margin: 1em 40px;
182204 }
183205
184206 /**
185207 * Address differences between Firefox and other browsers.
186 * Known issue: no IE 6/7 normalization.
187208 */
188209
189210 hr {
190 -moz-box-sizing: content-box;
191 box-sizing: content-box;
192 height: 0;
193 }
194
195 /**
196 * Address styling not present in IE 6/7/8/9.
197 */
198
199 mark {
200 background: #ff0;
201 color: #000;
202 }
203
204 /**
205 * Address margins set differently in IE 6/7.
206 */
207
208 p,
211 -moz-box-sizing: content-box;
212 box-sizing: content-box;
213 height: 0;
214 }
215
216 /**
217 * Contain overflow in all browsers.
218 */
219
209220 pre {
210 margin: 1em 0;
211 }
212
213 /**
214 * Correct font family set oddly in IE 6, Safari 4/5, and Chrome.
221 overflow: auto;
222 }
223
224 /**
225 * Address odd `em`-unit font size rendering in all browsers.
215226 */
216227
217228 code,
218229 kbd,
219230 pre,
220231 samp {
221 font-family: monospace, serif;
222 _font-family: 'courier new', monospace;
223 font-size: 1em;
224 }
225
226 /**
227 * Improve readability of pre-formatted text in all browsers.
228 */
229
230 pre {
231 white-space: pre;
232 white-space: pre-wrap;
233 word-wrap: break-word;
234 }
235
236 /**
237 * Address CSS quotes not supported in IE 6/7.
238 */
239
240 q {
241 quotes: none;
242 }
243
244 /**
245 * Address `quotes` property not supported in Safari 4.
246 */
247
248 q:before,
249 q:after {
250 content: '';
251 content: none;
252 }
253
254 /**
255 * Address inconsistent and variable font size in all browsers.
256 */
257
258 small {
259 font-size: 80%;
260 }
261
262 /**
263 * Prevent `sub` and `sup` affecting `line-height` in all browsers.
264 */
265
266 sub,
267 sup {
268 font-size: 75%;
269 line-height: 0;
270 position: relative;
271 vertical-align: baseline;
272 }
273
274 sup {
275 top: -0.5em;
276 }
277
278 sub {
279 bottom: -0.25em;
280 }
281
282 /* ==========================================================================
283 Lists
284 ========================================================================== */
285
286 /**
287 * Address margins set differently in IE 6/7.
288 */
289
290 dl,
291 menu,
292 ol,
293 ul {
294 margin: 1em 0;
295 }
296
297 dd {
298 margin: 0 0 0 40px;
299 }
300
301 /**
302 * Address paddings set differently in IE 6/7.
303 */
304
305 menu,
306 ol,
307 ul {
308 padding: 0 0 0 40px;
309 }
310
311 /**
312 * Correct list images handled incorrectly in IE 7.
313 */
314
315 nav ul,
316 nav ol {
317 list-style: none;
318 list-style-image: none;
319 }
320
321 /* ==========================================================================
322 Embedded content
323 ========================================================================== */
324
325 /**
326 * 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3.
327 * 2. Improve image quality when scaled in IE 7.
328 */
329
330 img {
331 border: 0; /* 1 */
332 -ms-interpolation-mode: bicubic; /* 2 */
333 }
334
335 /**
336 * Correct overflow displayed oddly in IE 9.
337 */
338
339 svg:not(:root) {
340 overflow: hidden;
341 }
342
343 /* ==========================================================================
344 Figures
345 ========================================================================== */
346
347 /**
348 * Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11.
349 */
350
351 figure {
352 margin: 0;
353 }
354
355 /* ==========================================================================
356 Forms
357 ========================================================================== */
358
359 /**
360 * Correct margin displayed oddly in IE 6/7.
361 */
362
363 form {
364 margin: 0;
365 }
366
367 /**
368 * Define consistent border, margin, and padding.
369 */
370
371 fieldset {
372 border: 1px solid #c0c0c0;
373 margin: 0 2px;
374 padding: 0.35em 0.625em 0.75em;
375 }
376
377 /**
378 * 1. Correct color not being inherited in IE 6/7/8/9.
379 * 2. Correct text not wrapping in Firefox 3.
380 * 3. Correct alignment displayed oddly in IE 6/7.
381 */
382
383 legend {
384 border: 0; /* 1 */
385 padding: 0;
386 white-space: normal; /* 2 */
387 *margin-left: -7px; /* 3 */
388 }
389
390 /**
391 * 1. Correct font size not being inherited in all browsers.
392 * 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5,
393 * and Chrome.
394 * 3. Improve appearance and consistency in all browsers.
232 font-family: monospace, monospace;
233 font-size: 1em;
234 }
235
236 /* Forms
237 ========================================================================== */
238
239 /**
240 * Known limitation: by default, Chrome and Safari on OS X allow very limited
241 * styling of `select`, unless a `border` property is set.
242 */
243
244 /**
245 * 1. Correct color not being inherited.
246 * Known issue: affects color of disabled elements.
247 * 2. Correct font properties not being inherited.
248 * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
395249 */
396250
397251 button,
398252 input,
253 optgroup,
399254 select,
400255 textarea {
401 font-size: 100%; /* 1 */
402 margin: 0; /* 2 */
403 vertical-align: baseline; /* 3 */
404 *vertical-align: middle; /* 3 */
405 }
406
407 /**
408 * Address Firefox 3+ setting `line-height` on `input` using `!important` in
409 * the UA stylesheet.
410 */
411
412 button,
413 input {
414 line-height: normal;
256 color: inherit; /* 1 */
257 font: inherit; /* 2 */
258 margin: 0; /* 3 */
259 }
260
261 /**
262 * Address `overflow` set to `hidden` in IE 8/9/10/11.
263 */
264
265 button {
266 overflow: visible;
415267 }
416268
417269 /**
418270 * Address inconsistent `text-transform` inheritance for `button` and `select`.
419271 * All other form control elements do not inherit `text-transform` values.
420 * Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+.
421 * Correct `select` style inheritance in Firefox 4+ and Opera.
272 * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
273 * Correct `select` style inheritance in Firefox.
422274 */
423275
424276 button,
425277 select {
426 text-transform: none;
278 text-transform: none;
427279 }
428280
429281 /**
432284 * 2. Correct inability to style clickable `input` types in iOS.
433285 * 3. Improve usability and consistency of cursor style between image-type
434286 * `input` and others.
435 * 4. Remove inner spacing in IE 7 without affecting normal text inputs.
436 * Known issue: inner spacing remains in IE 6.
437287 */
438288
439289 button,
440290 html input[type="button"], /* 1 */
441291 input[type="reset"],
442292 input[type="submit"] {
443 -webkit-appearance: button; /* 2 */
444 cursor: pointer; /* 3 */
445 *overflow: visible; /* 4 */
293 -webkit-appearance: button; /* 2 */
294 cursor: pointer; /* 3 */
446295 }
447296
448297 /**
451300
452301 button[disabled],
453302 html input[disabled] {
454 cursor: default;
455 }
456
457 /**
458 * 1. Address box sizing set to content-box in IE 8/9.
459 * 2. Remove excess padding in IE 8/9.
460 * 3. Remove excess padding in IE 7.
461 * Known issue: excess padding remains in IE 6.
303 cursor: default;
304 }
305
306 /**
307 * Remove inner padding and border in Firefox 4+.
308 */
309
310 button::-moz-focus-inner,
311 input::-moz-focus-inner {
312 border: 0;
313 padding: 0;
314 }
315
316 /**
317 * Address Firefox 4+ setting `line-height` on `input` using `!important` in
318 * the UA stylesheet.
319 */
320
321 input {
322 line-height: normal;
323 }
324
325 /**
326 * It's recommended that you don't attempt to style these elements.
327 * Firefox's implementation doesn't respect box-sizing, padding, or width.
328 *
329 * 1. Address box sizing set to `content-box` in IE 8/9/10.
330 * 2. Remove excess padding in IE 8/9/10.
462331 */
463332
464333 input[type="checkbox"],
465334 input[type="radio"] {
466 box-sizing: border-box; /* 1 */
467 padding: 0; /* 2 */
468 *height: 13px; /* 3 */
469 *width: 13px; /* 3 */
470 }
471
472 /**
473 * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
474 * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
335 box-sizing: border-box; /* 1 */
336 padding: 0; /* 2 */
337 }
338
339 /**
340 * Fix the cursor style for Chrome's increment/decrement buttons. For certain
341 * `font-size` values of the `input`, it causes the cursor style of the
342 * decrement button to change from `default` to `text`.
343 */
344
345 input[type="number"]::-webkit-inner-spin-button,
346 input[type="number"]::-webkit-outer-spin-button {
347 height: auto;
348 }
349
350 /**
351 * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
352 * 2. Address `box-sizing` set to `border-box` in Safari and Chrome
475353 * (include `-moz` to future-proof).
476354 */
477355
478356 input[type="search"] {
479 -webkit-appearance: textfield; /* 1 */
480 -moz-box-sizing: content-box;
481 -webkit-box-sizing: content-box; /* 2 */
482 box-sizing: content-box;
483 }
484
485 /**
486 * Remove inner padding and search cancel button in Safari 5 and Chrome
487 * on OS X.
357 -webkit-appearance: textfield; /* 1 */
358 -moz-box-sizing: content-box;
359 -webkit-box-sizing: content-box; /* 2 */
360 box-sizing: content-box;
361 }
362
363 /**
364 * Remove inner padding and search cancel button in Safari and Chrome on OS X.
365 * Safari (but not Chrome) clips the cancel button when the search input has
366 * padding (and `textfield` appearance).
488367 */
489368
490369 input[type="search"]::-webkit-search-cancel-button,
491370 input[type="search"]::-webkit-search-decoration {
492 -webkit-appearance: none;
493 }
494
495 /**
496 * Remove inner padding and border in Firefox 3+.
497 */
498
499 button::-moz-focus-inner,
500 input::-moz-focus-inner {
501 border: 0;
502 padding: 0;
503 }
504
505 /**
506 * 1. Remove default vertical scrollbar in IE 6/7/8/9.
507 * 2. Improve readability and alignment in all browsers.
371 -webkit-appearance: none;
372 }
373
374 /**
375 * Define consistent border, margin, and padding.
376 */
377
378 fieldset {
379 border: 1px solid #c0c0c0;
380 margin: 0 2px;
381 padding: 0.35em 0.625em 0.75em;
382 }
383
384 /**
385 * 1. Correct `color` not being inherited in IE 8/9/10/11.
386 * 2. Remove padding so people aren't caught out if they zero out fieldsets.
387 */
388
389 legend {
390 border: 0; /* 1 */
391 padding: 0; /* 2 */
392 }
393
394 /**
395 * Remove default vertical scrollbar in IE 8/9/10/11.
508396 */
509397
510398 textarea {
511 overflow: auto; /* 1 */
512 vertical-align: top; /* 2 */
513 }
514
515 /* ==========================================================================
516 Tables
399 overflow: auto;
400 }
401
402 /**
403 * Don't inherit the `font-weight` (applied by a rule above).
404 * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
405 */
406
407 optgroup {
408 font-weight: bold;
409 }
410
411 /* Tables
517412 ========================================================================== */
518413
519414 /**
521416 */
522417
523418 table {
524 border-collapse: collapse;
525 border-spacing: 0;
526 }
419 border-collapse: collapse;
420 border-spacing: 0;
421 }
422
423 td,
424 th {
425 padding: 0;
426 }
0 /*!
1 * Chart.js
2 * http://chartjs.org/
3 * Version: 1.0.1-beta.3
4 *
5 * Copyright 2014 Nick Downie
6 * Released under the MIT license
7 * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
8 */
9
10
11 (function(){
12
13 "use strict";
14
15 //Declare root variable - window in the browser, global on the server
16 var root = this,
17 previous = root.Chart;
18
19 //Occupy the global variable of Chart, and create a simple base class
20 var Chart = function(context){
21 var chart = this;
22 this.canvas = context.canvas;
23
24 this.ctx = context;
25
26 //Variables global to the chart
27 var width = this.width = context.canvas.width;
28 var height = this.height = context.canvas.height;
29 this.aspectRatio = this.width / this.height;
30 //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
31 helpers.retinaScale(this);
32
33 return this;
34 };
35 //Globally expose the defaults to allow for user updating/changing
36 Chart.defaults = {
37 global: {
38 // Boolean - Whether to animate the chart
39 animation: true,
40
41 // Number - Number of animation steps
42 animationSteps: 60,
43
44 // String - Animation easing effect
45 animationEasing: "easeOutQuart",
46
47 // Boolean - If we should show the scale at all
48 showScale: true,
49
50 // Boolean - If we want to override with a hard coded scale
51 scaleOverride: false,
52
53 // ** Required if scaleOverride is true **
54 // Number - The number of steps in a hard coded scale
55 scaleSteps: null,
56 // Number - The value jump in the hard coded scale
57 scaleStepWidth: null,
58 // Number - The scale starting value
59 scaleStartValue: null,
60
61 // String - Colour of the scale line
62 scaleLineColor: "rgba(0,0,0,.1)",
63
64 // Number - Pixel width of the scale line
65 scaleLineWidth: 1,
66
67 // Boolean - Whether to show labels on the scale
68 scaleShowLabels: true,
69
70 // Boolean or a positive integer denoting number of labels to be shown on x axis
71 showXLabels: true,
72
73 // Interpolated JS string - can access value
74 scaleLabel: "<%=value%>",
75
76 // Boolean - Whether the scale should stick to integers, and not show any floats even if drawing space is there
77 scaleIntegersOnly: true,
78
79 // Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
80 scaleBeginAtZero: false,
81
82 // String - Scale label font declaration for the scale label
83 scaleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
84
85 // Number - Scale label font size in pixels
86 scaleFontSize: 12,
87
88 // String - Scale label font weight style
89 scaleFontStyle: "normal",
90
91 // String - Scale label font colour
92 scaleFontColor: "#666",
93
94 // Boolean - whether or not the chart should be responsive and resize when the browser does.
95 responsive: false,
96
97 // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container
98 maintainAspectRatio: true,
99
100 // Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove
101 showTooltips: true,
102
103 // Array - Array of string names to attach tooltip events
104 tooltipEvents: ["mousemove", "touchstart", "touchmove", "mouseout"],
105
106 // String - Tooltip background colour
107 tooltipFillColor: "rgba(0,0,0,0.8)",
108
109 // String - Tooltip label font declaration for the scale label
110 tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
111
112 // Number - Tooltip label font size in pixels
113 tooltipFontSize: 14,
114
115 // String - Tooltip font weight style
116 tooltipFontStyle: "normal",
117
118 // String - Tooltip label font colour
119 tooltipFontColor: "#fff",
120
121 // String - Tooltip title font declaration for the scale label
122 tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
123
124 // Number - Tooltip title font size in pixels
125 tooltipTitleFontSize: 14,
126
127 // String - Tooltip title font weight style
128 tooltipTitleFontStyle: "bold",
129
130 // String - Tooltip title font colour
131 tooltipTitleFontColor: "#fff",
132
133 // Number - pixel width of padding around tooltip text
134 tooltipYPadding: 6,
135
136 // Number - pixel width of padding around tooltip text
137 tooltipXPadding: 6,
138
139 // Number - Size of the caret on the tooltip
140 tooltipCaretSize: 8,
141
142 // Number - Pixel radius of the tooltip border
143 tooltipCornerRadius: 6,
144
145 // Number - Pixel offset from point x to tooltip edge
146 tooltipXOffset: 10,
147
148 // String - Template string for single tooltips
149 tooltipTemplate: "<%if (label){%><%=label%>: <%}%><%= value %>",
150
151 // String - Template string for single tooltips
152 multiTooltipTemplate: "<%= value %>",
153
154 // String - Colour behind the legend colour block
155 multiTooltipKeyBackground: '#fff',
156
157 // Function - Will fire on animation progression.
158 onAnimationProgress: function(){},
159
160 // Function - Will fire on animation completion.
161 onAnimationComplete: function(){}
162
163 }
164 };
165
166 //Create a dictionary of chart types, to allow for extension of existing types
167 Chart.types = {};
168
169 //Global Chart helpers object for utility methods and classes
170 var helpers = Chart.helpers = {};
171
172 //-- Basic js utility methods
173 var each = helpers.each = function(loopable,callback,self){
174 var additionalArgs = Array.prototype.slice.call(arguments, 3);
175 // Check to see if null or undefined firstly.
176 if (loopable){
177 if (loopable.length === +loopable.length){
178 var i;
179 for (i=0; i<loopable.length; i++){
180 callback.apply(self,[loopable[i], i].concat(additionalArgs));
181 }
182 }
183 else{
184 for (var item in loopable){
185 callback.apply(self,[loopable[item],item].concat(additionalArgs));
186 }
187 }
188 }
189 },
190 clone = helpers.clone = function(obj){
191 var objClone = {};
192 each(obj,function(value,key){
193 if (obj.hasOwnProperty(key)) objClone[key] = value;
194 });
195 return objClone;
196 },
197 extend = helpers.extend = function(base){
198 each(Array.prototype.slice.call(arguments,1), function(extensionObject) {
199 each(extensionObject,function(value,key){
200 if (extensionObject.hasOwnProperty(key)) base[key] = value;
201 });
202 });
203 return base;
204 },
205 merge = helpers.merge = function(base,master){
206 //Merge properties in left object over to a shallow clone of object right.
207 var args = Array.prototype.slice.call(arguments,0);
208 args.unshift({});
209 return extend.apply(null, args);
210 },
211 indexOf = helpers.indexOf = function(arrayToSearch, item){
212 if (Array.prototype.indexOf) {
213 return arrayToSearch.indexOf(item);
214 }
215 else{
216 for (var i = 0; i < arrayToSearch.length; i++) {
217 if (arrayToSearch[i] === item) return i;
218 }
219 return -1;
220 }
221 },
222 inherits = helpers.inherits = function(extensions){
223 //Basic javascript inheritance based on the model created in Backbone.js
224 var parent = this;
225 var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function(){ return parent.apply(this, arguments); };
226
227 var Surrogate = function(){ this.constructor = ChartElement;};
228 Surrogate.prototype = parent.prototype;
229 ChartElement.prototype = new Surrogate();
230
231 ChartElement.extend = inherits;
232
233 if (extensions) extend(ChartElement.prototype, extensions);
234
235 ChartElement.__super__ = parent.prototype;
236
237 return ChartElement;
238 },
239 noop = helpers.noop = function(){},
240 uid = helpers.uid = (function(){
241 var id=0;
242 return function(){
243 return "chart-" + id++;
244 };
245 })(),
246 warn = helpers.warn = function(str){
247 //Method for warning of errors
248 if (window.console && typeof window.console.warn == "function") console.warn(str);
249 },
250 amd = helpers.amd = (typeof root.define == 'function' && root.define.amd),
251 //-- Math methods
252 isNumber = helpers.isNumber = function(n){
253 return !isNaN(parseFloat(n)) && isFinite(n);
254 },
255 max = helpers.max = function(array){
256 return Math.max.apply( Math, array );
257 },
258 min = helpers.min = function(array){
259 return Math.min.apply( Math, array );
260 },
261 cap = helpers.cap = function(valueToCap,maxValue,minValue){
262 if(isNumber(maxValue)) {
263 if( valueToCap > maxValue ) {
264 return maxValue;
265 }
266 }
267 else if(isNumber(minValue)){
268 if ( valueToCap < minValue ){
269 return minValue;
270 }
271 }
272 return valueToCap;
273 },
274 getDecimalPlaces = helpers.getDecimalPlaces = function(num){
275 if (num%1!==0 && isNumber(num)){
276 return num.toString().split(".")[1].length;
277 }
278 else {
279 return 0;
280 }
281 },
282 toRadians = helpers.radians = function(degrees){
283 return degrees * (Math.PI/180);
284 },
285 // Gets the angle from vertical upright to the point about a centre.
286 getAngleFromPoint = helpers.getAngleFromPoint = function(centrePoint, anglePoint){
287 var distanceFromXCenter = anglePoint.x - centrePoint.x,
288 distanceFromYCenter = anglePoint.y - centrePoint.y,
289 radialDistanceFromCenter = Math.sqrt( distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
290
291
292 var angle = Math.PI * 2 + Math.atan2(distanceFromYCenter, distanceFromXCenter);
293
294 //If the segment is in the top left quadrant, we need to add another rotation to the angle
295 if (distanceFromXCenter < 0 && distanceFromYCenter < 0){
296 angle += Math.PI*2;
297 }
298
299 return {
300 angle: angle,
301 distance: radialDistanceFromCenter
302 };
303 },
304 aliasPixel = helpers.aliasPixel = function(pixelWidth){
305 return (pixelWidth % 2 === 0) ? 0 : 0.5;
306 },
307 splineCurve = helpers.splineCurve = function(FirstPoint,MiddlePoint,AfterPoint,t){
308 //Props to Rob Spencer at scaled innovation for his post on splining between points
309 //http://scaledinnovation.com/analytics/splines/aboutSplines.html
310 var d01=Math.sqrt(Math.pow(MiddlePoint.x-FirstPoint.x,2)+Math.pow(MiddlePoint.y-FirstPoint.y,2)),
311 d12=Math.sqrt(Math.pow(AfterPoint.x-MiddlePoint.x,2)+Math.pow(AfterPoint.y-MiddlePoint.y,2)),
312 fa=t*d01/(d01+d12),// scaling factor for triangle Ta
313 fb=t*d12/(d01+d12);
314 return {
315 inner : {
316 x : MiddlePoint.x-fa*(AfterPoint.x-FirstPoint.x),
317 y : MiddlePoint.y-fa*(AfterPoint.y-FirstPoint.y)
318 },
319 outer : {
320 x: MiddlePoint.x+fb*(AfterPoint.x-FirstPoint.x),
321 y : MiddlePoint.y+fb*(AfterPoint.y-FirstPoint.y)
322 }
323 };
324 },
325 calculateOrderOfMagnitude = helpers.calculateOrderOfMagnitude = function(val){
326 return Math.floor(Math.log(val) / Math.LN10);
327 },
328 calculateScaleRange = helpers.calculateScaleRange = function(valuesArray, drawingSize, textSize, startFromZero, integersOnly){
329
330 //Set a minimum step of two - a point at the top of the graph, and a point at the base
331 var minSteps = 2,
332 maxSteps = Math.floor(drawingSize/(textSize * 1.5)),
333 skipFitting = (minSteps >= maxSteps);
334
335 var maxValue = max(valuesArray),
336 minValue = min(valuesArray);
337
338 // We need some degree of seperation here to calculate the scales if all the values are the same
339 // Adding/minusing 0.5 will give us a range of 1.
340 if (maxValue === minValue){
341 maxValue += 0.5;
342 // So we don't end up with a graph with a negative start value if we've said always start from zero
343 if (minValue >= 0.5 && !startFromZero){
344 minValue -= 0.5;
345 }
346 else{
347 // Make up a whole number above the values
348 maxValue += 0.5;
349 }
350 }
351
352 var valueRange = Math.abs(maxValue - minValue),
353 rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange),
354 graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude),
355 graphMin = (startFromZero) ? 0 : Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude),
356 graphRange = graphMax - graphMin,
357 stepValue = Math.pow(10, rangeOrderOfMagnitude),
358 numberOfSteps = Math.round(graphRange / stepValue);
359
360 //If we have more space on the graph we'll use it to give more definition to the data
361 while((numberOfSteps > maxSteps || (numberOfSteps * 2) < maxSteps) && !skipFitting) {
362 if(numberOfSteps > maxSteps){
363 stepValue *=2;
364 numberOfSteps = Math.round(graphRange/stepValue);
365 // Don't ever deal with a decimal number of steps - cancel fitting and just use the minimum number of steps.
366 if (numberOfSteps % 1 !== 0){
367 skipFitting = true;
368 }
369 }
370 //We can fit in double the amount of scale points on the scale
371 else{
372 //If user has declared ints only, and the step value isn't a decimal
373 if (integersOnly && rangeOrderOfMagnitude >= 0){
374 //If the user has said integers only, we need to check that making the scale more granular wouldn't make it a float
375 if(stepValue/2 % 1 === 0){
376 stepValue /=2;
377 numberOfSteps = Math.round(graphRange/stepValue);
378 }
379 //If it would make it a float break out of the loop
380 else{
381 break;
382 }
383 }
384 //If the scale doesn't have to be an int, make the scale more granular anyway.
385 else{
386 stepValue /=2;
387 numberOfSteps = Math.round(graphRange/stepValue);
388 }
389
390 }
391 }
392
393 if (skipFitting){
394 numberOfSteps = minSteps;
395 stepValue = graphRange / numberOfSteps;
396 }
397
398 return {
399 steps : numberOfSteps,
400 stepValue : stepValue,
401 min : graphMin,
402 max : graphMin + (numberOfSteps * stepValue)
403 };
404
405 },
406 /* jshint ignore:start */
407 // Blows up jshint errors based on the new Function constructor
408 //Templating methods
409 //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/
410 template = helpers.template = function(templateString, valuesObject){
411 // If templateString is function rather than string-template - call the function for valuesObject
412 if(templateString instanceof Function)
413 {
414 return templateString(valuesObject);
415 }
416
417 var cache = {};
418 function tmpl(str, data){
419 // Figure out if we're getting a template, or if we need to
420 // load the template - and be sure to cache the result.
421 var fn = !/\W/.test(str) ?
422 cache[str] = cache[str] :
423
424 // Generate a reusable function that will serve as a template
425 // generator (and which will be cached).
426 new Function("obj",
427 "var p=[],print=function(){p.push.apply(p,arguments);};" +
428
429 // Introduce the data as local variables using with(){}
430 "with(obj){p.push('" +
431
432 // Convert the template into pure JavaScript
433 str
434 .replace(/[\r\t\n]/g, " ")
435 .split("<%").join("\t")
436 .replace(/((^|%>)[^\t]*)'/g, "$1\r")
437 .replace(/\t=(.*?)%>/g, "',$1,'")
438 .split("\t").join("');")
439 .split("%>").join("p.push('")
440 .split("\r").join("\\'") +
441 "');}return p.join('');"
442 );
443
444 // Provide some basic currying to the user
445 return data ? fn( data ) : fn;
446 }
447 return tmpl(templateString,valuesObject);
448 },
449 /* jshint ignore:end */
450 generateLabels = helpers.generateLabels = function(templateString,numberOfSteps,graphMin,stepValue){
451 var labelsArray = new Array(numberOfSteps);
452 if (labelTemplateString){
453 each(labelsArray,function(val,index){
454 labelsArray[index] = template(templateString,{value: (graphMin + (stepValue*(index+1)))});
455 });
456 }
457 return labelsArray;
458 },
459 //--Animation methods
460 //Easing functions adapted from Robert Penner's easing equations
461 //http://www.robertpenner.com/easing/
462 easingEffects = helpers.easingEffects = {
463 linear: function (t) {
464 return t;
465 },
466 easeInQuad: function (t) {
467 return t * t;
468 },
469 easeOutQuad: function (t) {
470 return -1 * t * (t - 2);
471 },
472 easeInOutQuad: function (t) {
473 if ((t /= 1 / 2) < 1) return 1 / 2 * t * t;
474 return -1 / 2 * ((--t) * (t - 2) - 1);
475 },
476 easeInCubic: function (t) {
477 return t * t * t;
478 },
479 easeOutCubic: function (t) {
480 return 1 * ((t = t / 1 - 1) * t * t + 1);
481 },
482 easeInOutCubic: function (t) {
483 if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t;
484 return 1 / 2 * ((t -= 2) * t * t + 2);
485 },
486 easeInQuart: function (t) {
487 return t * t * t * t;
488 },
489 easeOutQuart: function (t) {
490 return -1 * ((t = t / 1 - 1) * t * t * t - 1);
491 },
492 easeInOutQuart: function (t) {
493 if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t;
494 return -1 / 2 * ((t -= 2) * t * t * t - 2);
495 },
496 easeInQuint: function (t) {
497 return 1 * (t /= 1) * t * t * t * t;
498 },
499 easeOutQuint: function (t) {
500 return 1 * ((t = t / 1 - 1) * t * t * t * t + 1);
501 },
502 easeInOutQuint: function (t) {
503 if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t * t;
504 return 1 / 2 * ((t -= 2) * t * t * t * t + 2);
505 },
506 easeInSine: function (t) {
507 return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1;
508 },
509 easeOutSine: function (t) {
510 return 1 * Math.sin(t / 1 * (Math.PI / 2));
511 },
512 easeInOutSine: function (t) {
513 return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1);
514 },
515 easeInExpo: function (t) {
516 return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1));
517 },
518 easeOutExpo: function (t) {
519 return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1);
520 },
521 easeInOutExpo: function (t) {
522 if (t === 0) return 0;
523 if (t === 1) return 1;
524 if ((t /= 1 / 2) < 1) return 1 / 2 * Math.pow(2, 10 * (t - 1));
525 return 1 / 2 * (-Math.pow(2, -10 * --t) + 2);
526 },
527 easeInCirc: function (t) {
528 if (t >= 1) return t;
529 return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1);
530 },
531 easeOutCirc: function (t) {
532 return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t);
533 },
534 easeInOutCirc: function (t) {
535 if ((t /= 1 / 2) < 1) return -1 / 2 * (Math.sqrt(1 - t * t) - 1);
536 return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1);
537 },
538 easeInElastic: function (t) {
539 var s = 1.70158;
540 var p = 0;
541 var a = 1;
542 if (t === 0) return 0;
543 if ((t /= 1) == 1) return 1;
544 if (!p) p = 1 * 0.3;
545 if (a < Math.abs(1)) {
546 a = 1;
547 s = p / 4;
548 } else s = p / (2 * Math.PI) * Math.asin(1 / a);
549 return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
550 },
551 easeOutElastic: function (t) {
552 var s = 1.70158;
553 var p = 0;
554 var a = 1;
555 if (t === 0) return 0;
556 if ((t /= 1) == 1) return 1;
557 if (!p) p = 1 * 0.3;
558 if (a < Math.abs(1)) {
559 a = 1;
560 s = p / 4;
561 } else s = p / (2 * Math.PI) * Math.asin(1 / a);
562 return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
563 },
564 easeInOutElastic: function (t) {
565 var s = 1.70158;
566 var p = 0;
567 var a = 1;
568 if (t === 0) return 0;
569 if ((t /= 1 / 2) == 2) return 1;
570 if (!p) p = 1 * (0.3 * 1.5);
571 if (a < Math.abs(1)) {
572 a = 1;
573 s = p / 4;
574 } else s = p / (2 * Math.PI) * Math.asin(1 / a);
575 if (t < 1) return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
576 return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1;
577 },
578 easeInBack: function (t) {
579 var s = 1.70158;
580 return 1 * (t /= 1) * t * ((s + 1) * t - s);
581 },
582 easeOutBack: function (t) {
583 var s = 1.70158;
584 return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1);
585 },
586 easeInOutBack: function (t) {
587 var s = 1.70158;
588 if ((t /= 1 / 2) < 1) return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s));
589 return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
590 },
591 easeInBounce: function (t) {
592 return 1 - easingEffects.easeOutBounce(1 - t);
593 },
594 easeOutBounce: function (t) {
595 if ((t /= 1) < (1 / 2.75)) {
596 return 1 * (7.5625 * t * t);
597 } else if (t < (2 / 2.75)) {
598 return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75);
599 } else if (t < (2.5 / 2.75)) {
600 return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375);
601 } else {
602 return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);
603 }
604 },
605 easeInOutBounce: function (t) {
606 if (t < 1 / 2) return easingEffects.easeInBounce(t * 2) * 0.5;
607 return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5;
608 }
609 },
610 //Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
611 requestAnimFrame = helpers.requestAnimFrame = (function(){
612 return window.requestAnimationFrame ||
613 window.webkitRequestAnimationFrame ||
614 window.mozRequestAnimationFrame ||
615 window.oRequestAnimationFrame ||
616 window.msRequestAnimationFrame ||
617 function(callback) {
618 return window.setTimeout(callback, 1000 / 60);
619 };
620 })(),
621 cancelAnimFrame = helpers.cancelAnimFrame = (function(){
622 return window.cancelAnimationFrame ||
623 window.webkitCancelAnimationFrame ||
624 window.mozCancelAnimationFrame ||
625 window.oCancelAnimationFrame ||
626 window.msCancelAnimationFrame ||
627 function(callback) {
628 return window.clearTimeout(callback, 1000 / 60);
629 };
630 })(),
631 animationLoop = helpers.animationLoop = function(callback,totalSteps,easingString,onProgress,onComplete,chartInstance){
632
633 var currentStep = 0,
634 easingFunction = easingEffects[easingString] || easingEffects.linear;
635
636 var animationFrame = function(){
637 currentStep++;
638 var stepDecimal = currentStep/totalSteps;
639 var easeDecimal = easingFunction(stepDecimal);
640
641 callback.call(chartInstance,easeDecimal,stepDecimal, currentStep);
642 onProgress.call(chartInstance,easeDecimal,stepDecimal);
643 if (currentStep < totalSteps){
644 chartInstance.animationFrame = requestAnimFrame(animationFrame);
645 } else{
646 onComplete.apply(chartInstance);
647 }
648 };
649 requestAnimFrame(animationFrame);
650 },
651 //-- DOM methods
652 getRelativePosition = helpers.getRelativePosition = function(evt){
653 var mouseX, mouseY;
654 var e = evt.originalEvent || evt,
655 canvas = evt.currentTarget || evt.srcElement,
656 boundingRect = canvas.getBoundingClientRect();
657
658 if (e.touches){
659 mouseX = e.touches[0].clientX - boundingRect.left;
660 mouseY = e.touches[0].clientY - boundingRect.top;
661
662 }
663 else{
664 mouseX = e.clientX - boundingRect.left;
665 mouseY = e.clientY - boundingRect.top;
666 }
667
668 return {
669 x : mouseX,
670 y : mouseY
671 };
672
673 },
674 addEvent = helpers.addEvent = function(node,eventType,method){
675 if (node.addEventListener){
676 node.addEventListener(eventType,method);
677 } else if (node.attachEvent){
678 node.attachEvent("on"+eventType, method);
679 } else {
680 node["on"+eventType] = method;
681 }
682 },
683 removeEvent = helpers.removeEvent = function(node, eventType, handler){
684 if (node.removeEventListener){
685 node.removeEventListener(eventType, handler, false);
686 } else if (node.detachEvent){
687 node.detachEvent("on"+eventType,handler);
688 } else{
689 node["on" + eventType] = noop;
690 }
691 },
692 bindEvents = helpers.bindEvents = function(chartInstance, arrayOfEvents, handler){
693 // Create the events object if it's not already present
694 if (!chartInstance.events) chartInstance.events = {};
695
696 each(arrayOfEvents,function(eventName){
697 chartInstance.events[eventName] = function(){
698 handler.apply(chartInstance, arguments);
699 };
700 addEvent(chartInstance.chart.canvas,eventName,chartInstance.events[eventName]);
701 });
702 },
703 unbindEvents = helpers.unbindEvents = function (chartInstance, arrayOfEvents) {
704 each(arrayOfEvents, function(handler,eventName){
705 removeEvent(chartInstance.chart.canvas, eventName, handler);
706 });
707 },
708 getMaximumWidth = helpers.getMaximumWidth = function(domNode){
709 var container = domNode.parentNode;
710 // TODO = check cross browser stuff with this.
711 return container.clientWidth;
712 },
713 getMaximumHeight = helpers.getMaximumHeight = function(domNode){
714 var container = domNode.parentNode;
715 // TODO = check cross browser stuff with this.
716 return container.clientHeight;
717 },
718 getMaximumSize = helpers.getMaximumSize = helpers.getMaximumWidth, // legacy support
719 retinaScale = helpers.retinaScale = function(chart){
720 var ctx = chart.ctx,
721 width = chart.canvas.width,
722 height = chart.canvas.height;
723 //console.log(width + " x " + height);
724 if (window.devicePixelRatio) {
725 ctx.canvas.style.width = width + "px";
726 ctx.canvas.style.height = height + "px";
727 ctx.canvas.height = height * window.devicePixelRatio;
728 ctx.canvas.width = width * window.devicePixelRatio;
729 ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
730 }
731 },
732 //-- Canvas methods
733 clear = helpers.clear = function(chart){
734 chart.ctx.clearRect(0,0,chart.width,chart.height);
735 },
736 fontString = helpers.fontString = function(pixelSize,fontStyle,fontFamily){
737 return fontStyle + " " + pixelSize+"px " + fontFamily;
738 },
739 longestText = helpers.longestText = function(ctx,font,arrayOfStrings){
740 ctx.font = font;
741 var longest = 0;
742 each(arrayOfStrings,function(string){
743 var textWidth = ctx.measureText(string).width;
744 longest = (textWidth > longest) ? textWidth : longest;
745 });
746 return longest;
747 },
748 drawRoundedRectangle = helpers.drawRoundedRectangle = function(ctx,x,y,width,height,radius){
749 ctx.beginPath();
750 ctx.moveTo(x + radius, y);
751 ctx.lineTo(x + width - radius, y);
752 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
753 ctx.lineTo(x + width, y + height - radius);
754 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
755 ctx.lineTo(x + radius, y + height);
756 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
757 ctx.lineTo(x, y + radius);
758 ctx.quadraticCurveTo(x, y, x + radius, y);
759 ctx.closePath();
760 };
761
762
763 //Store a reference to each instance - allowing us to globally resize chart instances on window resize.
764 //Destroy method on the chart will remove the instance of the chart from this reference.
765 Chart.instances = {};
766
767 Chart.Type = function(data,options,chart){
768 this.options = options;
769 this.chart = chart;
770 this.id = uid();
771 //Add the chart instance to the global namespace
772 Chart.instances[this.id] = this;
773
774 // Initialize is always called when a chart type is created
775 // By default it is a no op, but it should be extended
776 if (options.responsive){
777 this.resize();
778 }
779 this.initialize.call(this,data);
780 };
781
782 //Core methods that'll be a part of every chart type
783 extend(Chart.Type.prototype,{
784 initialize : function(){return this;},
785 clear : function(){
786 clear(this.chart);
787 return this;
788 },
789 stop : function(){
790 // Stops any current animation loop occuring
791 helpers.cancelAnimFrame.call(root, this.animationFrame);
792 return this;
793 },
794 resize : function(callback){
795 this.stop();
796 var canvas = this.chart.canvas,
797 newWidth = getMaximumWidth(this.chart.canvas),
798 newHeight = this.options.maintainAspectRatio ? newWidth / this.chart.aspectRatio : getMaximumHeight(this.chart.canvas);
799
800 canvas.width = this.chart.width = newWidth;
801 canvas.height = this.chart.height = newHeight;
802
803 retinaScale(this.chart);
804
805 if (typeof callback === "function"){
806 callback.apply(this, Array.prototype.slice.call(arguments, 1));
807 }
808 return this;
809 },
810 reflow : noop,
811 render : function(reflow){
812 if (reflow){
813 this.reflow();
814 }
815 if (this.options.animation && !reflow){
816 helpers.animationLoop(
817 this.draw,
818 this.options.animationSteps,
819 this.options.animationEasing,
820 this.options.onAnimationProgress,
821 this.options.onAnimationComplete,
822 this
823 );
824 }
825 else{
826 this.draw();
827 this.options.onAnimationComplete.call(this);
828 }
829 return this;
830 },
831 generateLegend : function(){
832 return template(this.options.legendTemplate,this);
833 },
834 destroy : function(){
835 this.clear();
836 unbindEvents(this, this.events);
837 delete Chart.instances[this.id];
838 },
839 showTooltip : function(ChartElements, forceRedraw){
840 // Only redraw the chart if we've actually changed what we're hovering on.
841 if (typeof this.activeElements === 'undefined') this.activeElements = [];
842
843 var isChanged = (function(Elements){
844 var changed = false;
845
846 if (Elements.length !== this.activeElements.length){
847 changed = true;
848 return changed;
849 }
850
851 each(Elements, function(element, index){
852 if (element !== this.activeElements[index]){
853 changed = true;
854 }
855 }, this);
856 return changed;
857 }).call(this, ChartElements);
858
859 if (!isChanged && !forceRedraw){
860 return;
861 }
862 else{
863 this.activeElements = ChartElements;
864 }
865 this.draw();
866 if (ChartElements.length > 0){
867 // If we have multiple datasets, show a MultiTooltip for all of the data points at that index
868 if (this.datasets && this.datasets.length > 1) {
869 var dataArray,
870 dataIndex;
871
872 for (var i = this.datasets.length - 1; i >= 0; i--) {
873 dataArray = this.datasets[i].points || this.datasets[i].bars || this.datasets[i].segments;
874 dataIndex = indexOf(dataArray, ChartElements[0]);
875 if (dataIndex !== -1){
876 break;
877 }
878 }
879 var tooltipLabels = [],
880 tooltipColors = [],
881 medianPosition = (function(index) {
882
883 // Get all the points at that particular index
884 var Elements = [],
885 dataCollection,
886 xPositions = [],
887 yPositions = [],
888 xMax,
889 yMax,
890 xMin,
891 yMin;
892 helpers.each(this.datasets, function(dataset){
893 dataCollection = dataset.points || dataset.bars || dataset.segments;
894 if (dataCollection[dataIndex]){
895 Elements.push(dataCollection[dataIndex]);
896 }
897 });
898
899 helpers.each(Elements, function(element) {
900 xPositions.push(element.x);
901 yPositions.push(element.y);
902
903
904 //Include any colour information about the element
905 tooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, element));
906 tooltipColors.push({
907 fill: element._saved.fillColor || element.fillColor,
908 stroke: element._saved.strokeColor || element.strokeColor
909 });
910
911 }, this);
912
913 yMin = min(yPositions);
914 yMax = max(yPositions);
915
916 xMin = min(xPositions);
917 xMax = max(xPositions);
918
919 return {
920 x: (xMin > this.chart.width/2) ? xMin : xMax,
921 y: (yMin + yMax)/2
922 };
923 }).call(this, dataIndex);
924
925 new Chart.MultiTooltip({
926 x: medianPosition.x,
927 y: medianPosition.y,
928 xPadding: this.options.tooltipXPadding,
929 yPadding: this.options.tooltipYPadding,
930 xOffset: this.options.tooltipXOffset,
931 fillColor: this.options.tooltipFillColor,
932 textColor: this.options.tooltipFontColor,
933 fontFamily: this.options.tooltipFontFamily,
934 fontStyle: this.options.tooltipFontStyle,
935 fontSize: this.options.tooltipFontSize,
936 titleTextColor: this.options.tooltipTitleFontColor,
937 titleFontFamily: this.options.tooltipTitleFontFamily,
938 titleFontStyle: this.options.tooltipTitleFontStyle,
939 titleFontSize: this.options.tooltipTitleFontSize,
940 cornerRadius: this.options.tooltipCornerRadius,
941 labels: tooltipLabels,
942 legendColors: tooltipColors,
943 legendColorBackground : this.options.multiTooltipKeyBackground,
944 title: ChartElements[0].label,
945 chart: this.chart,
946 ctx: this.chart.ctx
947 }).draw();
948
949 } else {
950 each(ChartElements, function(Element) {
951 var tooltipPosition = Element.tooltipPosition();
952 new Chart.Tooltip({
953 x: Math.round(tooltipPosition.x),
954 y: Math.round(tooltipPosition.y),
955 xPadding: this.options.tooltipXPadding,
956 yPadding: this.options.tooltipYPadding,
957 fillColor: this.options.tooltipFillColor,
958 textColor: this.options.tooltipFontColor,
959 fontFamily: this.options.tooltipFontFamily,
960 fontStyle: this.options.tooltipFontStyle,
961 fontSize: this.options.tooltipFontSize,
962 caretHeight: this.options.tooltipCaretSize,
963 cornerRadius: this.options.tooltipCornerRadius,
964 text: template(this.options.tooltipTemplate, Element),
965 chart: this.chart
966 }).draw();
967 }, this);
968 }
969 }
970 return this;
971 },
972 toBase64Image : function(){
973 return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments);
974 }
975 });
976
977 Chart.Type.extend = function(extensions){
978
979 var parent = this;
980
981 var ChartType = function(){
982 return parent.apply(this,arguments);
983 };
984
985 //Copy the prototype object of the this class
986 ChartType.prototype = clone(parent.prototype);
987 //Now overwrite some of the properties in the base class with the new extensions
988 extend(ChartType.prototype, extensions);
989
990 ChartType.extend = Chart.Type.extend;
991
992 if (extensions.name || parent.prototype.name){
993
994 var chartName = extensions.name || parent.prototype.name;
995 //Assign any potential default values of the new chart type
996
997 //If none are defined, we'll use a clone of the chart type this is being extended from.
998 //I.e. if we extend a line chart, we'll use the defaults from the line chart if our new chart
999 //doesn't define some defaults of their own.
1000
1001 var baseDefaults = (Chart.defaults[parent.prototype.name]) ? clone(Chart.defaults[parent.prototype.name]) : {};
1002
1003 Chart.defaults[chartName] = extend(baseDefaults,extensions.defaults);
1004
1005 Chart.types[chartName] = ChartType;
1006
1007 //Register this new chart type in the Chart prototype
1008 Chart.prototype[chartName] = function(data,options){
1009 var config = merge(Chart.defaults.global, Chart.defaults[chartName], options || {});
1010 return new ChartType(data,config,this);
1011 };
1012 } else{
1013 warn("Name not provided for this chart, so it hasn't been registered");
1014 }
1015 return parent;
1016 };
1017
1018 Chart.Element = function(configuration){
1019 extend(this,configuration);
1020 this.initialize.apply(this,arguments);
1021 this.save();
1022 };
1023 extend(Chart.Element.prototype,{
1024 initialize : function(){},
1025 restore : function(props){
1026 if (!props){
1027 extend(this,this._saved);
1028 } else {
1029 each(props,function(key){
1030 this[key] = this._saved[key];
1031 },this);
1032 }
1033 return this;
1034 },
1035 save : function(){
1036 this._saved = clone(this);
1037 delete this._saved._saved;
1038 return this;
1039 },
1040 update : function(newProps){
1041 each(newProps,function(value,key){
1042 this._saved[key] = this[key];
1043 this[key] = value;
1044 },this);
1045 return this;
1046 },
1047 transition : function(props,ease){
1048 each(props,function(value,key){
1049 this[key] = ((value - this._saved[key]) * ease) + this._saved[key];
1050 },this);
1051 return this;
1052 },
1053 tooltipPosition : function(){
1054 return {
1055 x : this.x,
1056 y : this.y
1057 };
1058 }
1059 });
1060
1061 Chart.Element.extend = inherits;
1062
1063
1064 Chart.Point = Chart.Element.extend({
1065 display: true,
1066 inRange: function(chartX,chartY){
1067 var hitDetectionRange = this.hitDetectionRadius + this.radius;
1068 return ((Math.pow(chartX-this.x, 2)+Math.pow(chartY-this.y, 2)) < Math.pow(hitDetectionRange,2));
1069 },
1070 draw : function(){
1071 if (this.display){
1072 var ctx = this.ctx;
1073 ctx.beginPath();
1074
1075 ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2);
1076 ctx.closePath();
1077
1078 ctx.strokeStyle = this.strokeColor;
1079 ctx.lineWidth = this.strokeWidth;
1080
1081 ctx.fillStyle = this.fillColor;
1082
1083 ctx.fill();
1084 ctx.stroke();
1085 }
1086
1087
1088 //Quick debug for bezier curve splining
1089 //Highlights control points and the line between them.
1090 //Handy for dev - stripped in the min version.
1091
1092 // ctx.save();
1093 // ctx.fillStyle = "black";
1094 // ctx.strokeStyle = "black"
1095 // ctx.beginPath();
1096 // ctx.arc(this.controlPoints.inner.x,this.controlPoints.inner.y, 2, 0, Math.PI*2);
1097 // ctx.fill();
1098
1099 // ctx.beginPath();
1100 // ctx.arc(this.controlPoints.outer.x,this.controlPoints.outer.y, 2, 0, Math.PI*2);
1101 // ctx.fill();
1102
1103 // ctx.moveTo(this.controlPoints.inner.x,this.controlPoints.inner.y);
1104 // ctx.lineTo(this.controlPoints.outer.x,this.controlPoints.outer.y);
1105 // ctx.stroke();
1106
1107 // ctx.restore();
1108
1109
1110
1111 }
1112 });
1113
1114 Chart.Arc = Chart.Element.extend({
1115 inRange : function(chartX,chartY){
1116
1117 var pointRelativePosition = helpers.getAngleFromPoint(this, {
1118 x: chartX,
1119 y: chartY
1120 });
1121
1122 //Check if within the range of the open/close angle
1123 var betweenAngles = (pointRelativePosition.angle >= this.startAngle && pointRelativePosition.angle <= this.endAngle),
1124 withinRadius = (pointRelativePosition.distance >= this.innerRadius && pointRelativePosition.distance <= this.outerRadius);
1125
1126 return (betweenAngles && withinRadius);
1127 //Ensure within the outside of the arc centre, but inside arc outer
1128 },
1129 tooltipPosition : function(){
1130 var centreAngle = this.startAngle + ((this.endAngle - this.startAngle) / 2),
1131 rangeFromCentre = (this.outerRadius - this.innerRadius) / 2 + this.innerRadius;
1132 return {
1133 x : this.x + (Math.cos(centreAngle) * rangeFromCentre),
1134 y : this.y + (Math.sin(centreAngle) * rangeFromCentre)
1135 };
1136 },
1137 draw : function(animationPercent){
1138
1139 var easingDecimal = animationPercent || 1;
1140
1141 var ctx = this.ctx;
1142
1143 ctx.beginPath();
1144
1145 ctx.arc(this.x, this.y, this.outerRadius, this.startAngle, this.endAngle);
1146
1147 ctx.arc(this.x, this.y, this.innerRadius, this.endAngle, this.startAngle, true);
1148
1149 ctx.closePath();
1150 ctx.strokeStyle = this.strokeColor;
1151 ctx.lineWidth = this.strokeWidth;
1152
1153 ctx.fillStyle = this.fillColor;
1154
1155 ctx.fill();
1156 ctx.lineJoin = 'bevel';
1157
1158 if (this.showStroke){
1159 ctx.stroke();
1160 }
1161 }
1162 });
1163
1164 Chart.Rectangle = Chart.Element.extend({
1165 draw : function(){
1166 var ctx = this.ctx,
1167 halfWidth = this.width/2,
1168 leftX = this.x - halfWidth,
1169 rightX = this.x + halfWidth,
1170 top = this.base - (this.base - this.y),
1171 halfStroke = this.strokeWidth / 2;
1172
1173 // Canvas doesn't allow us to stroke inside the width so we can
1174 // adjust the sizes to fit if we're setting a stroke on the line
1175 if (this.showStroke){
1176 leftX += halfStroke;
1177 rightX -= halfStroke;
1178 top += halfStroke;
1179 }
1180
1181 ctx.beginPath();
1182
1183 ctx.fillStyle = this.fillColor;
1184 ctx.strokeStyle = this.strokeColor;
1185 ctx.lineWidth = this.strokeWidth;
1186
1187 // It'd be nice to keep this class totally generic to any rectangle
1188 // and simply specify which border to miss out.
1189 ctx.moveTo(leftX, this.base);
1190 ctx.lineTo(leftX, top);
1191 ctx.lineTo(rightX, top);
1192 ctx.lineTo(rightX, this.base);
1193 ctx.fill();
1194 if (this.showStroke){
1195 ctx.stroke();
1196 }
1197 },
1198 height : function(){
1199 return this.base - this.y;
1200 },
1201 inRange : function(chartX,chartY){
1202 return (chartX >= this.x - this.width/2 && chartX <= this.x + this.width/2) && (chartY >= this.y && chartY <= this.base);
1203 }
1204 });
1205
1206 Chart.Tooltip = Chart.Element.extend({
1207 draw : function(){
1208
1209 var ctx = this.chart.ctx;
1210
1211 ctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);
1212
1213 this.xAlign = "center";
1214 this.yAlign = "above";
1215
1216 //Distance between the actual element.y position and the start of the tooltip caret
1217 var caretPadding = 2;
1218
1219 var tooltipWidth = ctx.measureText(this.text).width + 2*this.xPadding,
1220 tooltipRectHeight = this.fontSize + 2*this.yPadding,
1221 tooltipHeight = tooltipRectHeight + this.caretHeight + caretPadding;
1222
1223 if (this.x + tooltipWidth/2 >this.chart.width){
1224 this.xAlign = "left";
1225 } else if (this.x - tooltipWidth/2 < 0){
1226 this.xAlign = "right";
1227 }
1228
1229 if (this.y - tooltipHeight < 0){
1230 this.yAlign = "below";
1231 }
1232
1233
1234 var tooltipX = this.x - tooltipWidth/2,
1235 tooltipY = this.y - tooltipHeight;
1236
1237 ctx.fillStyle = this.fillColor;
1238
1239 switch(this.yAlign)
1240 {
1241 case "above":
1242 //Draw a caret above the x/y
1243 ctx.beginPath();
1244 ctx.moveTo(this.x,this.y - caretPadding);
1245 ctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight));
1246 ctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight));
1247 ctx.closePath();
1248 ctx.fill();
1249 break;
1250 case "below":
1251 tooltipY = this.y + caretPadding + this.caretHeight;
1252 //Draw a caret below the x/y
1253 ctx.beginPath();
1254 ctx.moveTo(this.x, this.y + caretPadding);
1255 ctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight);
1256 ctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight);
1257 ctx.closePath();
1258 ctx.fill();
1259 break;
1260 }
1261
1262 switch(this.xAlign)
1263 {
1264 case "left":
1265 tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight);
1266 break;
1267 case "right":
1268 tooltipX = this.x - (this.cornerRadius + this.caretHeight);
1269 break;
1270 }
1271
1272 drawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius);
1273
1274 ctx.fill();
1275
1276 ctx.fillStyle = this.textColor;
1277 ctx.textAlign = "center";
1278 ctx.textBaseline = "middle";
1279 ctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2);
1280 }
1281 });
1282
1283 Chart.MultiTooltip = Chart.Element.extend({
1284 initialize : function(){
1285 this.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);
1286
1287 this.titleFont = fontString(this.titleFontSize,this.titleFontStyle,this.titleFontFamily);
1288
1289 this.height = (this.labels.length * this.fontSize) + ((this.labels.length-1) * (this.fontSize/2)) + (this.yPadding*2) + this.titleFontSize *1.5;
1290
1291 this.ctx.font = this.titleFont;
1292
1293 var titleWidth = this.ctx.measureText(this.title).width,
1294 //Label has a legend square as well so account for this.
1295 labelWidth = longestText(this.ctx,this.font,this.labels) + this.fontSize + 3,
1296 longestTextWidth = max([labelWidth,titleWidth]);
1297
1298 this.width = longestTextWidth + (this.xPadding*2);
1299
1300
1301 var halfHeight = this.height/2;
1302
1303 //Check to ensure the height will fit on the canvas
1304 //The three is to buffer form the very
1305 if (this.y - halfHeight < 0 ){
1306 this.y = halfHeight;
1307 } else if (this.y + halfHeight > this.chart.height){
1308 this.y = this.chart.height - halfHeight;
1309 }
1310
1311 //Decide whether to align left or right based on position on canvas
1312 if (this.x > this.chart.width/2){
1313 this.x -= this.xOffset + this.width;
1314 } else {
1315 this.x += this.xOffset;
1316 }
1317
1318
1319 },
1320 getLineHeight : function(index){
1321 var baseLineHeight = this.y - (this.height/2) + this.yPadding,
1322 afterTitleIndex = index-1;
1323
1324 //If the index is zero, we're getting the title
1325 if (index === 0){
1326 return baseLineHeight + this.titleFontSize/2;
1327 } else{
1328 return baseLineHeight + ((this.fontSize*1.5*afterTitleIndex) + this.fontSize/2) + this.titleFontSize * 1.5;
1329 }
1330
1331 },
1332 draw : function(){
1333 drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius);
1334 var ctx = this.ctx;
1335 ctx.fillStyle = this.fillColor;
1336 ctx.fill();
1337 ctx.closePath();
1338
1339 ctx.textAlign = "left";
1340 ctx.textBaseline = "middle";
1341 ctx.fillStyle = this.titleTextColor;
1342 ctx.font = this.titleFont;
1343
1344 ctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0));
1345
1346 ctx.font = this.font;
1347 helpers.each(this.labels,function(label,index){
1348 ctx.fillStyle = this.textColor;
1349 ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1));
1350
1351 //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas)
1352 //ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
1353 //Instead we'll make a white filled block to put the legendColour palette over.
1354
1355 ctx.fillStyle = this.legendColorBackground;
1356 ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
1357
1358 ctx.fillStyle = this.legendColors[index].fill;
1359 ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
1360
1361
1362 },this);
1363 }
1364 });
1365
1366 Chart.Scale = Chart.Element.extend({
1367 initialize : function(){
1368 this.fit();
1369 },
1370 buildYLabels : function(){
1371 this.yLabels = [];
1372
1373 var stepDecimalPlaces = getDecimalPlaces(this.stepValue);
1374
1375 for (var i=0; i<=this.steps; i++){
1376 this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)}));
1377 }
1378 this.yLabelWidth = (this.display && this.showLabels) ? longestText(this.ctx,this.font,this.yLabels) : 0;
1379 },
1380 addXLabel : function(label){
1381 this.xLabels.push(label);
1382 this.valuesCount++;
1383 this.fit();
1384 },
1385 removeXLabel : function(){
1386 this.xLabels.shift();
1387 this.valuesCount--;
1388 this.fit();
1389 },
1390 // Fitting loop to rotate x Labels and figure out what fits there, and also calculate how many Y steps to use
1391 fit: function(){
1392 // First we need the width of the yLabels, assuming the xLabels aren't rotated
1393
1394 // To do that we need the base line at the top and base of the chart, assuming there is no x label rotation
1395 this.startPoint = (this.display) ? this.fontSize : 0;
1396 this.endPoint = (this.display) ? this.height - (this.fontSize * 1.5) - 5 : this.height; // -5 to pad labels
1397
1398 // Apply padding settings to the start and end point.
1399 this.startPoint += this.padding;
1400 this.endPoint -= this.padding;
1401
1402 // Cache the starting height, so can determine if we need to recalculate the scale yAxis
1403 var cachedHeight = this.endPoint - this.startPoint,
1404 cachedYLabelWidth;
1405
1406 // Build the current yLabels so we have an idea of what size they'll be to start
1407 /*
1408 * This sets what is returned from calculateScaleRange as static properties of this class:
1409 *
1410 this.steps;
1411 this.stepValue;
1412 this.min;
1413 this.max;
1414 *
1415 */
1416 this.calculateYRange(cachedHeight);
1417
1418 // With these properties set we can now build the array of yLabels
1419 // and also the width of the largest yLabel
1420 this.buildYLabels();
1421
1422 this.calculateXLabelRotation();
1423
1424 while((cachedHeight > this.endPoint - this.startPoint)){
1425 cachedHeight = this.endPoint - this.startPoint;
1426 cachedYLabelWidth = this.yLabelWidth;
1427
1428 this.calculateYRange(cachedHeight);
1429 this.buildYLabels();
1430
1431 // Only go through the xLabel loop again if the yLabel width has changed
1432 if (cachedYLabelWidth < this.yLabelWidth){
1433 this.calculateXLabelRotation();
1434 }
1435 }
1436
1437 },
1438 calculateXLabelRotation : function(){
1439 //Get the width of each grid by calculating the difference
1440 //between x offsets between 0 and 1.
1441
1442 this.ctx.font = this.font;
1443
1444 var firstWidth = this.ctx.measureText(this.xLabels[0]).width,
1445 lastWidth = this.ctx.measureText(this.xLabels[this.xLabels.length - 1]).width,
1446 firstRotated,
1447 lastRotated;
1448
1449
1450 this.xScalePaddingRight = lastWidth/2 + 3;
1451 this.xScalePaddingLeft = (firstWidth/2 > this.yLabelWidth + 10) ? firstWidth/2 : this.yLabelWidth + 10;
1452
1453 this.xLabelRotation = 0;
1454 if (this.display){
1455 var originalLabelWidth = longestText(this.ctx,this.font,this.xLabels),
1456 cosRotation,
1457 firstRotatedWidth;
1458 this.xLabelWidth = originalLabelWidth;
1459 //Allow 3 pixels x2 padding either side for label readability
1460 var xGridWidth = Math.floor(this.calculateX(1) - this.calculateX(0)) - 6;
1461
1462 //Max label rotate should be 90 - also act as a loop counter
1463 while ((this.xLabelWidth > xGridWidth && this.xLabelRotation === 0) || (this.xLabelWidth > xGridWidth && this.xLabelRotation <= 90 && this.xLabelRotation > 0)){
1464 cosRotation = Math.cos(toRadians(this.xLabelRotation));
1465
1466 firstRotated = cosRotation * firstWidth;
1467 lastRotated = cosRotation * lastWidth;
1468
1469 // We're right aligning the text now.
1470 if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8){
1471 this.xScalePaddingLeft = firstRotated + this.fontSize / 2;
1472 }
1473 this.xScalePaddingRight = this.fontSize/2;
1474
1475
1476 this.xLabelRotation++;
1477 this.xLabelWidth = cosRotation * originalLabelWidth;
1478
1479 }
1480 if (this.xLabelRotation > 0){
1481 this.endPoint -= Math.sin(toRadians(this.xLabelRotation))*originalLabelWidth + 3;
1482 }
1483 }
1484 else{
1485 this.xLabelWidth = 0;
1486 this.xScalePaddingRight = this.padding;
1487 this.xScalePaddingLeft = this.padding;
1488 }
1489
1490 },
1491 // Needs to be overidden in each Chart type
1492 // Otherwise we need to pass all the data into the scale class
1493 calculateYRange: noop,
1494 drawingArea: function(){
1495 return this.startPoint - this.endPoint;
1496 },
1497 calculateY : function(value){
1498 var scalingFactor = this.drawingArea() / (this.min - this.max);
1499 return this.endPoint - (scalingFactor * (value - this.min));
1500 },
1501 calculateX : function(index){
1502 var isRotated = (this.xLabelRotation > 0),
1503 // innerWidth = (this.offsetGridLines) ? this.width - offsetLeft - this.padding : this.width - (offsetLeft + halfLabelWidth * 2) - this.padding,
1504 innerWidth = this.width - (this.xScalePaddingLeft + this.xScalePaddingRight),
1505 valueWidth = innerWidth/(this.valuesCount - ((this.offsetGridLines) ? 0 : 1)),
1506 valueOffset = (valueWidth * index) + this.xScalePaddingLeft;
1507
1508 if (this.offsetGridLines){
1509 valueOffset += (valueWidth/2);
1510 }
1511
1512 return Math.round(valueOffset);
1513 },
1514 update : function(newProps){
1515 helpers.extend(this, newProps);
1516 this.fit();
1517 },
1518 draw : function(){
1519 var ctx = this.ctx,
1520 yLabelGap = (this.endPoint - this.startPoint) / this.steps,
1521 xStart = Math.round(this.xScalePaddingLeft);
1522 if (this.display){
1523 ctx.fillStyle = this.textColor;
1524 ctx.font = this.font;
1525 each(this.yLabels,function(labelString,index){
1526 var yLabelCenter = this.endPoint - (yLabelGap * index),
1527 linePositionY = Math.round(yLabelCenter);
1528
1529 ctx.textAlign = "right";
1530 ctx.textBaseline = "middle";
1531 if (this.showLabels){
1532 ctx.fillText(labelString,xStart - 10,yLabelCenter);
1533 }
1534 ctx.beginPath();
1535 if (index > 0){
1536 // This is a grid line in the centre, so drop that
1537 ctx.lineWidth = this.gridLineWidth;
1538 ctx.strokeStyle = this.gridLineColor;
1539 } else {
1540 // This is the first line on the scale
1541 ctx.lineWidth = this.lineWidth;
1542 ctx.strokeStyle = this.lineColor;
1543 }
1544
1545 linePositionY += helpers.aliasPixel(ctx.lineWidth);
1546
1547 ctx.moveTo(xStart, linePositionY);
1548 ctx.lineTo(this.width, linePositionY);
1549 ctx.stroke();
1550 ctx.closePath();
1551
1552 ctx.lineWidth = this.lineWidth;
1553 ctx.strokeStyle = this.lineColor;
1554 ctx.beginPath();
1555 ctx.moveTo(xStart - 5, linePositionY);
1556 ctx.lineTo(xStart, linePositionY);
1557 ctx.stroke();
1558 ctx.closePath();
1559
1560 },this);
1561
1562 // xLabelsSkipper is a number which if gives 0 as remainder [ indexof(xLabel)/xLabelsSkipper ], we print xLabels, otherwise, we skip it
1563 // if number then divide and determine | else, if true, print all labels, else we never print
1564 this.xLabelsSkipper = isNumber(this.showXLabels) ? Math.ceil(this.xLabels.length/this.showXLabels) : (this.showXLabels === true) ? 1 : this.xLabels.length+1;
1565 each(this.xLabels,function(label,index){
1566 var xPos = this.calculateX(index) + aliasPixel(this.lineWidth),
1567 // Check to see if line/bar here and decide where to place the line
1568 linePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth),
1569 isRotated = (this.xLabelRotation > 0);
1570
1571 ctx.beginPath();
1572
1573 if (index > 0){
1574 // This is a grid line in the centre, so drop that
1575 ctx.lineWidth = this.gridLineWidth;
1576 ctx.strokeStyle = this.gridLineColor;
1577 } else {
1578 // This is the first line on the scale
1579 ctx.lineWidth = this.lineWidth;
1580 ctx.strokeStyle = this.lineColor;
1581 }
1582 ctx.moveTo(linePos,this.endPoint);
1583 ctx.lineTo(linePos,this.startPoint - 3);
1584 ctx.stroke();
1585 ctx.closePath();
1586
1587
1588 ctx.lineWidth = this.lineWidth;
1589 ctx.strokeStyle = this.lineColor;
1590
1591
1592 // Small lines at the bottom of the base grid line
1593 ctx.beginPath();
1594 ctx.moveTo(linePos,this.endPoint);
1595 ctx.lineTo(linePos,this.endPoint + 5);
1596 ctx.stroke();
1597 ctx.closePath();
1598
1599 ctx.save();
1600 ctx.translate(xPos,(isRotated) ? this.endPoint + 12 : this.endPoint + 8);
1601 ctx.rotate(toRadians(this.xLabelRotation)*-1);
1602 ctx.font = this.font;
1603 ctx.textAlign = (isRotated) ? "right" : "center";
1604 ctx.textBaseline = (isRotated) ? "middle" : "top";
1605 if(index % this.xLabelsSkipper === 0) {
1606 ctx.fillText(label, 0, 0);
1607 }
1608 ctx.restore();
1609 },this);
1610
1611 }
1612 }
1613
1614 });
1615
1616 Chart.RadialScale = Chart.Element.extend({
1617 initialize: function(){
1618 this.size = min([this.height, this.width]);
1619 this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2);
1620 },
1621 calculateCenterOffset: function(value){
1622 // Take into account half font size + the yPadding of the top value
1623 var scalingFactor = this.drawingArea / (this.max - this.min);
1624
1625 return (value - this.min) * scalingFactor;
1626 },
1627 update : function(){
1628 if (!this.lineArc){
1629 this.setScaleSize();
1630 } else {
1631 this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2);
1632 }
1633 this.buildYLabels();
1634 },
1635 buildYLabels: function(){
1636 this.yLabels = [];
1637
1638 var stepDecimalPlaces = getDecimalPlaces(this.stepValue);
1639
1640 for (var i=0; i<=this.steps; i++){
1641 this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)}));
1642 }
1643 },
1644 getCircumference : function(){
1645 return ((Math.PI*2) / this.valuesCount);
1646 },
1647 setScaleSize: function(){
1648 /*
1649 * Right, this is really confusing and there is a lot of maths going on here
1650 * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
1651 *
1652 * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
1653 *
1654 * Solution:
1655 *
1656 * We assume the radius of the polygon is half the size of the canvas at first
1657 * at each index we check if the text overlaps.
1658 *
1659 * Where it does, we store that angle and that index.
1660 *
1661 * After finding the largest index and angle we calculate how much we need to remove
1662 * from the shape radius to move the point inwards by that x.
1663 *
1664 * We average the left and right distances to get the maximum shape radius that can fit in the box
1665 * along with labels.
1666 *
1667 * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
1668 * on each side, removing that from the size, halving it and adding the left x protrusion width.
1669 *
1670 * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
1671 * and position it in the most space efficient manner
1672 *
1673 * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
1674 */
1675
1676
1677 // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
1678 // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
1679 var largestPossibleRadius = min([(this.height/2 - this.pointLabelFontSize - 5), this.width/2]),
1680 pointPosition,
1681 i,
1682 textWidth,
1683 halfTextWidth,
1684 furthestRight = this.width,
1685 furthestRightIndex,
1686 furthestRightAngle,
1687 furthestLeft = 0,
1688 furthestLeftIndex,
1689 furthestLeftAngle,
1690 xProtrusionLeft,
1691 xProtrusionRight,
1692 radiusReductionRight,
1693 radiusReductionLeft,
1694 maxWidthRadius;
1695 this.ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);
1696 for (i=0;i<this.valuesCount;i++){
1697 // 5px to space the text slightly out - similar to what we do in the draw function.
1698 pointPosition = this.getPointPosition(i, largestPossibleRadius);
1699 textWidth = this.ctx.measureText(template(this.templateString, { value: this.labels[i] })).width + 5;
1700 if (i === 0 || i === this.valuesCount/2){
1701 // If we're at index zero, or exactly the middle, we're at exactly the top/bottom
1702 // of the radar chart, so text will be aligned centrally, so we'll half it and compare
1703 // w/left and right text sizes
1704 halfTextWidth = textWidth/2;
1705 if (pointPosition.x + halfTextWidth > furthestRight) {
1706 furthestRight = pointPosition.x + halfTextWidth;
1707 furthestRightIndex = i;
1708 }
1709 if (pointPosition.x - halfTextWidth < furthestLeft) {
1710 furthestLeft = pointPosition.x - halfTextWidth;
1711 furthestLeftIndex = i;
1712 }
1713 }
1714 else if (i < this.valuesCount/2) {
1715 // Less than half the values means we'll left align the text
1716 if (pointPosition.x + textWidth > furthestRight) {
1717 furthestRight = pointPosition.x + textWidth;
1718 furthestRightIndex = i;
1719 }
1720 }
1721 else if (i > this.valuesCount/2){
1722 // More than half the values means we'll right align the text
1723 if (pointPosition.x - textWidth < furthestLeft) {
1724 furthestLeft = pointPosition.x - textWidth;
1725 furthestLeftIndex = i;
1726 }
1727 }
1728 }
1729
1730 xProtrusionLeft = furthestLeft;
1731
1732 xProtrusionRight = Math.ceil(furthestRight - this.width);
1733
1734 furthestRightAngle = this.getIndexAngle(furthestRightIndex);
1735
1736 furthestLeftAngle = this.getIndexAngle(furthestLeftIndex);
1737
1738 radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI/2);
1739
1740 radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI/2);
1741
1742 // Ensure we actually need to reduce the size of the chart
1743 radiusReductionRight = (isNumber(radiusReductionRight)) ? radiusReductionRight : 0;
1744 radiusReductionLeft = (isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;
1745
1746 this.drawingArea = largestPossibleRadius - (radiusReductionLeft + radiusReductionRight)/2;
1747
1748 //this.drawingArea = min([maxWidthRadius, (this.height - (2 * (this.pointLabelFontSize + 5)))/2])
1749 this.setCenterPoint(radiusReductionLeft, radiusReductionRight);
1750
1751 },
1752 setCenterPoint: function(leftMovement, rightMovement){
1753
1754 var maxRight = this.width - rightMovement - this.drawingArea,
1755 maxLeft = leftMovement + this.drawingArea;
1756
1757 this.xCenter = (maxLeft + maxRight)/2;
1758 // Always vertically in the centre as the text height doesn't change
1759 this.yCenter = (this.height/2);
1760 },
1761
1762 getIndexAngle : function(index){
1763 var angleMultiplier = (Math.PI * 2) / this.valuesCount;
1764 // Start from the top instead of right, so remove a quarter of the circle
1765
1766 return index * angleMultiplier - (Math.PI/2);
1767 },
1768 getPointPosition : function(index, distanceFromCenter){
1769 var thisAngle = this.getIndexAngle(index);
1770 return {
1771 x : (Math.cos(thisAngle) * distanceFromCenter) + this.xCenter,
1772 y : (Math.sin(thisAngle) * distanceFromCenter) + this.yCenter
1773 };
1774 },
1775 draw: function(){
1776 if (this.display){
1777 var ctx = this.ctx;
1778 each(this.yLabels, function(label, index){
1779 // Don't draw a centre value
1780 if (index > 0){
1781 var yCenterOffset = index * (this.drawingArea/this.steps),
1782 yHeight = this.yCenter - yCenterOffset,
1783 pointPosition;
1784
1785 // Draw circular lines around the scale
1786 if (this.lineWidth > 0){
1787 ctx.strokeStyle = this.lineColor;
1788 ctx.lineWidth = this.lineWidth;
1789
1790 if(this.lineArc){
1791 ctx.beginPath();
1792 ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI*2);
1793 ctx.closePath();
1794 ctx.stroke();
1795 } else{
1796 ctx.beginPath();
1797 for (var i=0;i<this.valuesCount;i++)
1798 {
1799 pointPosition = this.getPointPosition(i, this.calculateCenterOffset(this.min + (index * this.stepValue)));
1800 if (i === 0){
1801 ctx.moveTo(pointPosition.x, pointPosition.y);
1802 } else {
1803 ctx.lineTo(pointPosition.x, pointPosition.y);
1804 }
1805 }
1806 ctx.closePath();
1807 ctx.stroke();
1808 }
1809 }
1810 if(this.showLabels){
1811 ctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);
1812 if (this.showLabelBackdrop){
1813 var labelWidth = ctx.measureText(label).width;
1814 ctx.fillStyle = this.backdropColor;
1815 ctx.fillRect(
1816 this.xCenter - labelWidth/2 - this.backdropPaddingX,
1817 yHeight - this.fontSize/2 - this.backdropPaddingY,
1818 labelWidth + this.backdropPaddingX*2,
1819 this.fontSize + this.backdropPaddingY*2
1820 );
1821 }
1822 ctx.textAlign = 'center';
1823 ctx.textBaseline = "middle";
1824 ctx.fillStyle = this.fontColor;
1825 ctx.fillText(label, this.xCenter, yHeight);
1826 }
1827 }
1828 }, this);
1829
1830 if (!this.lineArc){
1831 ctx.lineWidth = this.angleLineWidth;
1832 ctx.strokeStyle = this.angleLineColor;
1833 for (var i = this.valuesCount - 1; i >= 0; i--) {
1834 if (this.angleLineWidth > 0){
1835 var outerPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max));
1836 ctx.beginPath();
1837 ctx.moveTo(this.xCenter, this.yCenter);
1838 ctx.lineTo(outerPosition.x, outerPosition.y);
1839 ctx.stroke();
1840 ctx.closePath();
1841 }
1842 // Extra 3px out for some label spacing
1843 var pointLabelPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max) + 5);
1844 ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);
1845 ctx.fillStyle = this.pointLabelFontColor;
1846
1847 var labelsCount = this.labels.length,
1848 halfLabelsCount = this.labels.length/2,
1849 quarterLabelsCount = halfLabelsCount/2,
1850 upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),
1851 exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);
1852 if (i === 0){
1853 ctx.textAlign = 'center';
1854 } else if(i === halfLabelsCount){
1855 ctx.textAlign = 'center';
1856 } else if (i < halfLabelsCount){
1857 ctx.textAlign = 'left';
1858 } else {
1859 ctx.textAlign = 'right';
1860 }
1861
1862 // Set the correct text baseline based on outer positioning
1863 if (exactQuarter){
1864 ctx.textBaseline = 'middle';
1865 } else if (upperHalf){
1866 ctx.textBaseline = 'bottom';
1867 } else {
1868 ctx.textBaseline = 'top';
1869 }
1870
1871 ctx.fillText(this.labels[i], pointLabelPosition.x, pointLabelPosition.y);
1872 }
1873 }
1874 }
1875 }
1876 });
1877
1878 // Attach global event to resize each chart instance when the browser resizes
1879 helpers.addEvent(window, "resize", (function(){
1880 // Basic debounce of resize function so it doesn't hurt performance when resizing browser.
1881 var timeout;
1882 return function(){
1883 clearTimeout(timeout);
1884 timeout = setTimeout(function(){
1885 each(Chart.instances,function(instance){
1886 // If the responsive flag is set in the chart instance config
1887 // Cascade the resize event down to the chart.
1888 if (instance.options.responsive){
1889 instance.resize(instance.render, true);
1890 }
1891 });
1892 }, 50);
1893 };
1894 })());
1895
1896
1897 if (amd) {
1898 define(function(){
1899 return Chart;
1900 });
1901 } else if (typeof module === 'object' && module.exports) {
1902 module.exports = Chart;
1903 }
1904
1905 root.Chart = Chart;
1906
1907 Chart.noConflict = function(){
1908 root.Chart = previous;
1909 return Chart;
1910 };
1911
1912 }).call(this);
1913
1914 (function(){
1915 "use strict";
1916
1917 var root = this,
1918 Chart = root.Chart,
1919 helpers = Chart.helpers;
1920
1921
1922 var defaultConfig = {
1923 //Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
1924 scaleBeginAtZero : true,
1925
1926 //Boolean - Whether grid lines are shown across the chart
1927 scaleShowGridLines : true,
1928
1929 //String - Colour of the grid lines
1930 scaleGridLineColor : "rgba(0,0,0,.05)",
1931
1932 //Number - Width of the grid lines
1933 scaleGridLineWidth : 1,
1934
1935 //Boolean - If there is a stroke on each bar
1936 barShowStroke : true,
1937
1938 //Number - Pixel width of the bar stroke
1939 barStrokeWidth : 2,
1940
1941 //Number - Spacing between each of the X value sets
1942 barValueSpacing : 5,
1943
1944 //Number - Spacing between data sets within X values
1945 barDatasetSpacing : 1,
1946
1947 //String - A legend template
1948 legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].fillColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
1949
1950 };
1951
1952
1953 Chart.Type.extend({
1954 name: "Bar",
1955 defaults : defaultConfig,
1956 initialize: function(data){
1957
1958 //Expose options as a scope variable here so we can access it in the ScaleClass
1959 var options = this.options;
1960
1961 this.ScaleClass = Chart.Scale.extend({
1962 offsetGridLines : true,
1963 calculateBarX : function(datasetCount, datasetIndex, barIndex){
1964 //Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
1965 var xWidth = this.calculateBaseWidth(),
1966 xAbsolute = this.calculateX(barIndex) - (xWidth/2),
1967 barWidth = this.calculateBarWidth(datasetCount);
1968
1969 return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth/2;
1970 },
1971 calculateBaseWidth : function(){
1972 return (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing);
1973 },
1974 calculateBarWidth : function(datasetCount){
1975 //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
1976 var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);
1977
1978 return (baseWidth / datasetCount);
1979 }
1980 });
1981
1982 this.datasets = [];
1983
1984 //Set up tooltip events on the chart
1985 if (this.options.showTooltips){
1986 helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
1987 var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];
1988
1989 this.eachBars(function(bar){
1990 bar.restore(['fillColor', 'strokeColor']);
1991 });
1992 helpers.each(activeBars, function(activeBar){
1993 activeBar.fillColor = activeBar.highlightFill;
1994 activeBar.strokeColor = activeBar.highlightStroke;
1995 });
1996 this.showTooltip(activeBars);
1997 });
1998 }
1999
2000 //Declare the extension of the default point, to cater for the options passed in to the constructor
2001 this.BarClass = Chart.Rectangle.extend({
2002 strokeWidth : this.options.barStrokeWidth,
2003 showStroke : this.options.barShowStroke,
2004 ctx : this.chart.ctx
2005 });
2006
2007 //Iterate through each of the datasets, and build this into a property of the chart
2008 helpers.each(data.datasets,function(dataset,datasetIndex){
2009
2010 var datasetObject = {
2011 label : dataset.label || null,
2012 fillColor : dataset.fillColor,
2013 strokeColor : dataset.strokeColor,
2014 bars : []
2015 };
2016
2017 this.datasets.push(datasetObject);
2018
2019 helpers.each(dataset.data,function(dataPoint,index){
2020 if (helpers.isNumber(dataPoint)){
2021 //Add a new point for each piece of data, passing any required data to draw.
2022 datasetObject.bars.push(new this.BarClass({
2023 value : dataPoint,
2024 label : data.labels[index],
2025 datasetLabel: dataset.label,
2026 strokeColor : dataset.strokeColor,
2027 fillColor : dataset.fillColor,
2028 highlightFill : dataset.highlightFill || dataset.fillColor,
2029 highlightStroke : dataset.highlightStroke || dataset.strokeColor
2030 }));
2031 }
2032 },this);
2033
2034 },this);
2035
2036 this.buildScale(data.labels);
2037
2038 this.BarClass.prototype.base = this.scale.endPoint;
2039
2040 this.eachBars(function(bar, index, datasetIndex){
2041 helpers.extend(bar, {
2042 width : this.scale.calculateBarWidth(this.datasets.length),
2043 x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
2044 y: this.scale.endPoint
2045 });
2046 bar.save();
2047 }, this);
2048
2049 this.render();
2050 },
2051 update : function(){
2052 this.scale.update();
2053 // Reset any highlight colours before updating.
2054 helpers.each(this.activeElements, function(activeElement){
2055 activeElement.restore(['fillColor', 'strokeColor']);
2056 });
2057
2058 this.eachBars(function(bar){
2059 bar.save();
2060 });
2061 this.render();
2062 },
2063 eachBars : function(callback){
2064 helpers.each(this.datasets,function(dataset, datasetIndex){
2065 helpers.each(dataset.bars, callback, this, datasetIndex);
2066 },this);
2067 },
2068 getBarsAtEvent : function(e){
2069 var barsArray = [],
2070 eventPosition = helpers.getRelativePosition(e),
2071 datasetIterator = function(dataset){
2072 barsArray.push(dataset.bars[barIndex]);
2073 },
2074 barIndex;
2075
2076 for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) {
2077 for (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) {
2078 if (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){
2079 helpers.each(this.datasets, datasetIterator);
2080 return barsArray;
2081 }
2082 }
2083 }
2084
2085 return barsArray;
2086 },
2087 buildScale : function(labels){
2088 var self = this;
2089
2090 var dataTotal = function(){
2091 var values = [];
2092 self.eachBars(function(bar){
2093 values.push(bar.value);
2094 });
2095 return values;
2096 };
2097
2098 var scaleOptions = {
2099 templateString : this.options.scaleLabel,
2100 height : this.chart.height,
2101 width : this.chart.width,
2102 ctx : this.chart.ctx,
2103 textColor : this.options.scaleFontColor,
2104 fontSize : this.options.scaleFontSize,
2105 fontStyle : this.options.scaleFontStyle,
2106 fontFamily : this.options.scaleFontFamily,
2107 valuesCount : labels.length,
2108 beginAtZero : this.options.scaleBeginAtZero,
2109 integersOnly : this.options.scaleIntegersOnly,
2110 calculateYRange: function(currentHeight){
2111 var updatedRanges = helpers.calculateScaleRange(
2112 dataTotal(),
2113 currentHeight,
2114 this.fontSize,
2115 this.beginAtZero,
2116 this.integersOnly
2117 );
2118 helpers.extend(this, updatedRanges);
2119 },
2120 xLabels : labels,
2121 showXLabels: (this.options.showXLabels) ? this.options.showXLabels : true,
2122 font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
2123 lineWidth : this.options.scaleLineWidth,
2124 lineColor : this.options.scaleLineColor,
2125 gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
2126 gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
2127 padding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0,
2128 showLabels : this.options.scaleShowLabels,
2129 display : this.options.showScale
2130 };
2131
2132 if (this.options.scaleOverride){
2133 helpers.extend(scaleOptions, {
2134 calculateYRange: helpers.noop,
2135 steps: this.options.scaleSteps,
2136 stepValue: this.options.scaleStepWidth,
2137 min: this.options.scaleStartValue,
2138 max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
2139 });
2140 }
2141
2142 this.scale = new this.ScaleClass(scaleOptions);
2143 },
2144 addData : function(valuesArray,label){
2145 //Map the values array for each of the datasets
2146 helpers.each(valuesArray,function(value,datasetIndex){
2147 if (helpers.isNumber(value)){
2148 //Add a new point for each piece of data, passing any required data to draw.
2149 this.datasets[datasetIndex].bars.push(new this.BarClass({
2150 value : value,
2151 label : label,
2152 x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1),
2153 y: this.scale.endPoint,
2154 width : this.scale.calculateBarWidth(this.datasets.length),
2155 base : this.scale.endPoint,
2156 strokeColor : this.datasets[datasetIndex].strokeColor,
2157 fillColor : this.datasets[datasetIndex].fillColor
2158 }));
2159 }
2160 },this);
2161
2162 this.scale.addXLabel(label);
2163 //Then re-render the chart.
2164 this.update();
2165 },
2166 removeData : function(){
2167 this.scale.removeXLabel();
2168 //Then re-render the chart.
2169 helpers.each(this.datasets,function(dataset){
2170 dataset.bars.shift();
2171 },this);
2172 this.update();
2173 },
2174 reflow : function(){
2175 helpers.extend(this.BarClass.prototype,{
2176 y: this.scale.endPoint,
2177 base : this.scale.endPoint
2178 });
2179 var newScaleProps = helpers.extend({
2180 height : this.chart.height,
2181 width : this.chart.width
2182 });
2183 this.scale.update(newScaleProps);
2184 },
2185 draw : function(ease){
2186 var easingDecimal = ease || 1;
2187 this.clear();
2188
2189 var ctx = this.chart.ctx;
2190
2191 this.scale.draw(easingDecimal);
2192
2193 //Draw all the bars for each dataset
2194 helpers.each(this.datasets,function(dataset,datasetIndex){
2195 helpers.each(dataset.bars,function(bar,index){
2196 bar.base = this.scale.endPoint;
2197 //Transition then draw
2198 bar.transition({
2199 x : this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
2200 y : this.scale.calculateY(bar.value),
2201 width : this.scale.calculateBarWidth(this.datasets.length)
2202 }, easingDecimal).draw();
2203 },this);
2204
2205 },this);
2206 }
2207 });
2208
2209
2210 }).call(this);
2211 (function(){
2212 "use strict";
2213
2214 var root = this,
2215 Chart = root.Chart,
2216 //Cache a local reference to Chart.helpers
2217 helpers = Chart.helpers;
2218
2219 var defaultConfig = {
2220 //Boolean - Whether we should show a stroke on each segment
2221 segmentShowStroke : true,
2222
2223 //String - The colour of each segment stroke
2224 segmentStrokeColor : "#fff",
2225
2226 //Number - The width of each segment stroke
2227 segmentStrokeWidth : 2,
2228
2229 //The percentage of the chart that we cut out of the middle.
2230 percentageInnerCutout : 50,
2231
2232 //Number - Amount of animation steps
2233 animationSteps : 100,
2234
2235 //String - Animation easing effect
2236 animationEasing : "easeOutBounce",
2237
2238 //Boolean - Whether we animate the rotation of the Doughnut
2239 animateRotate : true,
2240
2241 //Boolean - Whether we animate scaling the Doughnut from the centre
2242 animateScale : false,
2243
2244 //String - A legend template
2245 legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>"
2246
2247 };
2248
2249
2250 Chart.Type.extend({
2251 //Passing in a name registers this chart in the Chart namespace
2252 name: "Doughnut",
2253 //Providing a defaults will also register the deafults in the chart namespace
2254 defaults : defaultConfig,
2255 //Initialize is fired when the chart is initialized - Data is passed in as a parameter
2256 //Config is automatically merged by the core of Chart.js, and is available at this.options
2257 initialize: function(data){
2258
2259 //Declare segments as a static property to prevent inheriting across the Chart type prototype
2260 this.segments = [];
2261 this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2;
2262
2263 this.SegmentArc = Chart.Arc.extend({
2264 ctx : this.chart.ctx,
2265 x : this.chart.width/2,
2266 y : this.chart.height/2
2267 });
2268
2269 //Set up tooltip events on the chart
2270 if (this.options.showTooltips){
2271 helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
2272 var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
2273
2274 helpers.each(this.segments,function(segment){
2275 segment.restore(["fillColor"]);
2276 });
2277 helpers.each(activeSegments,function(activeSegment){
2278 activeSegment.fillColor = activeSegment.highlightColor;
2279 });
2280 this.showTooltip(activeSegments);
2281 });
2282 }
2283 this.calculateTotal(data);
2284
2285 helpers.each(data,function(datapoint, index){
2286 this.addData(datapoint, index, true);
2287 },this);
2288
2289 this.render();
2290 },
2291 getSegmentsAtEvent : function(e){
2292 var segmentsArray = [];
2293
2294 var location = helpers.getRelativePosition(e);
2295
2296 helpers.each(this.segments,function(segment){
2297 if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
2298 },this);
2299 return segmentsArray;
2300 },
2301 addData : function(segment, atIndex, silent){
2302 var index = atIndex || this.segments.length;
2303 this.segments.splice(index, 0, new this.SegmentArc({
2304 value : segment.value,
2305 outerRadius : (this.options.animateScale) ? 0 : this.outerRadius,
2306 innerRadius : (this.options.animateScale) ? 0 : (this.outerRadius/100) * this.options.percentageInnerCutout,
2307 fillColor : segment.color,
2308 highlightColor : segment.highlight || segment.color,
2309 showStroke : this.options.segmentShowStroke,
2310 strokeWidth : this.options.segmentStrokeWidth,
2311 strokeColor : this.options.segmentStrokeColor,
2312 startAngle : Math.PI * 1.5,
2313 circumference : (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value),
2314 label : segment.label
2315 }));
2316 if (!silent){
2317 this.reflow();
2318 this.update();
2319 }
2320 },
2321 calculateCircumference : function(value){
2322 return (Math.PI*2)*(value / this.total);
2323 },
2324 calculateTotal : function(data){
2325 this.total = 0;
2326 helpers.each(data,function(segment){
2327 this.total += segment.value;
2328 },this);
2329 },
2330 update : function(){
2331 this.calculateTotal(this.segments);
2332
2333 // Reset any highlight colours before updating.
2334 helpers.each(this.activeElements, function(activeElement){
2335 activeElement.restore(['fillColor']);
2336 });
2337
2338 helpers.each(this.segments,function(segment){
2339 segment.save();
2340 });
2341 this.render();
2342 },
2343
2344 removeData: function(atIndex){
2345 var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1;
2346 this.segments.splice(indexToDelete, 1);
2347 this.reflow();
2348 this.update();
2349 },
2350
2351 reflow : function(){
2352 helpers.extend(this.SegmentArc.prototype,{
2353 x : this.chart.width/2,
2354 y : this.chart.height/2
2355 });
2356 this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2;
2357 helpers.each(this.segments, function(segment){
2358 segment.update({
2359 outerRadius : this.outerRadius,
2360 innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout
2361 });
2362 }, this);
2363 },
2364 draw : function(easeDecimal){
2365 var animDecimal = (easeDecimal) ? easeDecimal : 1;
2366 this.clear();
2367 helpers.each(this.segments,function(segment,index){
2368 segment.transition({
2369 circumference : this.calculateCircumference(segment.value),
2370 outerRadius : this.outerRadius,
2371 innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout
2372 },animDecimal);
2373
2374 segment.endAngle = segment.startAngle + segment.circumference;
2375
2376 segment.draw();
2377 if (index === 0){
2378 segment.startAngle = Math.PI * 1.5;
2379 }
2380 //Check to see if it's the last segment, if not get the next and update the start angle
2381 if (index < this.segments.length-1){
2382 this.segments[index+1].startAngle = segment.endAngle;
2383 }
2384 },this);
2385
2386 }
2387 });
2388
2389 Chart.types.Doughnut.extend({
2390 name : "Pie",
2391 defaults : helpers.merge(defaultConfig,{percentageInnerCutout : 0})
2392 });
2393
2394 }).call(this);
2395 (function(){
2396 "use strict";
2397
2398 var root = this,
2399 Chart = root.Chart,
2400 helpers = Chart.helpers;
2401
2402 var defaultConfig = {
2403
2404 ///Boolean - Whether grid lines are shown across the chart
2405 scaleShowGridLines : true,
2406
2407 //String - Colour of the grid lines
2408 scaleGridLineColor : "rgba(0,0,0,.05)",
2409
2410 //Number - Width of the grid lines
2411 scaleGridLineWidth : 1,
2412
2413 //Boolean - Whether the line is curved between points
2414 bezierCurve : true,
2415
2416 //Number - Tension of the bezier curve between points
2417 bezierCurveTension : 0.4,
2418
2419 //Boolean - Whether to show a dot for each point
2420 pointDot : true,
2421
2422 //Number - Radius of each point dot in pixels
2423 pointDotRadius : 4,
2424
2425 //Number - Pixel width of point dot stroke
2426 pointDotStrokeWidth : 1,
2427
2428 //Number - amount extra to add to the radius to cater for hit detection outside the drawn point
2429 pointHitDetectionRadius : 20,
2430
2431 //Boolean - Whether to show a stroke for datasets
2432 datasetStroke : true,
2433
2434 //Number - Pixel width of dataset stroke
2435 datasetStrokeWidth : 2,
2436
2437 //Boolean - Whether to fill the dataset with a colour
2438 datasetFill : true,
2439
2440 //String - A legend template
2441 legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
2442
2443 };
2444
2445
2446 Chart.Type.extend({
2447 name: "Line",
2448 defaults : defaultConfig,
2449 initialize: function(data){
2450 //Declare the extension of the default point, to cater for the options passed in to the constructor
2451 this.PointClass = Chart.Point.extend({
2452 strokeWidth : this.options.pointDotStrokeWidth,
2453 radius : this.options.pointDotRadius,
2454 display: this.options.pointDot,
2455 hitDetectionRadius : this.options.pointHitDetectionRadius,
2456 ctx : this.chart.ctx,
2457 inRange : function(mouseX){
2458 return (Math.pow(mouseX-this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius,2));
2459 }
2460 });
2461
2462 this.datasets = [];
2463
2464 //Set up tooltip events on the chart
2465 if (this.options.showTooltips){
2466 helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
2467 var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
2468 this.eachPoints(function(point){
2469 point.restore(['fillColor', 'strokeColor']);
2470 });
2471 helpers.each(activePoints, function(activePoint){
2472 activePoint.fillColor = activePoint.highlightFill;
2473 activePoint.strokeColor = activePoint.highlightStroke;
2474 });
2475 this.showTooltip(activePoints);
2476 });
2477 }
2478
2479 //Iterate through each of the datasets, and build this into a property of the chart
2480 helpers.each(data.datasets,function(dataset){
2481
2482 var datasetObject = {
2483 label : dataset.label || null,
2484 fillColor : dataset.fillColor,
2485 strokeColor : dataset.strokeColor,
2486 pointColor : dataset.pointColor,
2487 pointStrokeColor : dataset.pointStrokeColor,
2488 points : []
2489 };
2490
2491 this.datasets.push(datasetObject);
2492
2493
2494 helpers.each(dataset.data,function(dataPoint,index){
2495 //Best way to do this? or in draw sequence...?
2496 if (helpers.isNumber(dataPoint)){
2497 //Add a new point for each piece of data, passing any required data to draw.
2498 datasetObject.points.push(new this.PointClass({
2499 value : dataPoint,
2500 label : data.labels[index],
2501 datasetLabel: dataset.label,
2502 strokeColor : dataset.pointStrokeColor,
2503 fillColor : dataset.pointColor,
2504 highlightFill : dataset.pointHighlightFill || dataset.pointColor,
2505 highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
2506 }));
2507 }
2508 },this);
2509
2510 this.buildScale(data.labels);
2511
2512
2513 this.eachPoints(function(point, index){
2514 helpers.extend(point, {
2515 x: this.scale.calculateX(index),
2516 y: this.scale.endPoint
2517 });
2518 point.save();
2519 }, this);
2520
2521 },this);
2522
2523
2524 this.render();
2525 },
2526 update : function(){
2527 this.scale.update();
2528 // Reset any highlight colours before updating.
2529 helpers.each(this.activeElements, function(activeElement){
2530 activeElement.restore(['fillColor', 'strokeColor']);
2531 });
2532 this.eachPoints(function(point){
2533 point.save();
2534 });
2535 this.render();
2536 },
2537 eachPoints : function(callback){
2538 helpers.each(this.datasets,function(dataset){
2539 helpers.each(dataset.points,callback,this);
2540 },this);
2541 },
2542 getPointsAtEvent : function(e){
2543 var pointsArray = [],
2544 eventPosition = helpers.getRelativePosition(e);
2545 helpers.each(this.datasets,function(dataset){
2546 helpers.each(dataset.points,function(point){
2547 if (point.inRange(eventPosition.x,eventPosition.y)) pointsArray.push(point);
2548 });
2549 },this);
2550 return pointsArray;
2551 },
2552 buildScale : function(labels){
2553 var self = this;
2554
2555 var dataTotal = function(){
2556 var values = [];
2557 self.eachPoints(function(point){
2558 values.push(point.value);
2559 });
2560
2561 return values;
2562 };
2563
2564 var scaleOptions = {
2565 templateString : this.options.scaleLabel,
2566 height : this.chart.height,
2567 width : this.chart.width,
2568 ctx : this.chart.ctx,
2569 textColor : this.options.scaleFontColor,
2570 fontSize : this.options.scaleFontSize,
2571 fontStyle : this.options.scaleFontStyle,
2572 fontFamily : this.options.scaleFontFamily,
2573 valuesCount : labels.length,
2574 beginAtZero : this.options.scaleBeginAtZero,
2575 integersOnly : this.options.scaleIntegersOnly,
2576 calculateYRange : function(currentHeight){
2577 var updatedRanges = helpers.calculateScaleRange(
2578 dataTotal(),
2579 currentHeight,
2580 this.fontSize,
2581 this.beginAtZero,
2582 this.integersOnly
2583 );
2584 helpers.extend(this, updatedRanges);
2585 },
2586 xLabels : labels,
2587 showXLabels: (this.options.showXLabels) ? this.options.showXLabels : true,
2588 font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
2589 lineWidth : this.options.scaleLineWidth,
2590 lineColor : this.options.scaleLineColor,
2591 gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
2592 gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
2593 padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth,
2594 showLabels : this.options.scaleShowLabels,
2595 display : this.options.showScale
2596 };
2597
2598 if (this.options.scaleOverride){
2599 helpers.extend(scaleOptions, {
2600 calculateYRange: helpers.noop,
2601 steps: this.options.scaleSteps,
2602 stepValue: this.options.scaleStepWidth,
2603 min: this.options.scaleStartValue,
2604 max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
2605 });
2606 }
2607
2608
2609 this.scale = new Chart.Scale(scaleOptions);
2610 },
2611 addData : function(valuesArray,label){
2612 //Map the values array for each of the datasets
2613
2614 helpers.each(valuesArray,function(value,datasetIndex){
2615 if (helpers.isNumber(value)){
2616 //Add a new point for each piece of data, passing any required data to draw.
2617 this.datasets[datasetIndex].points.push(new this.PointClass({
2618 value : value,
2619 label : label,
2620 x: this.scale.calculateX(this.scale.valuesCount+1),
2621 y: this.scale.endPoint,
2622 strokeColor : this.datasets[datasetIndex].pointStrokeColor,
2623 fillColor : this.datasets[datasetIndex].pointColor
2624 }));
2625 }
2626 },this);
2627
2628 this.scale.addXLabel(label);
2629 //Then re-render the chart.
2630 this.update();
2631 },
2632 removeData : function(){
2633 this.scale.removeXLabel();
2634 //Then re-render the chart.
2635 helpers.each(this.datasets,function(dataset){
2636 dataset.points.shift();
2637 },this);
2638 this.update();
2639 },
2640 reflow : function(){
2641 var newScaleProps = helpers.extend({
2642 height : this.chart.height,
2643 width : this.chart.width
2644 });
2645 this.scale.update(newScaleProps);
2646 },
2647 draw : function(ease){
2648 var easingDecimal = ease || 1;
2649 this.clear();
2650
2651 var ctx = this.chart.ctx;
2652
2653 this.scale.draw(easingDecimal);
2654
2655
2656 helpers.each(this.datasets,function(dataset){
2657
2658 //Transition each point first so that the line and point drawing isn't out of sync
2659 //We can use this extra loop to calculate the control points of this dataset also in this loop
2660
2661 helpers.each(dataset.points,function(point,index){
2662 point.transition({
2663 y : this.scale.calculateY(point.value),
2664 x : this.scale.calculateX(index)
2665 }, easingDecimal);
2666
2667 },this);
2668
2669
2670 // Control points need to be calculated in a seperate loop, because we need to know the current x/y of the point
2671 // This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed
2672 if (this.options.bezierCurve){
2673 helpers.each(dataset.points,function(point,index){
2674 //If we're at the start or end, we don't have a previous/next point
2675 //By setting the tension to 0 here, the curve will transition to straight at the end
2676 if (index === 0){
2677 point.controlPoints = helpers.splineCurve(point,point,dataset.points[index+1],0);
2678 }
2679 else if (index >= dataset.points.length-1){
2680 point.controlPoints = helpers.splineCurve(dataset.points[index-1],point,point,0);
2681 }
2682 else{
2683 point.controlPoints = helpers.splineCurve(dataset.points[index-1],point,dataset.points[index+1],this.options.bezierCurveTension);
2684 }
2685 },this);
2686 }
2687
2688
2689 //Draw the line between all the points
2690 ctx.lineWidth = this.options.datasetStrokeWidth;
2691 ctx.strokeStyle = dataset.strokeColor;
2692 ctx.beginPath();
2693 helpers.each(dataset.points,function(point,index){
2694 if (index>0){
2695 if(this.options.bezierCurve){
2696 ctx.bezierCurveTo(
2697 dataset.points[index-1].controlPoints.outer.x,
2698 dataset.points[index-1].controlPoints.outer.y,
2699 point.controlPoints.inner.x,
2700 point.controlPoints.inner.y,
2701 point.x,
2702 point.y
2703 );
2704 }
2705 else{
2706 ctx.lineTo(point.x,point.y);
2707 }
2708
2709 }
2710 else{
2711 ctx.moveTo(point.x,point.y);
2712 }
2713 },this);
2714 ctx.stroke();
2715
2716
2717 if (this.options.datasetFill){
2718 //Round off the line by going to the base of the chart, back to the start, then fill.
2719 ctx.lineTo(dataset.points[dataset.points.length-1].x, this.scale.endPoint);
2720 ctx.lineTo(this.scale.calculateX(0), this.scale.endPoint);
2721 ctx.fillStyle = dataset.fillColor;
2722 ctx.closePath();
2723 ctx.fill();
2724 }
2725
2726 //Now draw the points over the line
2727 //A little inefficient double looping, but better than the line
2728 //lagging behind the point positions
2729 helpers.each(dataset.points,function(point){
2730 point.draw();
2731 });
2732
2733 },this);
2734 }
2735 });
2736
2737
2738 }).call(this);
2739 (function(){
2740 "use strict";
2741
2742 var root = this,
2743 Chart = root.Chart,
2744 //Cache a local reference to Chart.helpers
2745 helpers = Chart.helpers;
2746
2747 var defaultConfig = {
2748 //Boolean - Show a backdrop to the scale label
2749 scaleShowLabelBackdrop : true,
2750
2751 //String - The colour of the label backdrop
2752 scaleBackdropColor : "rgba(255,255,255,0.75)",
2753
2754 // Boolean - Whether the scale should begin at zero
2755 scaleBeginAtZero : true,
2756
2757 //Number - The backdrop padding above & below the label in pixels
2758 scaleBackdropPaddingY : 2,
2759
2760 //Number - The backdrop padding to the side of the label in pixels
2761 scaleBackdropPaddingX : 2,
2762
2763 //Boolean - Show line for each value in the scale
2764 scaleShowLine : true,
2765
2766 //Boolean - Stroke a line around each segment in the chart
2767 segmentShowStroke : true,
2768
2769 //String - The colour of the stroke on each segement.
2770 segmentStrokeColor : "#fff",
2771
2772 //Number - The width of the stroke value in pixels
2773 segmentStrokeWidth : 2,
2774
2775 //Number - Amount of animation steps
2776 animationSteps : 100,
2777
2778 //String - Animation easing effect.
2779 animationEasing : "easeOutBounce",
2780
2781 //Boolean - Whether to animate the rotation of the chart
2782 animateRotate : true,
2783
2784 //Boolean - Whether to animate scaling the chart from the centre
2785 animateScale : false,
2786
2787 //String - A legend template
2788 legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>"
2789 };
2790
2791
2792 Chart.Type.extend({
2793 //Passing in a name registers this chart in the Chart namespace
2794 name: "PolarArea",
2795 //Providing a defaults will also register the deafults in the chart namespace
2796 defaults : defaultConfig,
2797 //Initialize is fired when the chart is initialized - Data is passed in as a parameter
2798 //Config is automatically merged by the core of Chart.js, and is available at this.options
2799 initialize: function(data){
2800 this.segments = [];
2801 //Declare segment class as a chart instance specific class, so it can share props for this instance
2802 this.SegmentArc = Chart.Arc.extend({
2803 showStroke : this.options.segmentShowStroke,
2804 strokeWidth : this.options.segmentStrokeWidth,
2805 strokeColor : this.options.segmentStrokeColor,
2806 ctx : this.chart.ctx,
2807 innerRadius : 0,
2808 x : this.chart.width/2,
2809 y : this.chart.height/2
2810 });
2811 this.scale = new Chart.RadialScale({
2812 display: this.options.showScale,
2813 fontStyle: this.options.scaleFontStyle,
2814 fontSize: this.options.scaleFontSize,
2815 fontFamily: this.options.scaleFontFamily,
2816 fontColor: this.options.scaleFontColor,
2817 showLabels: this.options.scaleShowLabels,
2818 showLabelBackdrop: this.options.scaleShowLabelBackdrop,
2819 backdropColor: this.options.scaleBackdropColor,
2820 backdropPaddingY : this.options.scaleBackdropPaddingY,
2821 backdropPaddingX: this.options.scaleBackdropPaddingX,
2822 lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,
2823 lineColor: this.options.scaleLineColor,
2824 lineArc: true,
2825 width: this.chart.width,
2826 height: this.chart.height,
2827 xCenter: this.chart.width/2,
2828 yCenter: this.chart.height/2,
2829 ctx : this.chart.ctx,
2830 templateString: this.options.scaleLabel,
2831 valuesCount: data.length
2832 });
2833
2834 this.updateScaleRange(data);
2835
2836 this.scale.update();
2837
2838 helpers.each(data,function(segment,index){
2839 this.addData(segment,index,true);
2840 },this);
2841
2842 //Set up tooltip events on the chart
2843 if (this.options.showTooltips){
2844 helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
2845 var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
2846 helpers.each(this.segments,function(segment){
2847 segment.restore(["fillColor"]);
2848 });
2849 helpers.each(activeSegments,function(activeSegment){
2850 activeSegment.fillColor = activeSegment.highlightColor;
2851 });
2852 this.showTooltip(activeSegments);
2853 });
2854 }
2855
2856 this.render();
2857 },
2858 getSegmentsAtEvent : function(e){
2859 var segmentsArray = [];
2860
2861 var location = helpers.getRelativePosition(e);
2862
2863 helpers.each(this.segments,function(segment){
2864 if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
2865 },this);
2866 return segmentsArray;
2867 },
2868 addData : function(segment, atIndex, silent){
2869 var index = atIndex || this.segments.length;
2870
2871 this.segments.splice(index, 0, new this.SegmentArc({
2872 fillColor: segment.color,
2873 highlightColor: segment.highlight || segment.color,
2874 label: segment.label,
2875 value: segment.value,
2876 outerRadius: (this.options.animateScale) ? 0 : this.scale.calculateCenterOffset(segment.value),
2877 circumference: (this.options.animateRotate) ? 0 : this.scale.getCircumference(),
2878 startAngle: Math.PI * 1.5
2879 }));
2880 if (!silent){
2881 this.reflow();
2882 this.update();
2883 }
2884 },
2885 removeData: function(atIndex){
2886 var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1;
2887 this.segments.splice(indexToDelete, 1);
2888 this.reflow();
2889 this.update();
2890 },
2891 calculateTotal: function(data){
2892 this.total = 0;
2893 helpers.each(data,function(segment){
2894 this.total += segment.value;
2895 },this);
2896 this.scale.valuesCount = this.segments.length;
2897 },
2898 updateScaleRange: function(datapoints){
2899 var valuesArray = [];
2900 helpers.each(datapoints,function(segment){
2901 valuesArray.push(segment.value);
2902 });
2903
2904 var scaleSizes = (this.options.scaleOverride) ?
2905 {
2906 steps: this.options.scaleSteps,
2907 stepValue: this.options.scaleStepWidth,
2908 min: this.options.scaleStartValue,
2909 max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
2910 } :
2911 helpers.calculateScaleRange(
2912 valuesArray,
2913 helpers.min([this.chart.width, this.chart.height])/2,
2914 this.options.scaleFontSize,
2915 this.options.scaleBeginAtZero,
2916 this.options.scaleIntegersOnly
2917 );
2918
2919 helpers.extend(
2920 this.scale,
2921 scaleSizes,
2922 {
2923 size: helpers.min([this.chart.width, this.chart.height]),
2924 xCenter: this.chart.width/2,
2925 yCenter: this.chart.height/2
2926 }
2927 );
2928
2929 },
2930 update : function(){
2931 this.calculateTotal(this.segments);
2932
2933 helpers.each(this.segments,function(segment){
2934 segment.save();
2935 });
2936 this.render();
2937 },
2938 reflow : function(){
2939 helpers.extend(this.SegmentArc.prototype,{
2940 x : this.chart.width/2,
2941 y : this.chart.height/2
2942 });
2943 this.updateScaleRange(this.segments);
2944 this.scale.update();
2945
2946 helpers.extend(this.scale,{
2947 xCenter: this.chart.width/2,
2948 yCenter: this.chart.height/2
2949 });
2950
2951 helpers.each(this.segments, function(segment){
2952 segment.update({
2953 outerRadius : this.scale.calculateCenterOffset(segment.value)
2954 });
2955 }, this);
2956
2957 },
2958 draw : function(ease){
2959 var easingDecimal = ease || 1;
2960 //Clear & draw the canvas
2961 this.clear();
2962 helpers.each(this.segments,function(segment, index){
2963 segment.transition({
2964 circumference : this.scale.getCircumference(),
2965 outerRadius : this.scale.calculateCenterOffset(segment.value)
2966 },easingDecimal);
2967
2968 segment.endAngle = segment.startAngle + segment.circumference;
2969
2970 // If we've removed the first segment we need to set the first one to
2971 // start at the top.
2972 if (index === 0){
2973 segment.startAngle = Math.PI * 1.5;
2974 }
2975
2976 //Check to see if it's the last segment, if not get the next and update the start angle
2977 if (index < this.segments.length - 1){
2978 this.segments[index+1].startAngle = segment.endAngle;
2979 }
2980 segment.draw();
2981 }, this);
2982 this.scale.draw();
2983 }
2984 });
2985
2986 }).call(this);
2987 (function(){
2988 "use strict";
2989
2990 var root = this,
2991 Chart = root.Chart,
2992 helpers = Chart.helpers;
2993
2994
2995
2996 Chart.Type.extend({
2997 name: "Radar",
2998 defaults:{
2999 //Boolean - Whether to show lines for each scale point
3000 scaleShowLine : true,
3001
3002 //Boolean - Whether we show the angle lines out of the radar
3003 angleShowLineOut : true,
3004
3005 //Boolean - Whether to show labels on the scale
3006 scaleShowLabels : false,
3007
3008 // Boolean - Whether the scale should begin at zero
3009 scaleBeginAtZero : true,
3010
3011 //String - Colour of the angle line
3012 angleLineColor : "rgba(0,0,0,.1)",
3013
3014 //Number - Pixel width of the angle line
3015 angleLineWidth : 1,
3016
3017 //String - Point label font declaration
3018 pointLabelFontFamily : "'Arial'",
3019
3020 //String - Point label font weight
3021 pointLabelFontStyle : "normal",
3022
3023 //Number - Point label font size in pixels
3024 pointLabelFontSize : 10,
3025
3026 //String - Point label font colour
3027 pointLabelFontColor : "#666",
3028
3029 //Boolean - Whether to show a dot for each point
3030 pointDot : true,
3031
3032 //Number - Radius of each point dot in pixels
3033 pointDotRadius : 3,
3034
3035 //Number - Pixel width of point dot stroke
3036 pointDotStrokeWidth : 1,
3037
3038 //Number - amount extra to add to the radius to cater for hit detection outside the drawn point
3039 pointHitDetectionRadius : 20,
3040
3041 //Boolean - Whether to show a stroke for datasets
3042 datasetStroke : true,
3043
3044 //Number - Pixel width of dataset stroke
3045 datasetStrokeWidth : 2,
3046
3047 //Boolean - Whether to fill the dataset with a colour
3048 datasetFill : true,
3049
3050 //String - A legend template
3051 legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
3052
3053 },
3054
3055 initialize: function(data){
3056 this.PointClass = Chart.Point.extend({
3057 strokeWidth : this.options.pointDotStrokeWidth,
3058 radius : this.options.pointDotRadius,
3059 display: this.options.pointDot,
3060 hitDetectionRadius : this.options.pointHitDetectionRadius,
3061 ctx : this.chart.ctx
3062 });
3063
3064 this.datasets = [];
3065
3066 this.buildScale(data);
3067
3068 //Set up tooltip events on the chart
3069 if (this.options.showTooltips){
3070 helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
3071 var activePointsCollection = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
3072
3073 this.eachPoints(function(point){
3074 point.restore(['fillColor', 'strokeColor']);
3075 });
3076 helpers.each(activePointsCollection, function(activePoint){
3077 activePoint.fillColor = activePoint.highlightFill;
3078 activePoint.strokeColor = activePoint.highlightStroke;
3079 });
3080
3081 this.showTooltip(activePointsCollection);
3082 });
3083 }
3084
3085 //Iterate through each of the datasets, and build this into a property of the chart
3086 helpers.each(data.datasets,function(dataset){
3087
3088 var datasetObject = {
3089 label: dataset.label || null,
3090 fillColor : dataset.fillColor,
3091 strokeColor : dataset.strokeColor,
3092 pointColor : dataset.pointColor,
3093 pointStrokeColor : dataset.pointStrokeColor,
3094 points : []
3095 };
3096
3097 this.datasets.push(datasetObject);
3098
3099 helpers.each(dataset.data,function(dataPoint,index){
3100 //Best way to do this? or in draw sequence...?
3101 if (helpers.isNumber(dataPoint)){
3102 //Add a new point for each piece of data, passing any required data to draw.
3103 var pointPosition;
3104 if (!this.scale.animation){
3105 pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(dataPoint));
3106 }
3107 datasetObject.points.push(new this.PointClass({
3108 value : dataPoint,
3109 label : data.labels[index],
3110 datasetLabel: dataset.label,
3111 x: (this.options.animation) ? this.scale.xCenter : pointPosition.x,
3112 y: (this.options.animation) ? this.scale.yCenter : pointPosition.y,
3113 strokeColor : dataset.pointStrokeColor,
3114 fillColor : dataset.pointColor,
3115 highlightFill : dataset.pointHighlightFill || dataset.pointColor,
3116 highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
3117 }));
3118 }
3119 },this);
3120
3121 },this);
3122
3123 this.render();
3124 },
3125 eachPoints : function(callback){
3126 helpers.each(this.datasets,function(dataset){
3127 helpers.each(dataset.points,callback,this);
3128 },this);
3129 },
3130
3131 getPointsAtEvent : function(evt){
3132 var mousePosition = helpers.getRelativePosition(evt),
3133 fromCenter = helpers.getAngleFromPoint({
3134 x: this.scale.xCenter,
3135 y: this.scale.yCenter
3136 }, mousePosition);
3137
3138 var anglePerIndex = (Math.PI * 2) /this.scale.valuesCount,
3139 pointIndex = Math.round((fromCenter.angle - Math.PI * 1.5) / anglePerIndex),
3140 activePointsCollection = [];
3141
3142 // If we're at the top, make the pointIndex 0 to get the first of the array.
3143 if (pointIndex >= this.scale.valuesCount || pointIndex < 0){
3144 pointIndex = 0;
3145 }
3146
3147 if (fromCenter.distance <= this.scale.drawingArea){
3148 helpers.each(this.datasets, function(dataset){
3149 activePointsCollection.push(dataset.points[pointIndex]);
3150 });
3151 }
3152
3153 return activePointsCollection;
3154 },
3155
3156 buildScale : function(data){
3157 this.scale = new Chart.RadialScale({
3158 display: this.options.showScale,
3159 fontStyle: this.options.scaleFontStyle,
3160 fontSize: this.options.scaleFontSize,
3161 fontFamily: this.options.scaleFontFamily,
3162 fontColor: this.options.scaleFontColor,
3163 showLabels: this.options.scaleShowLabels,
3164 showLabelBackdrop: this.options.scaleShowLabelBackdrop,
3165 backdropColor: this.options.scaleBackdropColor,
3166 backdropPaddingY : this.options.scaleBackdropPaddingY,
3167 backdropPaddingX: this.options.scaleBackdropPaddingX,
3168 lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,
3169 lineColor: this.options.scaleLineColor,
3170 angleLineColor : this.options.angleLineColor,
3171 angleLineWidth : (this.options.angleShowLineOut) ? this.options.angleLineWidth : 0,
3172 // Point labels at the edge of each line
3173 pointLabelFontColor : this.options.pointLabelFontColor,
3174 pointLabelFontSize : this.options.pointLabelFontSize,
3175 pointLabelFontFamily : this.options.pointLabelFontFamily,
3176 pointLabelFontStyle : this.options.pointLabelFontStyle,
3177 height : this.chart.height,
3178 width: this.chart.width,
3179 xCenter: this.chart.width/2,
3180 yCenter: this.chart.height/2,
3181 ctx : this.chart.ctx,
3182 templateString: this.options.scaleLabel,
3183 labels: data.labels,
3184 valuesCount: data.datasets[0].data.length
3185 });
3186
3187 this.scale.setScaleSize();
3188 this.updateScaleRange(data.datasets);
3189 this.scale.buildYLabels();
3190 },
3191 updateScaleRange: function(datasets){
3192 var valuesArray = (function(){
3193 var totalDataArray = [];
3194 helpers.each(datasets,function(dataset){
3195 if (dataset.data){
3196 totalDataArray = totalDataArray.concat(dataset.data);
3197 }
3198 else {
3199 helpers.each(dataset.points, function(point){
3200 totalDataArray.push(point.value);
3201 });
3202 }
3203 });
3204 return totalDataArray;
3205 })();
3206
3207
3208 var scaleSizes = (this.options.scaleOverride) ?
3209 {
3210 steps: this.options.scaleSteps,
3211 stepValue: this.options.scaleStepWidth,
3212 min: this.options.scaleStartValue,
3213 max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
3214 } :
3215 helpers.calculateScaleRange(
3216 valuesArray,
3217 helpers.min([this.chart.width, this.chart.height])/2,
3218 this.options.scaleFontSize,
3219 this.options.scaleBeginAtZero,
3220 this.options.scaleIntegersOnly
3221 );
3222
3223 helpers.extend(
3224 this.scale,
3225 scaleSizes
3226 );
3227
3228 },
3229 addData : function(valuesArray,label){
3230 //Map the values array for each of the datasets
3231 this.scale.valuesCount++;
3232 helpers.each(valuesArray,function(value,datasetIndex){
3233 if (helpers.isNumber(value)){
3234 var pointPosition = this.scale.getPointPosition(this.scale.valuesCount, this.scale.calculateCenterOffset(value));
3235 this.datasets[datasetIndex].points.push(new this.PointClass({
3236 value : value,
3237 label : label,
3238 x: pointPosition.x,
3239 y: pointPosition.y,
3240 strokeColor : this.datasets[datasetIndex].pointStrokeColor,
3241 fillColor : this.datasets[datasetIndex].pointColor
3242 }));
3243 }
3244 },this);
3245
3246 this.scale.labels.push(label);
3247
3248 this.reflow();
3249
3250 this.update();
3251 },
3252 removeData : function(){
3253 this.scale.valuesCount--;
3254 this.scale.labels.shift();
3255 helpers.each(this.datasets,function(dataset){
3256 dataset.points.shift();
3257 },this);
3258 this.reflow();
3259 this.update();
3260 },
3261 update : function(){
3262 this.eachPoints(function(point){
3263 point.save();
3264 });
3265 this.reflow();
3266 this.render();
3267 },
3268 reflow: function(){
3269 helpers.extend(this.scale, {
3270 width : this.chart.width,
3271 height: this.chart.height,
3272 size : helpers.min([this.chart.width, this.chart.height]),
3273 xCenter: this.chart.width/2,
3274 yCenter: this.chart.height/2
3275 });
3276 this.updateScaleRange(this.datasets);
3277 this.scale.setScaleSize();
3278 this.scale.buildYLabels();
3279 },
3280 draw : function(ease){
3281 var easeDecimal = ease || 1,
3282 ctx = this.chart.ctx;
3283 this.clear();
3284 this.scale.draw();
3285
3286 helpers.each(this.datasets,function(dataset){
3287
3288 //Transition each point first so that the line and point drawing isn't out of sync
3289 helpers.each(dataset.points,function(point,index){
3290 point.transition(this.scale.getPointPosition(index, this.scale.calculateCenterOffset(point.value)), easeDecimal);
3291 },this);
3292
3293
3294
3295 //Draw the line between all the points
3296 ctx.lineWidth = this.options.datasetStrokeWidth;
3297 ctx.strokeStyle = dataset.strokeColor;
3298 ctx.beginPath();
3299 helpers.each(dataset.points,function(point,index){
3300 if (index === 0){
3301 ctx.moveTo(point.x,point.y);
3302 }
3303 else{
3304 ctx.lineTo(point.x,point.y);
3305 }
3306 },this);
3307 ctx.closePath();
3308 ctx.stroke();
3309
3310 ctx.fillStyle = dataset.fillColor;
3311 ctx.fill();
3312
3313 //Now draw the points over the line
3314 //A little inefficient double looping, but better than the line
3315 //lagging behind the point positions
3316 helpers.each(dataset.points,function(point){
3317 point.draw();
3318 });
3319
3320 },this);
3321
3322 }
3323
3324 });
3325
3326
3327
3328
3329
3330 }).call(this);
0 .chart-legend,.bar-legend,.line-legend,.pie-legend,.radar-legend,.polararea-legend,.doughnut-legend{list-style-type:none;margin-top:5px;text-align:center;-webkit-padding-start:0;-moz-padding-start:0;padding-left:0}.chart-legend li,.bar-legend li,.line-legend li,.pie-legend li,.radar-legend li,.polararea-legend li,.doughnut-legend li{display:inline-block;white-space:nowrap;position:relative;margin-bottom:4px;border-radius:5px;padding:2px 8px 2px 28px;font-size:smaller;cursor:default}.chart-legend li span,.bar-legend li span,.line-legend li span,.pie-legend li span,.radar-legend li span,.polararea-legend li span,.doughnut-legend li span{display:block;position:absolute;left:0;top:0;width:20px;height:20px;border-radius:5px}
1 /*# sourceMappingURL=angular-chart.css.map */
0 {"version":3,"sources":["angular-chart.less"],"names":[],"mappings":"AAAA;AAAe;AAAa;AAAc;AAAa;AAAe;AAAmB;EACvF,qBAAA;EACA,eAAA;EACA,kBAAA;;EAEA,wBAAA;;EACA,qBAAA;;EACA,eAAA;;;AAPF,aASE;AATa,WASb;AAT0B,YAS1B;AATwC,WASxC;AATqD,aASrD;AAToE,iBASpE;AATuF,gBASvF;EACE,qBAAA;EACA,mBAAA;EACA,kBAAA;EACA,kBAAA;EACA,kBAAA;EACA,yBAAA;EACA,kBAAA;EACA,eAAA;;AAjBJ,aASE,GAUE;AAnBW,WASb,GAUE;AAnBwB,YAS1B,GAUE;AAnBsC,WASxC,GAUE;AAnBmD,aASrD,GAUE;AAnBkE,iBASpE,GAUE;AAnBqF,gBASvF,GAUE;EACE,cAAA;EACA,kBAAA;EACA,OAAA;EACA,MAAA;EACA,WAAA;EACA,YAAA;EACA,kBAAA","file":"angular-chart.css","sourcesContent":[".chart-legend, .bar-legend, .line-legend, .pie-legend, .radar-legend, .polararea-legend, .doughnut-legend {\n list-style-type: none;\n margin-top: 5px;\n text-align: center;\n /* NOTE: Browsers automatically add 40px of padding-left to all lists, so we should offset that, otherwise the legend is off-center */\n -webkit-padding-start:0; /* Webkit */\n -moz-padding-start:0; /* Mozilla */\n padding-left:0; /* IE (handles all cases, really, but we should also include the vendor-specific properties just to be safe) */\n\n li {\n display: inline-block;\n white-space: nowrap;\n position: relative;\n margin-bottom: 4px;\n border-radius: 5px;\n padding: 2px 8px 2px 28px;\n font-size: smaller;\n cursor: default;\n\n span {\n display: block;\n position: absolute;\n left: 0;\n top: 0;\n width: 20px;\n height: 20px;\n border-radius: 5px;\n }\n }\n}\n"],"sourceRoot":"/source/"}
0 (function (factory) {
1 'use strict';
2 if (typeof define === 'function' && define.amd) {
3 // AMD. Register as an anonymous module.
4 define(['angular', 'chart'], factory);
5 } else if (typeof exports === 'object') {
6 // Node/CommonJS
7 module.exports = factory(require('angular'), require('chart.js'));
8 } else {
9 // Browser globals
10 factory(angular, Chart);
11 }
12 }(function (angular, Chart) {
13 'use strict';
14
15 Chart.defaults.global.responsive = true;
16 Chart.defaults.global.multiTooltipTemplate = '<%if (datasetLabel){%><%=datasetLabel%>: <%}%><%= value %>';
17
18 Chart.defaults.global.colours = [
19 '#97BBCD', // blue
20 '#DCDCDC', // light grey
21 '#F7464A', // red
22 '#46BFBD', // green
23 '#FDB45C', // yellow
24 '#949FB1', // grey
25 '#4D5360' // dark grey
26 ];
27
28 var usingExcanvas = typeof window.G_vmlCanvasManager === 'object' &&
29 window.G_vmlCanvasManager !== null &&
30 typeof window.G_vmlCanvasManager.initElement === 'function';
31
32 if (usingExcanvas) Chart.defaults.global.animation = false;
33
34 return angular.module('chart.js', [])
35 .provider('ChartJs', ChartJsProvider)
36 .factory('ChartJsFactory', ['ChartJs', '$timeout', ChartJsFactory])
37 .directive('chartBase', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory(); }])
38 .directive('chartLine', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('Line'); }])
39 .directive('chartBar', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('Bar'); }])
40 .directive('chartRadar', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('Radar'); }])
41 .directive('chartDoughnut', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('Doughnut'); }])
42 .directive('chartPie', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('Pie'); }])
43 .directive('chartPolarArea', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('PolarArea'); }]);
44
45 /**
46 * Wrapper for chart.js
47 * Allows configuring chart js using the provider
48 *
49 * angular.module('myModule', ['chart.js']).config(function(ChartJsProvider) {
50 * ChartJsProvider.setOptions({ responsive: true });
51 * ChartJsProvider.setOptions('Line', { responsive: false });
52 * })))
53 */
54 function ChartJsProvider () {
55 var options = {};
56 var ChartJs = {
57 Chart: Chart,
58 getOptions: function (type) {
59 var typeOptions = type && options[type] || {};
60 return angular.extend({}, options, typeOptions);
61 }
62 };
63
64 /**
65 * Allow to set global options during configuration
66 */
67 this.setOptions = function (type, customOptions) {
68 // If no type was specified set option for the global object
69 if (! customOptions) {
70 customOptions = type;
71 options = angular.extend(options, customOptions);
72 return;
73 }
74 // Set options for the specific chart
75 options[type] = angular.extend(options[type] || {}, customOptions);
76 };
77
78 this.$get = function () {
79 return ChartJs;
80 };
81 }
82
83 function ChartJsFactory (ChartJs, $timeout) {
84 return function chart (type) {
85 return {
86 restrict: 'CA',
87 scope: {
88 data: '=?',
89 labels: '=?',
90 options: '=?',
91 series: '=?',
92 colours: '=?',
93 getColour: '=?',
94 chartType: '=',
95 legend: '@',
96 click: '=?',
97 hover: '=?',
98
99 chartData: '=?',
100 chartLabels: '=?',
101 chartOptions: '=?',
102 chartSeries: '=?',
103 chartColours: '=?',
104 chartLegend: '@',
105 chartClick: '=?',
106 chartHover: '=?'
107 },
108 link: function (scope, elem/*, attrs */) {
109 var chart, container = document.createElement('div');
110 container.className = 'chart-container';
111 elem.replaceWith(container);
112 container.appendChild(elem[0]);
113
114 if (usingExcanvas) window.G_vmlCanvasManager.initElement(elem[0]);
115
116 ['data', 'labels', 'options', 'series', 'colours', 'legend', 'click', 'hover'].forEach(deprecated);
117 function aliasVar (fromName, toName) {
118 scope.$watch(fromName, function (newVal) {
119 if (typeof newVal === 'undefined') return;
120 scope[toName] = newVal;
121 });
122 }
123 /* provide backward compatibility to "old" directive names, by
124 * having an alias point from the new names to the old names. */
125 aliasVar('chartData', 'data');
126 aliasVar('chartLabels', 'labels');
127 aliasVar('chartOptions', 'options');
128 aliasVar('chartSeries', 'series');
129 aliasVar('chartColours', 'colours');
130 aliasVar('chartLegend', 'legend');
131 aliasVar('chartClick', 'click');
132 aliasVar('chartHover', 'hover');
133
134 // Order of setting "watch" matter
135
136 scope.$watch('data', function (newVal, oldVal) {
137 if (! newVal || ! newVal.length || (Array.isArray(newVal[0]) && ! newVal[0].length)) return;
138 var chartType = type || scope.chartType;
139 if (! chartType) return;
140
141 if (chart) {
142 if (canUpdateChart(newVal, oldVal)) return updateChart(chart, newVal, scope, elem);
143 chart.destroy();
144 }
145
146 createChart(chartType);
147 }, true);
148
149 scope.$watch('series', resetChart, true);
150 scope.$watch('labels', resetChart, true);
151 scope.$watch('options', resetChart, true);
152 scope.$watch('colours', resetChart, true);
153
154 scope.$watch('chartType', function (newVal, oldVal) {
155 if (isEmpty(newVal)) return;
156 if (angular.equals(newVal, oldVal)) return;
157 if (chart) chart.destroy();
158 createChart(newVal);
159 });
160
161 scope.$on('$destroy', function () {
162 if (chart) chart.destroy();
163 });
164
165 function resetChart (newVal, oldVal) {
166 if (isEmpty(newVal)) return;
167 if (angular.equals(newVal, oldVal)) return;
168 var chartType = type || scope.chartType;
169 if (! chartType) return;
170
171 // chart.update() doesn't work for series and labels
172 // so we have to re-create the chart entirely
173 if (chart) chart.destroy();
174
175 createChart(chartType);
176 }
177
178 function createChart (type) {
179 if (isResponsive(type, scope) && elem[0].clientHeight === 0 && container.clientHeight === 0) {
180 return $timeout(function () {
181 createChart(type);
182 }, 50);
183 }
184 if (! scope.data || ! scope.data.length) return;
185 scope.getColour = typeof scope.getColour === 'function' ? scope.getColour : getRandomColour;
186 scope.colours = getColours(type, scope);
187 var cvs = elem[0], ctx = cvs.getContext('2d');
188 var data = Array.isArray(scope.data[0]) ?
189 getDataSets(scope.labels, scope.data, scope.series || [], scope.colours) :
190 getData(scope.labels, scope.data, scope.colours);
191 var options = angular.extend({}, ChartJs.getOptions(type), scope.options);
192 chart = new ChartJs.Chart(ctx)[type](data, options);
193 scope.$emit('create', chart);
194
195 ['hover', 'click'].forEach(function (action) {
196 if (scope[action])
197 cvs[action === 'click' ? 'onclick' : 'onmousemove'] = getEventHandler(scope, chart, action);
198 });
199 if (scope.legend && scope.legend !== 'false') setLegend(elem, chart);
200 }
201
202 function deprecated (attr) {
203 if (typeof console !== 'undefined' && ChartJs.getOptions().env !== 'test') {
204 var warn = typeof console.warn === 'function' ? console.warn : console.log;
205 if (!! scope[attr]) {
206 warn.call(console, '"%s" is deprecated and will be removed in a future version. ' +
207 'Please use "chart-%s" instead.', attr, attr);
208 }
209 }
210 }
211 }
212 };
213 };
214
215 function canUpdateChart (newVal, oldVal) {
216 if (newVal && oldVal && newVal.length && oldVal.length) {
217 return Array.isArray(newVal[0]) ?
218 newVal.length === oldVal.length && newVal.every(function (element, index) {
219 return element.length === oldVal[index].length; }) :
220 oldVal.reduce(sum, 0) > 0 ? newVal.length === oldVal.length : false;
221 }
222 return false;
223 }
224
225 function sum (carry, val) {
226 return carry + val;
227 }
228
229 function getEventHandler (scope, chart, action) {
230 return function (evt) {
231 var atEvent = chart.getPointsAtEvent || chart.getBarsAtEvent || chart.getSegmentsAtEvent;
232 if (atEvent) {
233 var activePoints = atEvent.call(chart, evt);
234 scope[action](activePoints, evt);
235 scope.$apply();
236 }
237 };
238 }
239
240 function getColours (type, scope) {
241 var colours = angular.copy(scope.colours ||
242 ChartJs.getOptions(type).colours ||
243 Chart.defaults.global.colours
244 );
245 while (colours.length < scope.data.length) {
246 colours.push(scope.getColour());
247 }
248 return colours.map(convertColour);
249 }
250
251 function convertColour (colour) {
252 if (typeof colour === 'object' && colour !== null) return colour;
253 if (typeof colour === 'string' && colour[0] === '#') return getColour(hexToRgb(colour.substr(1)));
254 return getRandomColour();
255 }
256
257 function getRandomColour () {
258 var colour = [getRandomInt(0, 255), getRandomInt(0, 255), getRandomInt(0, 255)];
259 return getColour(colour);
260 }
261
262 function getColour (colour) {
263 return {
264 fillColor: rgba(colour, 0.2),
265 strokeColor: rgba(colour, 1),
266 pointColor: rgba(colour, 1),
267 pointStrokeColor: '#fff',
268 pointHighlightFill: '#fff',
269 pointHighlightStroke: rgba(colour, 0.8)
270 };
271 }
272
273 function getRandomInt (min, max) {
274 return Math.floor(Math.random() * (max - min + 1)) + min;
275 }
276
277 function rgba (colour, alpha) {
278 if (usingExcanvas) {
279 // rgba not supported by IE8
280 return 'rgb(' + colour.join(',') + ')';
281 } else {
282 return 'rgba(' + colour.concat(alpha).join(',') + ')';
283 }
284 }
285
286 // Credit: http://stackoverflow.com/a/11508164/1190235
287 function hexToRgb (hex) {
288 var bigint = parseInt(hex, 16),
289 r = (bigint >> 16) & 255,
290 g = (bigint >> 8) & 255,
291 b = bigint & 255;
292
293 return [r, g, b];
294 }
295
296 function getDataSets (labels, data, series, colours) {
297 return {
298 labels: labels,
299 datasets: data.map(function (item, i) {
300 return angular.extend({}, colours[i], {
301 label: series[i],
302 data: item
303 });
304 })
305 };
306 }
307
308 function getData (labels, data, colours) {
309 return labels.map(function (label, i) {
310 return angular.extend({}, colours[i], {
311 label: label,
312 value: data[i],
313 color: colours[i].strokeColor,
314 highlight: colours[i].pointHighlightStroke
315 });
316 });
317 }
318
319 function setLegend (elem, chart) {
320 var $parent = elem.parent(),
321 $oldLegend = $parent.find('chart-legend'),
322 legend = '<chart-legend>' + chart.generateLegend() + '</chart-legend>';
323 if ($oldLegend.length) $oldLegend.replaceWith(legend);
324 else $parent.append(legend);
325 }
326
327 function updateChart (chart, values, scope, elem) {
328 if (Array.isArray(scope.data[0])) {
329 chart.datasets.forEach(function (dataset, i) {
330 (dataset.points || dataset.bars).forEach(function (dataItem, j) {
331 dataItem.value = values[i][j];
332 });
333 });
334 } else {
335 chart.segments.forEach(function (segment, i) {
336 segment.value = values[i];
337 });
338 }
339 chart.update();
340 scope.$emit('update', chart);
341 if (scope.legend && scope.legend !== 'false') setLegend(elem, chart);
342 }
343
344 function isEmpty (value) {
345 return ! value ||
346 (Array.isArray(value) && ! value.length) ||
347 (typeof value === 'object' && ! Object.keys(value).length);
348 }
349
350 function isResponsive (type, scope) {
351 var options = angular.extend({}, Chart.defaults.global, ChartJs.getOptions(type), scope.options);
352 return options.responsive;
353 }
354 }
355 }));
77 async: false
88 });
99
10 var faradayApp = angular.module('faradayApp', ['ngRoute', 'selectionModel', 'ui.bootstrap', 'angularFileUpload', 'filter', 'ngClipboard', 'ngCookies', 'cfp.hotkeys'])
10 var faradayApp = angular.module('faradayApp', ['ngRoute', 'selectionModel', 'ui.bootstrap', 'angularFileUpload', 'filter', 'ngClipboard', 'ngCookies', 'cfp.hotkeys', 'chart.js'])
1111 .constant("BASEURL", (function() {
1212 var url = window.location.origin + "/";
1313 return url;
5252 controller: 'workspacesCtrl',
5353 title: 'Dashboard | '
5454 }).
55 when('/hosts/ws/:wsId/search/:search', {
56 templateUrl: 'scripts/hosts/partials/list.html',
57 controller: 'hostsCtrl',
58 title: 'Hosts | '
59 }).
60 when('/hosts/ws/:wsId/search', {
61 templateUrl: 'scripts/hosts/partials/list.html',
62 controller: 'hostsCtrl',
63 title: 'Hosts | '
64 }).
5565 when('/hosts/ws/:wsId', {
5666 templateUrl: 'scripts/hosts/partials/list.html',
5767 controller: 'hostsCtrl',
6171 templateUrl: 'scripts/commons/partials/workspaces.html',
6272 controller: 'workspacesCtrl',
6373 title: 'Hosts | '
74 }).
75 when('/host/ws/:wsId/hid/:hidId/search/:search', {
76 templateUrl: 'scripts/services/partials/list.html',
77 controller: 'hostCtrl',
78 title: 'Services | '
79 }).
80 when('/host/ws/:wsId/hid/:hidId/search', {
81 templateUrl: 'scripts/services/partials/list.html',
82 controller: 'hostCtrl',
83 title: 'Services | '
6484 }).
6585 when('/hosts', {
6686 templateUrl: 'scripts/commons/partials/workspaces.html',
102122 controller: 'workspacesCtrl',
103123 title: 'Workspaces | '
104124 }).
125 when('/communication', {
126 templateUrl: 'scripts/commons/partials/commercial.html',
127 controller: 'commercialCtrl',
128 title: 'Communication | '
129 }).
130 when('/comparison', {
131 templateUrl: 'scripts/commons/partials/commercial.html',
132 controller: 'commercialCtrl'
133 }).
134 when('/webshell', {
135 templateUrl: 'scripts/commons/partials/commercial.html',
136 controller: 'commercialCtrl'
137 }).
138 when('/executive', {
139 templateUrl: 'scripts/commons/partials/commercial.html',
140 controller: 'commercialCtrl',
141 title: 'Executive Report | '
142 }).
143 when('/users', {
144 templateUrl: 'scripts/commons/partials/commercial.html',
145 controller: 'commercialCtrl',
146 title: 'Users | '
147 }).
105148 otherwise({
106149 templateUrl: 'scripts/commons/partials/home.html',
107150 controller: 'statusReportCtrl'
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('commercialCtrl',
6 ['$scope', '$location',
7 function($scope, $location) {
8 if ($location.path().split("/")[1] === "executive") {
9 $scope.header = "executive report";
10 } else if ($location.path().split("/")[1] === "comparison") {
11 $scope.header = "workspace comparison";
12 } else if ($location.path().split("/")[1] === "communication") {
13 $scope.header = "chat";
14 } else {
15 $scope.header = $location.path().split("/")[1];
16 }
17 }]);
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <section id="main" class="seccion clearfix">
5 <div class="right-main"><div id="reports-main" class="fila clearfix">
6 <div class="jumbotron" ng-class="{ 'jumbotron-font':header === 'workspace comparison' }">
7 <h1><b>Welcome to the <span style="text-transform: capitalize;">{{header}}</span> panel!</b></h1>
8 <p>This feature belongs to our commercial versions</p>
9 <p>For more information, please contact us at <span class="bold">[email protected]</span> or visit <a href="http://www.faradaysec.com">www.faradaysec.com</a> !</p>
10 </div><!-- .jumbotron -->
11 </div><!-- #reports-main --></div><!-- .right-main -->
12 </section><!-- #main -->
99 </h2><!-- .ws-label -->
1010 <div class="reports">
1111 <div class="reports">
12 <div class="ws-list home-list community clearfix">
12 <div class="ws-list home-list corporate clearfix">
1313 <a href="#/dashboard" class="ws-link item animated flipInX">
1414 <img src="images/ico-dashboard.svg" />
1515 <span class="ws-name">Dashboard</span>
4242 <strong>Manage your hosts</strong>
4343 </small>
4444 </a>
45 <a href="#/users" class="ws-link item animated flipInX">
46 <img src="images/ico-users.svg" />
47 <span class="ws-name">Users</span>
48 <small>
49 Create and edit members.<br/>
50 <strong>Manage your Team</strong>
51 </small>
52 </a>
53 <a href="#/executive" class="ws-link item animated flipInX">
54 <img src="images/ico-executive.svg" />
55 <span class="ws-name">Executive Report</span>
56 <small>
57 Export project to a word file.<br/>
58 <strong>Manage reports</strong>
59 </small>
60 </a>
61 <a href="#/communication" class="ws-link item animated flipInX">
62 <img src="images/ico-communication.svg" />
63 <span class="ws-name">Chat</span>
64 <small>
65 Share information with other users.<br/>
66 <strong>Join the conversation</strong>
67 </small>
68 </a>
69 <a href="#/comparison" class="ws-link item animated flipInX">
70 <img src="images/ico-workspace-comparison.svg" />
71 <span class="ws-name">Workspace Comparison</span>
72 <small>
73 Compare two workspaces.<br/>
74 <strong>Differences between projects</strong>
75 </small>
76 </a>
77 <a href="#/webshell" class="ws-link item animated flipInX">
78 <img src="images/ico-web-shell.svg" />
79 <span class="ws-name">Web Shell</span>
80 <small>
81 Run commands directly from your.<br/>
82 <strong>UI Web</strong>
83 </small>
84 </a>
4585 </div><!-- .ws-list -->
4686 </div><!-- .reports -->
4787 </div><!-- #reports-main --></div><!-- .right-main -->
0 # Faraday Penetration Test IDE
1 # Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 # See the file 'doc/LICENSE' for the license information
3
4 # Ignore config file
5 config.json
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .factory('configSrv', ['BASEURL', '$http', function(BASEURL, $http) {
6
7 var p = $http.get('/reports/_design/reports/scripts/config/config.json').then(function(conf) {
8 configSrv.faraday_version = conf.data.ver;
9 });
10
11 configSrv = {
12 faraday_version: null,
13 promise: p
14 }
15
16 return configSrv;
17 }]);
156156
157157 // used to create pie chart for vulns
158158 $scope.vulnsCountClass = {"children": angular.copy(tmp)};
159 $scope.doughnut = {key: [], value: [], colors: [], options: {maintainAspectRatio: false}};
159160 for(var i = 0; i < $scope.vulnsCountClass.children.length; i++) {
160161 if($scope.vulnsCountClass.children[i].key == "unclassified") {
161162 $scope.vulnsCountClass.children.splice(i, 1);
162163 break;
163164 }
165 $scope.doughnut.key.push($scope.vulnsCountClass.children[i].key);
166 $scope.doughnut.value.push($scope.vulnsCountClass.children[i].value);
167 $scope.doughnut.colors.push($scope.vulnsCountClass.children[i].color);
164168 };
165169
166170 $scope.$watch('vulnPrices', function(ps) {
216220 return b.value-a.value;
217221 });
218222 var colors = ["rgb(57, 59, 121)","rgb(82, 84, 163)","rgb(107, 110, 207)"];
219 var tmp = [];
223 var tmp = {key:[], colors:[], value:[]};
224 tmp.options = {
225 showScale : false,
226 maintainAspectRatio: false
227 };
220228 servicesCount.slice(0, 3).forEach(function(srv) {
221 srv.color = colors.shift();
222 tmp.push(srv);
229 tmp.colors.push(colors.shift());
230 tmp.value.push(srv.value);
231 tmp.key.push(host.name);
223232 });
224233 $scope.topHosts = tmp;
225234 }
0 <canvas id="bar" class="chart chart-bar" chart-data="[topHosts.value]" chart-labels="topHosts.key" chart-series="topHosts.key" chart-legend="false" chart-options="topHosts.options"></canvas>
0 <canvas id="doughnut" class="chart chart-doughnut" chart-data="doughnut.value" chart-labels="doughnut.key" chart-colours="doughnut.colors" chart-options="doughnut.options"></canvas>
3535 </button>
3636 <p>At least 3 hosts needed to show this visualization</p>
3737 </div>
38 <d3-bars data="topHosts"></d3-bars>
38 <div id="bar" ng-include="'scripts/dashboard/partials/barChart.html'" ng-if="topHosts != undefined || topHosts.length > 3"></div>
3939 </article>
4040 </div>
4141 <div class='col-lg-2'>
5353 </button>
5454 <p>No vulnerabilities found yet</p>
5555 </div>
56 <d3-cake data="vulnsCountClass"></d3-cake>
56 <div id="doughnut" ng-include="'scripts/dashboard/partials/doughnut.html'" ng-if="vulnsCountClass != undefined || vulnsCountClass.length != 0"></div>
5757 </article>
5858 </div>
33
44 angular.module('faradayApp')
55 .controller('hostCtrl',
6 ['$scope', '$filter', '$route', '$routeParams', '$modal', 'hostsManager', 'workspacesFact', 'dashboardSrv', 'servicesManager',
7 function($scope, $filter, $route, $routeParams, $modal, hostsManager, workspacesFact, dashboardSrv, servicesManager) {
6 ['$scope', '$cookies', '$filter', '$location', '$route', '$routeParams', '$modal', 'hostsManager', 'workspacesFact', 'dashboardSrv', 'servicesManager',
7 function($scope, $cookies, $filter, $location, $route, $routeParams, $modal, hostsManager, workspacesFact, dashboardSrv, servicesManager) {
88
99 init = function() {
1010 $scope.selectall = false;
1212 $scope.workspace = $routeParams.wsId;
1313 //ID of current host
1414 var hostId = $routeParams.hidId;
15
16 $scope.sortField = "name";
17
1518 // load all workspaces
1619 workspacesFact.list().then(function(wss) {
1720 $scope.workspaces = wss;
4245 .catch(function(e) {
4346 console.log(e);
4447 });
48
49 $scope.pageSize = 10;
50 $scope.currentPage = 0;
51 $scope.newCurrentPage = 0;
52
53 if(!isNaN(parseInt($cookies.pageSize))) $scope.pageSize = parseInt($cookies.pageSize);
54 $scope.newPageSize = $scope.pageSize;
55
56 // current search
57 $scope.search = $routeParams.search;
58 $scope.searchParams = "";
59 $scope.expression = {};
60 if($scope.search != "" && $scope.search != undefined && $scope.search.indexOf("=") > -1) {
61 // search expression for filter
62 $scope.expression = $scope.decodeSearch($scope.search);
63 // search params for search field, which shouldn't be used for filtering
64 $scope.searchParams = $scope.stringSearch($scope.expression);
65 }
4566 };
67
68 $scope.selectedServices = function() {
69 selected = [];
70
71 tmp_services = filter($scope.services);
72 tmp_services.forEach(function(service) {
73 if(service.selected === true) {
74 selected.push(service);
75 }
76 });
77 return selected;
78 };
79
80 // changes the URL according to search params
81 $scope.searchFor = function(search, params) {
82 var url = "/host/ws/" + $routeParams.wsId + "/hid/" + $routeParams.hidId;
83
84 if(search && params != "" && params != undefined) {
85 url += "/search/" + $scope.encodeSearch(params);
86 }
87
88 $location.path(url);
89 };
90
91 $scope.go = function() {
92 $scope.pageSize = $scope.newPageSize;
93 $cookies.pageSize = $scope.pageSize;
94 $scope.currentPage = 0;
95 if($scope.newCurrentPage <= parseInt($scope.services.length/$scope.pageSize)
96 && $scope.newCurrentPage > -1 && !isNaN(parseInt($scope.newCurrentPage))) {
97 $scope.currentPage = $scope.newCurrentPage;
98 }
99 };
100
101 // encodes search string in order to send it through URL
102 $scope.encodeSearch = function(search) {
103 var i = -1,
104 encode = "",
105 params = search.split(" "),
106 chunks = {};
107
108 params.forEach(function(chunk) {
109 i = chunk.indexOf(":");
110 if(i > 0) {
111 chunks[chunk.slice(0, i)] = chunk.slice(i+1);
112 } else {
113 if(!chunks.hasOwnProperty("free")) {
114 chunks.free = "";
115 }
116 chunks.free += " ".concat(chunk);
117 }
118 });
119
120 if(chunks.hasOwnProperty("free")) {
121 chunks.free = chunks.free.slice(1);
122 }
123
124 for(var prop in chunks) {
125 if(chunks.hasOwnProperty(prop)) {
126 if(chunks.prop != "") {
127 encode += "&" + encodeURIComponent(prop) + "=" + encodeURIComponent(chunks[prop]);
128 }
129 }
130 }
131 return encode.slice(1);
132 };
133
134 // decodes search parameters to object in order to use in filter
135 $scope.decodeSearch = function(search) {
136 var i = -1,
137 decode = {},
138 params = search.split("&");
139
140 params.forEach(function(param) {
141 i = param.indexOf("=");
142 decode[decodeURIComponent(param.slice(0,i))] = decodeURIComponent(param.slice(i+1));
143 });
144
145 if(decode.hasOwnProperty("free")) {
146 decode['$'] = decode.free;
147 delete decode.free;
148 }
149
150 return decode;
151 };
152
153 // converts current search object to string to be displayed in search field
154 $scope.stringSearch = function(obj) {
155 var search = "";
156
157 for(var prop in obj) {
158 if(obj.hasOwnProperty(prop)) {
159 if(search != "") {
160 search += " ";
161 }
162 if(prop == "$") {
163 search += obj[prop];
164 } else {
165 search += prop + ":" + obj[prop];
166 }
167 }
168 }
169
170 return search;
171 };
46172
47173 $scope.new = function() {
48174 var modal = $modal.open({
89215 };
90216
91217 $scope.edit = function() {
92 var selected_service = [];
93
94 $scope.services.forEach(function(service) {
95 if(service.selected) {
96 // if more than one service was selected,
97 // we only use the last one, for now
98 selected_service.push(service);
99 }
100 });
101
102 if(selected_service.length > 0) {
218 if($scope.selectedServices().length > 0) {
103219 var modal = $modal.open({
104220 templateUrl: 'scripts/services/partials/modalEdit.html',
105221 controller: 'serviceModalEdit',
106222 size: 'lg',
107223 resolve: {
108224 service: function() {
109 return selected_service;
225 return $scope.selectedServices();
110226 },
111227 services: function() {
112228 return $scope.services;
115231 });
116232
117233 modal.result.then(function(data) {
118 $scope.update(selected_service, data);
234 $scope.update($scope.selectedServices(), data);
119235 });
120236 } else {
121237 $modal.open(config = {
133249
134250 $scope.delete = function() {
135251 var selected = [];
136 $scope.services.forEach(function(service){
252 $scope.selectedServices().forEach(function(service){
137253 if(service.selected){
138254 selected.push(service._id);
139255 }
190306 });
191307 };
192308
193
194 $scope.checkAll = function() {
195 $scope.selectall = !$scope.selectall;
196
197 angular.forEach($filter('filter')($scope.hosts, $scope.query), function(host) {
198 host.selected = $scope.selectall;
199 });
200 };
201
202309 $scope.checkAllServices = function() {
203310 $scope.selectall = !$scope.selectall;
204311
205 angular.forEach($filter('filter')($scope.services, $scope.query), function(service) {
312 tmp_services = filter($scope.services);
313 tmp_services.forEach(function(service) {
206314 service.selected = $scope.selectall;
207315 });
208316 };
223331 $scope.reverse = !$scope.reverse;
224332 }
225333
334 filter = function(data) {
335 var tmp_data = $filter('orderObjectBy')(data, $scope.sortField, $scope.reverse);
336 tmp_data = $filter('filter')(tmp_data, $scope.expression);
337 tmp_data = tmp_data.splice($scope.pageSize * $scope.currentPage, $scope.pageSize);
338
339 return tmp_data;
340 };
341
226342 init();
227343 }]);
33
44 angular.module('faradayApp')
55 .controller('hostsCtrl',
6 ['$scope', '$filter', '$route', '$routeParams', '$modal', 'hostsManager', 'workspacesFact',
7 function($scope, $filter, $route, $routeParams, $modal, hostsManager, workspacesFact) {
6 ['$scope', '$cookies', '$filter', '$location', '$route', '$routeParams', '$modal', 'hostsManager', 'workspacesFact',
7 function($scope, $cookies, $filter, $location, $route, $routeParams, $modal, hostsManager, workspacesFact) {
88
99 init = function() {
1010 $scope.selectall = false;
1212 $scope.hosts = [];
1313 // current workspace
1414 $scope.workspace = $routeParams.wsId;
15
16 $scope.sortField = "name";
17
1518 // load all workspaces
1619 workspacesFact.list().then(function(wss) {
1720 $scope.workspaces = wss;
3841 .catch(function(e) {
3942 console.log(e);
4043 });
44
45 $scope.pageSize = 10;
46 $scope.currentPage = 0;
47 $scope.newCurrentPage = 0;
48
49 if(!isNaN(parseInt($cookies.pageSize))) $scope.pageSize = parseInt($cookies.pageSize);
50 $scope.newPageSize = $scope.pageSize;
51
52 // current search
53 $scope.search = $routeParams.search;
54 $scope.searchParams = "";
55 $scope.expression = {};
56 if($scope.search != "" && $scope.search != undefined && $scope.search.indexOf("=") > -1) {
57 // search expression for filter
58 $scope.expression = $scope.decodeSearch($scope.search);
59 // search params for search field, which shouldn't be used for filtering
60 $scope.searchParams = $scope.stringSearch($scope.expression);
61 }
4162 };
4263
4364 $scope.loadIcons = function() {
5778 }
5879 });
5980 });
81 };
82
83 // changes the URL according to search params
84 $scope.searchFor = function(search, params) {
85 var url = "/hosts/ws/" + $routeParams.wsId;
86
87 if(search && params != "" && params != undefined) {
88 url += "/search/" + $scope.encodeSearch(params);
89 }
90
91 $location.path(url);
92 };
93
94 $scope.go = function() {
95 $scope.pageSize = $scope.newPageSize;
96 $cookies.pageSize = $scope.pageSize;
97 $scope.currentPage = 0;
98 if($scope.newCurrentPage <= parseInt($scope.hosts.length/$scope.pageSize)
99 && $scope.newCurrentPage > -1 && !isNaN(parseInt($scope.newCurrentPage))) {
100 $scope.currentPage = $scope.newCurrentPage;
101 }
102 };
103
104 // encodes search string in order to send it through URL
105 $scope.encodeSearch = function(search) {
106 var i = -1,
107 encode = "",
108 params = search.split(" "),
109 chunks = {};
110
111 params.forEach(function(chunk) {
112 i = chunk.indexOf(":");
113 if(i > 0) {
114 chunks[chunk.slice(0, i)] = chunk.slice(i+1);
115 } else {
116 if(!chunks.hasOwnProperty("free")) {
117 chunks.free = "";
118 }
119 chunks.free += " ".concat(chunk);
120 }
121 });
122
123 if(chunks.hasOwnProperty("free")) {
124 chunks.free = chunks.free.slice(1);
125 }
126
127 for(var prop in chunks) {
128 if(chunks.hasOwnProperty(prop)) {
129 if(chunks.prop != "") {
130 encode += "&" + encodeURIComponent(prop) + "=" + encodeURIComponent(chunks[prop]);
131 }
132 }
133 }
134 return encode.slice(1);
135 };
136
137 // decodes search parameters to object in order to use in filter
138 $scope.decodeSearch = function(search) {
139 var i = -1,
140 decode = {},
141 params = search.split("&");
142
143 params.forEach(function(param) {
144 i = param.indexOf("=");
145 decode[decodeURIComponent(param.slice(0,i))] = decodeURIComponent(param.slice(i+1));
146 });
147
148 if(decode.hasOwnProperty("free")) {
149 decode['$'] = decode.free;
150 delete decode.free;
151 }
152
153 return decode;
154 };
155
156 // converts current search object to string to be displayed in search field
157 $scope.stringSearch = function(obj) {
158 var search = "";
159
160 for(var prop in obj) {
161 if(obj.hasOwnProperty(prop)) {
162 if(search != "") {
163 search += " ";
164 }
165 if(prop == "$") {
166 search += obj[prop];
167 } else {
168 search += prop + ":" + obj[prop];
169 }
170 }
171 }
172
173 return search;
60174 };
61175
62176 $scope.remove = function(ids) {
158272 }, function(message){
159273 console.log(message);
160274 });
161 }
275 };
162276
163277 $scope.edit = function() {
164
165278 if($scope.selectedHosts().length == 1) {
166279 var modal = $modal.open({
167280 templateUrl: 'scripts/hosts/partials/modalEdit.html',
240353
241354 $scope.selectedHosts = function() {
242355 selected = [];
243 $scope.hosts.forEach(function(host) {
356
357 tmp_hosts = filter($scope.hosts);
358 tmp_hosts.forEach(function(host) {
244359 if(host.selected === true) {
245360 selected.push(host);
246361 }
251366 $scope.checkAll = function() {
252367 $scope.selectall = !$scope.selectall;
253368
254 angular.forEach($filter('filter')($scope.hosts, $scope.query), function(host) {
369 tmp_hosts = filter($scope.hosts);
370 tmp_hosts.forEach(function(host) {
255371 host.selected = $scope.selectall;
256372 });
257373 };
271387 $scope.toggleReverse = function() {
272388 $scope.reverse = !$scope.reverse;
273389 }
390
391 filter = function(data) {
392 var tmp_data = $filter('orderObjectBy')(data, $scope.sortField, $scope.reverse);
393 tmp_data = $filter('filter')(tmp_data, $scope.expression);
394 tmp_data = tmp_data.splice($scope.pageSize * $scope.currentPage, $scope.pageSize);
395
396 return tmp_data;
397 };
274398
275399 init();
276400 }]);
3131 </button>
3232 </h2><!-- .ws-label -->
3333 <div class="reports col-md-9 col-sm-9 col-xs-12">
34 <div class="col-md-6 col-sm-3 col-xs-11">
35 <form role="form" ng-submit="searchFor(true, searchParams)">
36 <div class="form-group">
37 <div class="input-group input-group-sm">
38 <span class="input-group-addon glyphicon-btn glyphicon glyphicon-remove" ng-click="searchFor(false, '')" ng-if="search"></span>
39 <input type="text" class="form-control" id="filter-by"
40 placeholder="enter keywords" ng-change="currentPage = 0" ng-model="searchParams" />
41 <span class="input-group-addon glyphicon-btn" ng-click="searchFor(true, searchParams)">
42 <i class="fa fa-search" ng-if="hosts"></i>
43 <i class="fa fa-refresh fa-spin" ng-if="hosts.length == 0"></i>
44 </span>
45 </div>
46 </div>
47 </form>
48 </div>
3449 <table class="status-report hosts-list table table-responsive">
3550 <thead>
3651 <tr>
5671 </tr>
5772 </thead>
5873 <tbody>
59 <tr ng-repeat="host in hosts | filter:query | orderBy:sortField:reverse"
74 <tr ng-repeat="host in filtered = (hosts | filter:expression) | orderBy:sortField:reverse | startFrom:currentPage*pageSize | limitTo:pageSize"
6075 selection-model selection-model-type="checkbox"
6176 selection-model-mode="multiple-additive"
6277 selection-model-selected-class="multi-selected"
7994 </tr>
8095 </tbody>
8196 </table><!-- #hosts -->
97 <div class="showPagination">
98 <div class="form-group">
99 <ul class="pagination">
100 <li><a ng-hide="currentPage <= 0" ng-click="currentPage = currentPage - 1"><span aria-hidden="true">&laquo;</span><span class="sr-only">Previous</span></a></li>
101 <li><a>{{currentPage}}/{{ ((filtered.length / pageSize) | integer)}}</a></li>
102 <li><a ng-hide="currentPage >= ((filtered.length / pageSize) | integer)" ng-click="currentPage = currentPage + 1"><span aria-hidden="true">&raquo;</span><span class="sr-only">Next</span></a></li>
103 </ul>
104 <form name="goToPage" id="goToPageStatus">
105 <div class="col-md-2">
106 <input type="number" min="0" max="{{ (filtered.length / pageSize) | integer }}" class="form-control" ng-model="newCurrentPage" placeholder="Go to page"/>
107 </div>
108 <button class="btn btn-default" ng-click="go()">GO</button>
109 <input type="number" min="0" class="form-control vuln_per_page" ng-model=newPageSize placeholder="Number page" />
110 </form>
111 </div>
112 </div><!-- .showPagination -->
82113 </div><!-- .reports -->
83114 </div><!-- #reports-main --></div><!-- .right-main -->
84115 </section><!-- #main -->
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('indexCtrl',
6 ['$scope', 'indexFact',
7 function($scope, indexFact) {
8 indexFact.getConf().then(function(conf) {
9 $scope.version = conf.data.ver;
10 });
11
12 }]);
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .factory('indexFact', ['$http', function($http) {
6 var indexFact = {};
7
8 indexFact.getConf = function() {
9 return $http.get('/reports/_design/reports/scripts/config/config.json');
10 };
11
12 return indexFact;
13 }]);
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
5 .controller('navigationCtrl', ['$scope', '$http','$route', '$routeParams', '$cookies', '$location', '$interval',
6 function($scope, $http, $route, $routeParams, $cookies, $location, $interval) {
5 .controller('navigationCtrl', ['$scope', '$http', '$route', '$routeParams', '$cookies', '$location', '$interval', 'configSrv',
6 function($scope, $http, $route, $routeParams, $cookies, $location, $interval, configSrv) {
77
88 $scope.workspace = "";
99 $scope.component = "";
1010
1111 $scope.checkCwe = function() {
12 $http.get("https://www.faradaysec.com/scripts/updatedb.php").then(function() {
12 $http.get("https://www.faradaysec.com/scripts/updatedb.php?version=" + configSrv.faraday_version).then(function() {
1313 }, function() {
1414 console.log("CWE database couldn't be updated");
1515 });
1616 };
1717
18 var timer = $interval($scope.checkCwe, 43200000);
19 $scope.checkCwe();
18 configSrv.promise.then(function() {
19 var timer = $interval($scope.checkCwe, 43200000);
20 $scope.checkCwe();
21 });
2022
2123 $scope.$on('$destroy', function() {
2224 $interval.cancel(timer);
2323 <i class="fa fa-sitemap host"></i>
2424 </a>
2525 </li>
26 <li>
27 <a href="#/users" class="users" style="color: #ffffff !important" tooltip="Users" tooltip-placement="right">
28 <img src="images/ico-users-menu.svg" alt="Users"/>
29 </a>
30 </li>
31 <li>
32 <a href="#/executive" class="executive-report" style="color: #ffffff !important" tooltip="Executive Report" tooltip-placement="right">
33 <img src="images/ico-executive-menu.svg" alt="Executive Report"/>
34 </a>
35 </li>
36 <li>
37 <a href="#/communication" class="executive-report" style="color: #ffffff !important" tooltip="Chat" tooltip-placement="right">
38 <img src="images/ico-communication-menu.svg" alt="Communication"/>
39 </a>
40 </li>
41 <li>
42 <a href="#/comparison" class="executive-report" style="color: #ffffff !important" tooltip="Workspaces Comparison" tooltip-placement="right">
43 <img src="images/ico-workspace-comparison-menu.svg" alt="Workspaces Comparison"/>
44 </a>
45 </li>
46 <li>
47 <a href="#/webshell" class="executive-report" style="color: #ffffff !important" tooltip="Web Shell" tooltip-placement="right">
48 <img src="images/ico-web-shell-menu.svg" alt="Webshell"/>
49 </a>
50 </li>
2651 </ul>
2752 </nav>
2853 <div ng-show="isIceweasel" class="alert alert-danger alert-dismissible">
5454 </button>
5555 </h2><!-- .ws-label -->
5656 <div class="reports col-md-9 col-sm-9 col-xs-12">
57 <div class="col-md-6 col-sm-3 col-xs-11">
58 <form role="form" ng-submit="searchFor(true, searchParams)">
59 <div class="form-group">
60 <div class="input-group input-group-sm">
61 <span class="input-group-addon glyphicon-btn glyphicon glyphicon-remove" ng-click="searchFor(false, '')" ng-if="search"></span>
62 <input type="text" class="form-control" id="filter-by"
63 placeholder="enter keywords" ng-change="currentPage = 0" ng-model="searchParams" />
64 <span class="input-group-addon glyphicon-btn" ng-click="searchFor(true, searchParams)">
65 <i class="fa fa-search" ng-if="services.length > 0"></i>
66 <i class="fa fa-refresh fa-spin" ng-if="services.length == 0"></i>
67 </span>
68 </div>
69 </div>
70 </form>
71 </div>
5772 <table class="status-report hosts-list table table-responsive">
5873 <thead>
5974 <tr>
7994 </tr>
8095 </thead>
8196 <tbody>
82 <tr ng-repeat="service in services | filter:query | orderBy:sortField:reverse"
97 <tr ng-repeat="service in filtered = (services | filter:expression) | orderBy:sortField:reverse | startFrom:currentPage*pageSize | limitTo:pageSize"
8398 selection-model selection-model-type="checkbox"
8499 selection-model-mode="multiple-additive"
85100 selection-model-selected-class="multi-selected">
105120 </tr>
106121 </tbody>
107122 </table><!-- #hosts -->
123 <div class="showPagination">
124 <div class="form-group">
125 <ul class="pagination">
126 <li><a ng-hide="currentPage <= 0" ng-click="currentPage = currentPage - 1"><span aria-hidden="true">&laquo;</span><span class="sr-only">Previous</span></a></li>
127 <li><a>{{currentPage}}/{{ ((filtered.length / pageSize) | integer)}}</a></li>
128 <li><a ng-hide="currentPage >= ((filtered.length / pageSize) | integer)" ng-click="currentPage = currentPage + 1"><span aria-hidden="true">&raquo;</span><span class="sr-only">Next</span></a></li>
129 </ul>
130 <form name="goToPage" id="goToPageStatus">
131 <div class="col-md-2">
132 <input type="number" min="0" max="{{ (filtered.length / pageSize) | integer }}" class="form-control" ng-model="newCurrentPage" placeholder="Go to page"/>
133 </div>
134 <button class="btn btn-default" ng-click="go()">GO</button>
135 <input type="number" min="0" class="form-control vuln_per_page" ng-model=newPageSize placeholder="Number page" />
136 </form>
137 </div>
138 </div><!-- .showPagination -->
108139 </div><!-- .reports -->
109140 </div><!-- #reports-main --></div><!-- .right-main -->
110141 </section><!-- #main -->
44 angular.module('faradayApp')
55 .controller('statusReportCtrl',
66 ['$scope', '$filter', '$routeParams',
7 '$location', '$modal', '$cookies', '$q', 'BASEURL',
7 '$location', '$modal', '$cookies', '$q', '$window', 'BASEURL',
88 'SEVERITIES', 'EASEOFRESOLUTION', 'hostsManager',
99 'vulnsManager', 'workspacesFact', 'csvService',
1010 function($scope, $filter, $routeParams,
11 $location, $modal, $cookies, $q, BASEURL,
11 $location, $modal, $cookies, $q, $window, BASEURL,
1212 SEVERITIES, EASEOFRESOLUTION, hostsManager,
1313 vulnsManager, workspacesFact, csvService) {
1414 $scope.baseurl;
7171 });
7272
7373 // created object for columns cookie columns
74 if(typeof($cookies.SRcolumns) != 'undefined'){
74 if(typeof($cookies.SRcolumns) != 'undefined') {
7575 var objectoSRColumns = {};
7676 var arrayOfColumns = $cookies.SRcolumns.replace(/[{}"']/g, "").split(',');
7777 arrayOfColumns.forEach(function(column){
8383 $scope.columns = objectoSRColumns || {
8484 "date": true,
8585 "severity": true,
86 "service": true,
8687 "target": true,
8788 "name": true,
8889 "desc": true,
108109 $scope.vulnWebSelected = false;
109110 };
110111
112 $scope.processReference = function(text) {
113 var url = 'http://google.com/',
114 url_pattern = new RegExp('^(http|https):\\/\\/?');
115
116 var cve_pattern = new RegExp(/^CVE-\d{4}-\d{4,7}$/),
117 cwe_pattern = new RegExp(/^CWE(-|:)\d{1,7}$/),
118 edb_pattern = new RegExp(/^EDB-ID:\s?\d{1,}$/),
119 osvdb_pattern = new RegExp(/^OSVDB:\s?\d{1,}$/);
120
121 var cve = text.search(cve_pattern),
122 cwe = text.search(cwe_pattern),
123 edb = text.search(edb_pattern),
124 osvdb = text.search(osvdb_pattern);
125
126 if(url_pattern.test(text)) {
127 url = text;
128 } else if(cve > -1) {
129 url = "https://cve.mitre.org/cgi-bin/cvename.cgi?name=" + text.substring(cve + 4);
130 } else if(cwe > -1) {
131 url = "https://cwe.mitre.org/data/definitions/" + text.substring(cwe + 4) + ".html";
132 } else if(osvdb > -1) {
133 url = "http://osvdb.org/show/osvdb/" + text.substring(osvdb + 6);
134 } else if(edb > -1) {
135 url = "https://www.exploit-db.com/exploits/" + text.substring(edb + 7);
136 } else {
137 url += 'search?q=' + text;
138 }
139
140 $window.open(url, '_blank');
141 };
142
111143 $scope.selectedVulns = function() {
112144 selected = [];
113145 var tmp_vulns = $filter('orderObjectBy')($scope.vulns, $scope.sortField, $scope.reverse);
267299 'Enter the new severity:',
268300 'severity',
269301 {options: SEVERITIES});
270 }
302 };
271303
272304 $scope.editEaseofresolution = function() {
273305 editProperty(
276308 'Enter the new easeofresolution:',
277309 'easeofresolution',
278310 {options: EASEOFRESOLUTION});
279 }
311 };
280312
281313 $scope.editReferences = function() {
282314 editProperty(
295327 return {'refs': references};
296328 }}
297329 );
298 }
330 };
299331
300332 $scope.editImpact = function() {
301333 editProperty(
324356 }
325357 }
326358 );
327 }
359 };
328360
329361 $scope.editString = function(property, message_word) {
330362 var message;
338370 'commonsModalEditString',
339371 message,
340372 property);
341 }
373 };
342374
343375 $scope.editText = function(property, message_word) {
344376 var message;
352384 'commonsModalEditString',
353385 message,
354386 property);
355 }
387 };
356388
357389 $scope.editCWE = function() {
358390 var modal = $modal.open({
382414 });
383415 });
384416 });
385 }
417 };
386418
387419 $scope.insert = function(vuln) {
388420 vulnsManager.createVuln($scope.workspace, vuln).then(function() {
1313 <div class="form-horizontal">
1414 <div class="form-group">
1515 <div class="col-md-12">
16 <input type="text" ng-model="modal.target_filter" class="form-control input-sm" placeholder="Search" ng-change="modal.currentPage = 0">
17 <accordion close-others="true">
18 <accordion-group is-open="isopen" ng-repeat="host in modal.targets_filtered = (modal.targets | filter:modal.target_filter) | startFrom:modal.currentPage*modal.pageSize | limitTo:modal.pageSize">
16 <div class="form-group input-accordion">
17 <input type="text" ng-model="modal.target_filter" class="form-control input-sm" placeholder="Search" ng-change="modal.currentPage = 0">
18 </div>
19 <accordion close-others="true">
20 <accordion-group is-open="isopen" ng-repeat="host in modal.targets_filtered = (modal.targets | filter:modal.target_filter) | startFrom:modal.currentPage*modal.pageSize | limitTo:modal.pageSize">
1921 <accordion-heading>
2022 <a ng-click="modal.setTarget(host)" ng-class="{'multi-selected': host.selected_modalNewCtrl == true}">{{host.name}} ({{host.hostnames[0]}})</a>
21 <i class="pull-right glyphicon"
22 ng-class="{'glyphicon glyphicon-minus-sign': isopen, 'glyphicon glyphicon-plus-sign': !isopen}"></i>
23 <i class="pull-right glyphicon"
24 ng-class="{'glyphicon glyphicon-minus-sign': isopen, 'glyphicon glyphicon-plus-sign': !isopen}"></i>
2325 </accordion-heading>
2426 <div class="panel-body" ng-repeat="service in host.services">
2527 <a ng-model="service" ng-click="modal.setTarget(service)" ng-class="{'multi-selected': service.selected_modalNewCtrl == true}">{{service.name}}</a>
26 </div>
27 </accordion-group>
28 </accordion>
29 <div class="showPagination" ng-show="modal.targets_filtered.length > modal.pageSize">
28 </div>
29 </accordion-group>
30 </accordion>
31 <div class="showPagination" ng-show="modal.targets_filtered.length > modal.pageSize">
3032 <div class="form-group">
3133 <ul class="pagination">
3234 <li><a ng-hide="modal.currentPage <= 0" ng-click="modal.currentPage = modal.currentPage - 1"><span aria-hidden="true">&laquo;</span><span class="sr-only">Previous</span></a></li>
105105 <a href="" ng-click="toggleSort('target')">Target</a>
106106 <a href="" ng-click="toggleShow('target', true)"><span class="glyphicon glyphicon-remove"></span></a>
107107 </th>
108 <th ng-if="columns.service">
109 <a href="" ng-click="toggleSort('service')">Service</a>
110 <a href="" ng-click="toggleShow('service', true)"><span class="glyphicon glyphicon-remove"></span></a>
111 </th>
108112 <th ng-if="columns.status">
109113 <a href="" ng-click="toggleSort('status')">Status</a>
110114 <a href="" ng-click="toggleShow('status', true)"><span class="glyphicon glyphicon-remove"></span></a>
199203 <td class="text-center"><span ng-click="deleteVuln(v)" class="glyphicon glyphicon-trash cursor" tooltip="Delete"></span></td>
200204 <td ng-if="columns.date">{{v.metadata.create_time * 1000 | date:'MM/dd/yyyy'}}</td>
201205 <td ng-if="columns.target">
202 <a href="#/status/ws/{{workspace}}/search/target={{v.target}}">{{v.target}}</a>
203 <a href="//www.shodan.io/search?query={{v.target}}" tooltip="Search in Shodan" target="_blank">
206 <a ng-href="#/status/ws/{{workspace}}/search/target={{v.target}}">{{v.target}}</a>
207 <a ng-href="//www.shodan.io/search?query={{v.target}}" tooltip="Search in Shodan" target="_blank">
204208 <img ng-src="../././reports/images/shodan.png" height="15px" width="15px" />
205209 </a>
210 </td>
211 <td ng-if="columns.service">
212 <a ng-href="#/status/ws/{{workspace}}/search/service={{v.service | encodeURIComponent | encodeURIComponent}}">{{v.service}}</a>
206213 </td>
207214 <td ng-if="columns.status">Vulnerable</td>
208215 <td ng-if="columns.severity"><a href="#/status/ws/{{workspace}}/search/severity={{v.severity}}"><span class="label vuln fondo-{{v.severity}}">{{v.severity}}</span></a></td>
222229 <span class="glyphicon glyphicon-remove" ng-show="v.type !== 'VulnerabilityWeb'"></span>
223230 </td>
224231 <td ng-if="columns.website"><a href="#/status/ws/{{workspace}}/search/website={{v.website}}">{{v.website}}</a></td>
225 <td ng-if="columns.refs"><p ng-repeat="refs in v.refs track by $index">{{refs}}</p></td>
232 <td ng-if="columns.refs">
233 <p ng-repeat="refs in v.refs track by $index"><a ng-click="processReference(refs)">{{refs}}</a></p>
234 </td>
226235 <td ng-if="columns.evidence">
227236 <div ng-repeat="(name, file) in v._attachments track by $index">
228237 <a ng-href="{{baseurl + workspace}}/{{v._id}}/{{name | encodeURIComponent}}" target="_blank">{{name | decodeURIComponent}}</a>
1111 this._id = "";
1212 this._rev = "";
1313 this._attachments = {};
14 this.confirmed = true;
1415 this.data = "";
1516 this.desc = "";
1617 this.easeofresolution = "";
3738 this.parent = "";
3839 this.refs = "";
3940 this.resolution = "";
41 this.service = "";
4042 this.severity = "";
4143 this.target = "";
4244 this.type = "Vulnerability";
5254
5355 Vuln.prototype = {
5456 public_properties: [
55 '_attachments', 'data', 'desc', 'easeofresolution',
57 '_attachments', 'confirmed', 'data', 'desc', 'easeofresolution',
5658 'impact', 'name', 'owned', 'refs', 'resolution', 'severity'
5759 ],
5860 set: function(ws, data) {
33
44 angular.module('faradayApp')
55 .factory('vulnsManager',
6 ['Vuln', 'WebVuln', 'BASEURL', '$filter', '$http', '$q', 'attachmentsFact', 'hostsManager',
7 function(Vuln, WebVuln, BASEURL, $filter, $http, $q, attachmentsFact, hostsManager) {
6 ['Vuln', 'WebVuln', 'BASEURL', '$filter', '$http', '$q', 'attachmentsFact', 'hostsManager', 'servicesManager',
7 function(Vuln, WebVuln, BASEURL, $filter, $http, $q, attachmentsFact, hostsManager, servicesManager) {
88 var vulnsManager = {};
99
1010 vulnsManager.vulns = [];
2828 return res;
2929 };
3030
31 vulnsManager._loadServices = function(services) {
32 var res = {};
33
34 services.forEach(function(service) {
35 res[service._id] = "(" + service['ports'].join(",") + "/" + service['protocol'] + ") " + service['name'];
36 });
37
38 return res;
39 };
40
3141 vulnsManager.createVuln = function(ws, data) {
3242 var deferred = $q.defer(),
3343 self = this;
4353 .then(function(resp) {
4454 self.vulns_indexes[vuln._id] = self.vulns.length;
4555 self.vulns.push(vuln);
46 var parents = [hostsManager.getHosts(ws), hostsManager.getAllInterfaces(ws)];
56 var parents = [hostsManager.getHosts(ws), hostsManager.getAllInterfaces(ws), servicesManager.getServices(ws)];
4757
4858 $q.all(parents)
4959 .then(function(ps) {
5060 var hosts = self._loadHosts(ps[0], ps[1]);
61 var services = self._loadServices(ps[2]);
5162
5263 self.vulns.forEach(function(vuln) {
5364 var pid = vuln.parent.split(".")[0];
5465 if (hosts.hasOwnProperty(pid)) {
5566 vuln.target = hosts[pid]["target"];
5667 vuln.hostnames = hosts[pid]["hostnames"];
57 };
68 }
69 if(services.hasOwnProperty(vuln.parent)) vuln.service = services[vuln.parent];
5870 });
5971 });
6072
112124 }
113125 }
114126
115 var parents = [hostsManager.getHosts(ws), hostsManager.getAllInterfaces(ws)];
127 var parents = [hostsManager.getHosts(ws), hostsManager.getAllInterfaces(ws), servicesManager.getServices(ws)];
116128
117129 $q.all(parents)
118130 .then(function(ps) {
119131 var hosts = self._loadHosts(ps[0], ps[1]);
132 var services = self._loadServices(ps[2]);
120133
121134 self.vulns.forEach(function(vuln) {
122135 var pid = vuln.parent.split(".")[0];
123 if (hosts.hasOwnProperty(pid)) {
136
137 if(hosts.hasOwnProperty(pid)) {
124138 vuln.target = hosts[pid]["target"];
125139 vuln.hostnames = hosts[pid]["hostnames"];
126140 }
141 if(services.hasOwnProperty(vuln.parent)) vuln.service = services[vuln.parent];
127142 });
128143 });
129144
11 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
22 <!-- See the file 'doc/LICENSE' for the license information -->
33
4 <form>
4 <form novalidate>
55 <div class="modal-header">
66 <div class="modal-button">
77 <button class="btn btn-success" ng-click="okEdit()">OK</button>
2020 </div>
2121 </div><!-- .form-group -->
2222 <div class="form-group">
23 <div class="col-md-6">
23 <div class="col-md-6 datepicker">
2424 <h5>Start Date</h5>
2525 <label class="sr-only" for="work-start">Start Date</label>
2626 <p class="input-group">
3030 </span>
3131 </p>
3232 </div>
33 <div class="col-md-6">
33 <div class="col-md-6 datepicker">
3434 <h5>End Date</h5>
3535 <label class="sr-only" for="work-end">End Date</label>
3636 <p class="input-group">
11 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
22 <!-- See the file 'doc/LICENSE' for the license information -->
33
4 <form name="form">
4 <form name="form" novalidate>
55 <div class="modal-header">
66 <div class="modal-button">
77 <button class="btn btn-success" ng-disabled="form.$invalid || date.$invalid" ng-click="okNew()">Save</button>
4040 </div><!-- .form-group -->
4141 <div class="form-group">
4242 <ng-form name="date" novalidate>
43 <div class="col-md-6">
43 <div class="col-md-6 datepicker">
4444 <label class="sr-only" for="work-start">Start Date</label>
4545 <p class="input-group">
4646 <input type="text" class="form-control" datepicker-popup="MM/dd/yyyy" ng-model="workspace.start" is-open="openedStart" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" close-text="Close" placeholder="Start Date" />
4949 </span>
5050 </p>
5151 </div>
52 <div class="col-md-6">
52 <div class="col-md-6 datepicker">
5353 <label class="sr-only" for="work-end">End Date</label>
5454 <p class="input-group">
5555 <input type="text" class="form-control" datepicker-popup="MM/dd/yyyy" ng-model="workspace.end" is-open="openedEnd" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" close-text="Close" placeholder="End Date" />