├── yandextank ├── plugins │ ├── Report │ │ ├── __init__.py │ │ ├── static │ │ │ ├── css │ │ │ │ ├── custom.css │ │ │ │ ├── rickshaw.css │ │ │ │ ├── bootstrap-theme.min.css │ │ │ │ ├── bootstrap-theme.css.map │ │ │ │ └── bootstrap-theme.css │ │ │ ├── favicon.ico │ │ │ ├── fonts │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ └── glyphicons-halflings-regular.woff │ │ │ └── js │ │ │ │ ├── angular-rickshaw.coffee │ │ │ │ ├── angular-rickshaw.js │ │ │ │ ├── ws.coffee │ │ │ │ ├── ws.js │ │ │ │ └── vendor │ │ │ │ ├── cubism.min.js │ │ │ │ ├── bootstrap.min.js │ │ │ │ └── socket.io.min.js │ │ ├── templates │ │ │ ├── brief.jade │ │ │ ├── monitoring.jade │ │ │ ├── index.jade │ │ │ └── offline.jade │ │ ├── plugin.py │ │ └── server.py │ └── __init__.py └── __init__.py ├── .gitignore ├── setup.py └── README.md /yandextank/plugins/Report/__init__.py: -------------------------------------------------------------------------------- 1 | from plugin import Plugin 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | yatank_online.egg-info/ 4 | *.pyc 5 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/static/css/custom.css: -------------------------------------------------------------------------------- 1 | .fs { 2 | height: 100vh; 3 | } 4 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex-load/yatank-online/HEAD/yandextank/plugins/Report/static/favicon.ico -------------------------------------------------------------------------------- /yandextank/plugins/Report/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex-load/yatank-online/HEAD/yandextank/plugins/Report/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /yandextank/plugins/Report/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex-load/yatank-online/HEAD/yandextank/plugins/Report/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /yandextank/plugins/Report/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex-load/yatank-online/HEAD/yandextank/plugins/Report/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /yandextank/__init__.py: -------------------------------------------------------------------------------- 1 | # this is a namespace package 2 | try: 3 | import pkg_resources 4 | pkg_resources.declare_namespace(__name__) 5 | except ImportError: 6 | import pkgutil 7 | __path__ = pkgutil.extend_path(__path__, __name__) 8 | -------------------------------------------------------------------------------- /yandextank/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | # this is a namespace package 2 | try: 3 | import pkg_resources 4 | pkg_resources.declare_namespace(__name__) 5 | except ImportError: 6 | import pkgutil 7 | __path__ = pkgutil.extend_path(__path__, __name__) 8 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | 5 | setup(name='yatank-online', 6 | version='1.8.1', 7 | description='Yandex.Tank Report plugin', 8 | author='Alexey Lavrenuke', 9 | author_email='direvius@gmail.com', 10 | url='https://github.com/yandex-load/yatank-online/', 11 | packages=['yandextank.plugins.Report'], 12 | package_data={'yandextank.plugins.Report': [ 13 | 'templates/*', 14 | 'static/favicon.ico', 15 | 'static/css/*', 16 | 'static/js/*.js', 17 | 'static/js/*.coffee', 18 | 'static/js/vendor/*.js', 19 | 'static/fonts/*', 20 | ]}, 21 | namespace_packages=['yandextank', 'yandextank.plugins'], 22 | install_requires=[ 23 | 'tornado', 24 | 'tornadio2', 25 | 'pyjade>=4.0.0', 26 | ], ) 27 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/templates/brief.jade: -------------------------------------------------------------------------------- 1 | !!! 5 2 | {% autoescape None %} 3 | html(lang="en") 4 | head 5 | meta(charset="utf-8") 6 | meta(http-equiv="X-UA-Compatible", content="IE=edge") 7 | meta(name="viewport", content="width=device-width, initial-scale=1") 8 | title YndxTnk brief 9 | link(rel="shortcut icon", href="static/favicon.ico", type="image/x-icon") 10 | link(rel="icon", href="static/favicon.ico", type="image/x-icon") 11 | link(rel="stylesheet", href="static/css/bootstrap.min.css") 12 | link(rel="stylesheet", href="static/css/bootstrap-theme.min.css") 13 | link(rel="stylesheet", href="static/css/rickshaw.css") 14 | link(rel="stylesheet", href="static/css/custom.css") 15 | script(src="static/js/vendor/socket.io.min.js") 16 | script(src="static/js/vendor/jquery.min.js") 17 | script(src="static/js/vendor/angular.min.js") 18 | script(src="static/js/vendor/bootstrap.min.js") 19 | script(src="static/js/vendor/d3.min.js") 20 | script(src="static/js/vendor/rickshaw.min.js") 21 | script(src="static/js/angular-rickshaw.js") 22 | script(src="static/js/ws.js") 23 | script(type="text/javascript") 24 | document.cached_data = {{cached_data}}; 25 | body 26 | .app(ng-app="ng-tank-report", ng-controller="TankReport") 27 | .fs 28 | h2 {{!quantiles.name}} 29 | rickshaw( 30 | rickshaw-series="quantiles.series", 31 | rickshaw-options="quantiles.options", 32 | rickshaw-features="quantiles.features" 33 | ) 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Attention please! 2 | 3 | This plugin is not the prefered way to build reports currently. Use Yandex [Overload](https://overload.yandex.net/mainpage/guide#install) instead. 4 | 5 | This doc is for new version of a plugin (currently work-in-progress). The old one (code and doc) is here: https://github.com/yandex-load/yatank-online/tree/1.7_branch 6 | 7 | # yatank-online 8 | 9 | Yandex.Tank OnlineReport plugin. Shows you some charts while you're shooting and saves a report in the end. 10 | 11 | ## How to enable 12 | Install with ```pip install yatank-online``` and specify these lines in your Tank configuration: 13 | ``` 14 | [tank] 15 | plugin_web=yandextank.plugins.Report 16 | ``` 17 | Then start your shooting and go to ```http://localhost:8001/``` 18 | 19 | ## WebSockets and Nginx configuration 20 | 21 | If you want to run online interface behind nginx, then this would be useful: 22 | 23 | http { 24 | 25 | <...> 26 | 27 | map $http_upgrade $connection_upgrade { 28 | default upgrade; 29 | '' close; 30 | } 31 | 32 | upstream backend { 33 | server 127.0.0.1:8001; 34 | } 35 | 36 | server { 37 | 38 | <...> 39 | 40 | location / { 41 | 42 | proxy_pass http://backend; 43 | 44 | proxy_http_version 1.1; 45 | 46 | proxy_set_header Host $host; 47 | proxy_set_header Origin $upstream_addr; 48 | proxy_set_header Upgrade $http_upgrade; 49 | proxy_set_header Connection $connection_upgrade; 50 | 51 | } 52 | 53 | <...> 54 | 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/templates/monitoring.jade: -------------------------------------------------------------------------------- 1 | !!! 5 2 | {% autoescape None %} 3 | html(lang="en") 4 | head 5 | meta(charset="utf-8") 6 | meta(http-equiv="X-UA-Compatible", content="IE=edge") 7 | meta(name="viewport", content="width=device-width, initial-scale=1") 8 | title Yandex.Tank monitoring 9 | link(rel="shortcut icon", href="static/favicon.ico", type="image/x-icon") 10 | link(rel="icon", href="static/favicon.ico", type="image/x-icon") 11 | link(rel="stylesheet", href="static/css/bootstrap.min.css") 12 | link(rel="stylesheet", href="static/css/bootstrap-theme.min.css") 13 | link(rel="stylesheet", href="static/css/rickshaw.css") 14 | link(rel="stylesheet", href="static/css/custom.css") 15 | script(src="static/js/vendor/socket.io.min.js") 16 | script(src="static/js/vendor/jquery.min.js") 17 | script(src="static/js/vendor/angular.min.js") 18 | script(src="static/js/vendor/bootstrap.min.js") 19 | script(src="static/js/vendor/d3.min.js") 20 | script(src="static/js/vendor/rickshaw.min.js") 21 | script(src="static/js/angular-rickshaw.js") 22 | script(src="static/js/ws.js") 23 | script(type="text/javascript") 24 | document.cached_data = {{cached_data}}; 25 | body 26 | .app.container(ng-app="ng-tank-report", ng-controller="TankReport") 27 | .host(ng-repeat="host in monitoringGraphData") 28 | h3 {{!host.hostname}} 29 | .row 30 | .col-md-5(ng-repeat="chart in host.charts") 31 | .panel.panel-default 32 | .panel-heading 33 | h3.panel-title {{!chart.name}} 34 | .panel-body 35 | rickshaw( 36 | rickshaw-series="chart.series", 37 | rickshaw-options="chart.options", 38 | rickshaw-features="chart.features" 39 | ) 40 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/templates/index.jade: -------------------------------------------------------------------------------- 1 | !!! 5 2 | {% autoescape None %} 3 | html(lang="en") 4 | head 5 | meta(charset="utf-8") 6 | meta(http-equiv="X-UA-Compatible", content="IE=edge") 7 | meta(name="viewport", content="width=device-width, initial-scale=1") 8 | title Yandex.Tank online report 9 | link(rel="shortcut icon", href="static/favicon.ico", type="image/x-icon") 10 | link(rel="icon", href="static/favicon.ico", type="image/x-icon") 11 | link(rel="stylesheet", href="static/css/bootstrap.min.css") 12 | link(rel="stylesheet", href="static/css/bootstrap-theme.min.css") 13 | link(rel="stylesheet", href="static/css/rickshaw.css") 14 | link(rel="stylesheet", href="static/css/custom.css") 15 | script(src="static/js/vendor/socket.io.min.js") 16 | script(src="static/js/vendor/jquery.min.js") 17 | script(src="static/js/vendor/angular.min.js") 18 | script(src="static/js/vendor/bootstrap.min.js") 19 | script(src="static/js/vendor/d3.min.js") 20 | script(src="static/js/vendor/rickshaw.min.js") 21 | script(src="static/js/angular-rickshaw.js") 22 | script(src="static/js/ws.js") 23 | script(type="text/javascript"). 24 | document.cached_data = {{cached_data}}; 25 | body 26 | .app.container(ng-app="ng-tank-report", ng-controller="TankReport") 27 | h1.page-header Yandex.Tank online report 28 | #status {{!status}} 29 | #timings 30 | .panel.panel-default 31 | .panel-heading 32 | h3.panel-title {{!quantiles.name}} 33 | .panel-body 34 | rickshaw.panel-body( 35 | rickshaw-series="quantiles.series", 36 | rickshaw-options="quantiles.options", 37 | rickshaw-features="quantiles.features" 38 | ) 39 | .panel.panel-default 40 | .panel-heading 41 | h3.panel-title {{!rps.name}} 42 | .panel-body 43 | rickshaw( 44 | rickshaw-series="rps.series", 45 | rickshaw-options="rps.options", 46 | rickshaw-features="rps.features" 47 | ) 48 | #codes 49 | .panel.panel-default 50 | .panel-heading 51 | h3.panel-title {{!protoCodes.name}} 52 | .panel-body 53 | rickshaw( 54 | rickshaw-series="protoCodes.series", 55 | rickshaw-options="protoCodes.options", 56 | rickshaw-features="protoCodes.features" 57 | ) 58 | .panel.panel-default 59 | .panel-heading 60 | h3.panel-title {{!netCodes.name}} 61 | .panel-body 62 | rickshaw( 63 | rickshaw-series="netCodes.series", 64 | rickshaw-options="netCodes.options", 65 | rickshaw-features="netCodes.features" 66 | ) 67 | #monitoring 68 | .host(ng-repeat="host in monitoringData") 69 | h3 {{!host.hostname}} 70 | .panel.panel-default(ng-repeat="group in host.groups") 71 | .panel-heading 72 | h3.panel-title {{!group.name}} 73 | .panel-body 74 | rickshaw( 75 | rickshaw-series="group.series", 76 | rickshaw-options="group.options", 77 | rickshaw-features="group.features" 78 | ) 79 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/static/js/angular-rickshaw.coffee: -------------------------------------------------------------------------------- 1 | ###* 2 | Based on https://github.com/ngyewch/angular-rickshaw 3 | ### 4 | "use strict" 5 | 6 | angular.module("angular-rickshaw", []).directive "rickshaw", ($compile) -> 7 | restrict: "EA" 8 | scope: 9 | options: "=rickshawOptions" 10 | series: "=rickshawSeries" 11 | features: "=rickshawFeatures" 12 | 13 | 14 | # replace: true, 15 | link: (scope, element, attrs) -> 16 | getSettings = (el) -> 17 | settings = angular.copy(scope.options) 18 | settings.element = el 19 | settings.series = scope.series 20 | settings 21 | update = -> 22 | mainEl = angular.element(element) 23 | mainEl.append graphEl 24 | mainEl.empty() 25 | graphEl = $compile("
")(scope) 26 | mainEl.append graphEl 27 | settings = getSettings(graphEl[0]) 28 | Rickshaw.Series.zeroFill(settings.series) 29 | scope.graph = new Rickshaw.Graph(settings) 30 | if scope.features and scope.features.hover 31 | hoverConfig = graph: scope.graph 32 | hoverConfig.xFormatter = scope.features.hover.xFormatter 33 | hoverConfig.yFormatter = scope.features.hover.yFormatter 34 | hoverConfig.formatter = scope.features.hover.formatter 35 | hoverDetail = new Rickshaw.Graph.HoverDetail(hoverConfig) 36 | if scope.features and scope.features.palette 37 | palette = new Rickshaw.Color.Palette(scheme: scope.features.palette) 38 | i = 0 39 | 40 | while i < settings.series.length 41 | settings.series[i].color = palette.color() if !settings.series[i].color 42 | i++ 43 | scope.graph.render() 44 | if scope.features and scope.features.xAxis 45 | xAxisConfig = graph: scope.graph 46 | if scope.features.xAxis.timeUnit 47 | time = new Rickshaw.Fixtures.Time() 48 | xAxisConfig.timeUnit = time.unit(scope.features.xAxis.timeUnit) 49 | xAxis = new Rickshaw.Graph.Axis.Time(xAxisConfig) 50 | xAxis.render() 51 | if scope.features and scope.features.yAxis 52 | yAxisConfig = graph: scope.graph, orientation: 'left' 53 | yAxisConfig.tickFormat = Rickshaw.Fixtures.Number[scope.features.yAxis.tickFormat] if scope.features.yAxis.tickFormat 54 | yAxis = new Rickshaw.Graph.Axis.Y(yAxisConfig) 55 | yAxis.render() 56 | if scope.features and scope.features.yAxisSecondary 57 | yAxisSecondaryConfig = graph: scope.graph, orientation: 'right' 58 | yAxisSecondaryConfig.tickFormat = Rickshaw.Fixtures.Number[scope.features.yAxisSecondary.tickFormat] if scope.features.yAxisSecondary.tickFormat 59 | yAxisSecondary = new Rickshaw.Graph.Axis.Y(yAxisSecondaryConfig) 60 | yAxisSecondary.render() 61 | if scope.features and scope.features.legend 62 | legendEl = $compile("
")(scope) 63 | mainEl.append legendEl 64 | legend = new Rickshaw.Graph.Legend( 65 | graph: scope.graph 66 | element: legendEl[0] 67 | ) 68 | if scope.features.legend.toggle 69 | shelving = new Rickshaw.Graph.Behavior.Series.Toggle( 70 | graph: scope.graph 71 | legend: legend 72 | ) 73 | if scope.features.legend.highlight 74 | highlighter = new Rickshaw.Graph.Behavior.Series.Highlight( 75 | graph: scope.graph 76 | legend: legend 77 | ) 78 | return 79 | scope.graph = undefined 80 | scope.$watch "options", (newValue, oldValue) -> 81 | update() unless angular.equals(newValue, oldValue) 82 | return 83 | 84 | scope.$watch "series", (newValue, oldValue) -> 85 | update() unless angular.equals(newValue, oldValue) 86 | return 87 | 88 | scope.$watch "features", (newValue, oldValue) -> 89 | update() unless angular.equals(newValue, oldValue) 90 | return 91 | 92 | scope.$on "DataUpdated", () -> 93 | scope.graph.update() 94 | 95 | update() 96 | 97 | controller: ($scope, $element, $attrs) -> 98 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/plugin.py: -------------------------------------------------------------------------------- 1 | import time 2 | import json 3 | from threading import Thread 4 | 5 | from ..Telegraf import Plugin as ReportPlugin 6 | from ..Aggregator import AggregatorPlugin 7 | from ...core.interfaces import AbstractPlugin 8 | 9 | from .server import ReportServer 10 | 11 | import logging 12 | 13 | logger = logging.getLogger(__name__) 14 | 15 | 16 | def uts(dt): 17 | return int(time.mktime(dt.timetuple())) 18 | 19 | 20 | class Plugin(AbstractPlugin, Thread): 21 | '''Interactive report plugin ''' 22 | SECTION = "report" 23 | 24 | @staticmethod 25 | def get_key(): 26 | return __file__ 27 | 28 | def __init__(self, core): 29 | AbstractPlugin.__init__(self, core) 30 | Thread.__init__(self) 31 | self.daemon = True # Thread auto-shutdown 32 | self.port = 8080 33 | self.last_sec = None 34 | self.server = None 35 | self.data = [] 36 | self.stats = [] 37 | self.monitoring = [] 38 | 39 | def get_all_data(self): 40 | return {"data": self.data, 41 | "stats": self.stats, 42 | "monitoring": self.monitoring, } 43 | 44 | def get_available_options(self): 45 | return ["port"] 46 | 47 | def configure(self): 48 | self.port = int(self.get_option("port", self.port)) 49 | try: 50 | aggregator = self.core.get_plugin_of_type(AggregatorPlugin) 51 | aggregator.add_result_listener(self) 52 | except KeyError: 53 | logger.warning( 54 | "No aggregator module, no valid report will be available") 55 | 56 | try: 57 | mon = self.core.get_plugin_of_type(ReportPlugin) 58 | if mon.monitoring: 59 | mon.monitoring.add_listener(self) 60 | except KeyError: 61 | logger.warning("No monitoring module, monitroing report disabled") 62 | 63 | def prepare_test(self): 64 | try: 65 | self.server = ReportServer(self) 66 | self.server.owner = self 67 | except Exception, ex: 68 | logger.warning("Failed to start web results server: %s", ex) 69 | 70 | def start_test(self): 71 | self.start() 72 | 73 | def end_test(self, retcode): 74 | logger.info("Ended test. Sending command to reload pages.") 75 | self.server.reload() 76 | return retcode 77 | 78 | def run(self): 79 | if (self.server): 80 | self.server.serve() 81 | logger.info("Server started.") 82 | 83 | def on_aggregated_data(self, data, stats): 84 | """ 85 | @data: aggregated data 86 | @stats: stats about gun 87 | """ 88 | if data: 89 | self.data.append(data) 90 | if stats: 91 | self.stats.append(stats) 92 | if self.server is not None and (data or stats): 93 | message = {'data': data, 'stats': stats} 94 | self.server.send({k: v for k, v in message.iteritems() if v}) 95 | 96 | def monitoring_data(self, data): 97 | if data: 98 | self.monitoring += data 99 | message = {'monitoring': data, } 100 | if self.server is not None: 101 | self.server.send(message) 102 | 103 | def post_process(self, retcode): 104 | report_json = self.core.mkstemp(".json", "report_") 105 | logger.info("Saving JSON report to %s", report_json) 106 | self.core.add_artifact_file(report_json) 107 | with open(report_json, 'w') as report_json_file: 108 | json.dump(self.get_all_data(), report_json_file, indent=2) 109 | report_html = self.core.mkstemp(".html", "report_") 110 | logger.info("Saving HTML report to %s", report_html) 111 | self.core.add_artifact_file(report_html) 112 | with open(report_html, 'w') as report_html_file: 113 | report_html_file.write(self.server.render_offline()) 114 | # raw_input('Press Enter to stop report server.') 115 | self.server.stop() 116 | del self.server 117 | self.server = None 118 | return retcode 119 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/server.py: -------------------------------------------------------------------------------- 1 | import tornado.ioloop 2 | import tornado.web 3 | import os.path 4 | import json 5 | import uuid 6 | 7 | import logging 8 | logger = logging.getLogger(__name__) 9 | 10 | from tornado import template 11 | from pyjade.ext.tornado import patch_tornado 12 | patch_tornado() 13 | 14 | from tornadio2 import SocketConnection, TornadioRouter, event 15 | from tornadio2.server import SocketServer 16 | 17 | from threading import Thread 18 | 19 | 20 | class Client(SocketConnection): 21 | CONNECTIONS = set() 22 | 23 | def on_open(self, info): 24 | logger.info('Client connected') 25 | self.CONNECTIONS.add(self) 26 | 27 | def on_message(self, msg): 28 | logger.info('Got %s', msg) 29 | 30 | def on_close(self): 31 | logger.info('Client disconnected') 32 | self.CONNECTIONS.remove(self) 33 | 34 | @event('heartbeat') 35 | def on_heartbeat(self): 36 | pass 37 | 38 | 39 | class MainHandler(tornado.web.RequestHandler): 40 | def initialize(self, template, reportUUID, cacher): 41 | self.template = template 42 | self.reportUUID = reportUUID 43 | self.cacher = cacher 44 | 45 | def get(self): 46 | if self.cacher is not None: 47 | cached_data = { 48 | 'data': self.cacher.get_all_data(), 49 | 'uuid': self.reportUUID, 50 | } 51 | else: 52 | cached_data = {'data': {}, 'uuid': self.reportUUID, } 53 | self.render(self.template, cached_data=json.dumps(cached_data)) 54 | 55 | 56 | class JsonHandler(tornado.web.RequestHandler): 57 | def initialize(self, reportUUID, cacher): 58 | self.reportUUID = reportUUID 59 | self.cacher = cacher 60 | 61 | def get(self): 62 | if self.cacher is not None: 63 | cached_data = { 64 | 'data': self.cacher.get_all_data(), 65 | 'uuid': self.reportUUID, 66 | } 67 | else: 68 | cached_data = {'data': {}, 'uuid': self.reportUUID, } 69 | self.set_status(200) 70 | self.set_header("Content-type", "application/json") 71 | self.finish(json.dumps(cached_data)) 72 | 73 | 74 | class ReportServer(object): 75 | def __init__(self, cacher): 76 | router = TornadioRouter(Client) 77 | self.server = None 78 | self.cacher = cacher 79 | self.reportUUID = uuid.uuid4().hex 80 | self.app = tornado.web.Application( 81 | router.apply_routes([ 82 | (r"/", MainHandler, dict(template='index.jade', 83 | reportUUID=self.reportUUID, 84 | cacher=cacher)), 85 | (r"/offline\.html", MainHandler, 86 | dict(template='offline.jade', 87 | reportUUID=self.reportUUID, 88 | cacher=cacher)), 89 | (r"/brief\.html$", MainHandler, 90 | dict(template='brief.jade', 91 | reportUUID=self.reportUUID, 92 | cacher=cacher)), 93 | (r"/monitoring\.html$", MainHandler, 94 | dict(template='monitoring.jade', 95 | reportUUID=self.reportUUID, 96 | cacher=cacher)), 97 | (r"/data\.json$", JsonHandler, dict(reportUUID=self.reportUUID, 98 | cacher=cacher)), 99 | ]), 100 | template_path=os.path.join( 101 | os.path.dirname(__file__), "templates"), 102 | static_path=os.path.join( 103 | os.path.dirname(__file__), "static"), 104 | debug=True, ) 105 | 106 | def serve(self): 107 | def run_server(server): 108 | tornado.ioloop.IOLoop.instance().start() 109 | 110 | self.server = SocketServer(self.app) 111 | th = Thread(target=run_server, args=(self.server, )) 112 | th.start() 113 | 114 | def stop(self): 115 | if (self.server): 116 | self.server.stop() 117 | 118 | def send(self, data): 119 | for connection in Client.CONNECTIONS: 120 | data['uuid'] = self.reportUUID 121 | connection.send(json.dumps(data)) 122 | 123 | def reload(self): 124 | for connection in Client.CONNECTIONS: 125 | connection.emit('reload') 126 | 127 | def render_offline(self): 128 | loader = template.Loader(os.path.join( 129 | os.path.dirname(__file__), "templates")) 130 | cached_data = { 131 | 'data': self.cacher.get_all_data(), 132 | 'uuid': self.reportUUID, 133 | } 134 | return loader.load('offline.jade').generate( 135 | cached_data=json.dumps(cached_data)) 136 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/static/js/angular-rickshaw.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.9.1 2 | 3 | /** 4 | Based on https://github.com/ngyewch/angular-rickshaw 5 | */ 6 | 7 | (function() { 8 | "use strict"; 9 | angular.module("angular-rickshaw", []).directive("rickshaw", function($compile) { 10 | return { 11 | restrict: "EA", 12 | scope: { 13 | options: "=rickshawOptions", 14 | series: "=rickshawSeries", 15 | features: "=rickshawFeatures" 16 | }, 17 | link: function(scope, element, attrs) { 18 | var getSettings, update; 19 | getSettings = function(el) { 20 | var settings; 21 | settings = angular.copy(scope.options); 22 | settings.element = el; 23 | settings.series = scope.series; 24 | return settings; 25 | }; 26 | update = function() { 27 | var graphEl, highlighter, hoverConfig, hoverDetail, i, legend, legendEl, mainEl, palette, settings, shelving, time, xAxis, xAxisConfig, yAxis, yAxisConfig, yAxisSecondary, yAxisSecondaryConfig; 28 | mainEl = angular.element(element); 29 | mainEl.append(graphEl); 30 | mainEl.empty(); 31 | graphEl = $compile("
")(scope); 32 | mainEl.append(graphEl); 33 | settings = getSettings(graphEl[0]); 34 | Rickshaw.Series.zeroFill(settings.series); 35 | scope.graph = new Rickshaw.Graph(settings); 36 | if (scope.features && scope.features.hover) { 37 | hoverConfig = { 38 | graph: scope.graph 39 | }; 40 | hoverConfig.xFormatter = scope.features.hover.xFormatter; 41 | hoverConfig.yFormatter = scope.features.hover.yFormatter; 42 | hoverConfig.formatter = scope.features.hover.formatter; 43 | hoverDetail = new Rickshaw.Graph.HoverDetail(hoverConfig); 44 | } 45 | if (scope.features && scope.features.palette) { 46 | palette = new Rickshaw.Color.Palette({ 47 | scheme: scope.features.palette 48 | }); 49 | i = 0; 50 | while (i < settings.series.length) { 51 | settings.series[i].color = palette.color(); 52 | i++; 53 | } 54 | } 55 | scope.graph.render(); 56 | if (scope.features && scope.features.xAxis) { 57 | xAxisConfig = { 58 | graph: scope.graph 59 | }; 60 | if (scope.features.xAxis.timeUnit) { 61 | time = new Rickshaw.Fixtures.Time(); 62 | xAxisConfig.timeUnit = time.unit(scope.features.xAxis.timeUnit); 63 | } 64 | xAxis = new Rickshaw.Graph.Axis.Time(xAxisConfig); 65 | xAxis.render(); 66 | } 67 | if (scope.features && scope.features.yAxis) { 68 | yAxisConfig = { 69 | graph: scope.graph, 70 | orientation: 'left' 71 | }; 72 | if (scope.features.yAxis.tickFormat) { 73 | yAxisConfig.tickFormat = Rickshaw.Fixtures.Number[scope.features.yAxis.tickFormat]; 74 | } 75 | yAxis = new Rickshaw.Graph.Axis.Y(yAxisConfig); 76 | yAxis.render(); 77 | } 78 | if (scope.features && scope.features.yAxisSecondary) { 79 | yAxisSecondaryConfig = { 80 | graph: scope.graph, 81 | orientation: 'right' 82 | }; 83 | if (scope.features.yAxisSecondary.tickFormat) { 84 | yAxisSecondaryConfig.tickFormat = Rickshaw.Fixtures.Number[scope.features.yAxisSecondary.tickFormat]; 85 | } 86 | yAxisSecondary = new Rickshaw.Graph.Axis.Y(yAxisSecondaryConfig); 87 | yAxisSecondary.render(); 88 | } 89 | if (scope.features && scope.features.legend) { 90 | legendEl = $compile("
")(scope); 91 | mainEl.append(legendEl); 92 | legend = new Rickshaw.Graph.Legend({ 93 | graph: scope.graph, 94 | element: legendEl[0] 95 | }); 96 | if (scope.features.legend.toggle) { 97 | shelving = new Rickshaw.Graph.Behavior.Series.Toggle({ 98 | graph: scope.graph, 99 | legend: legend 100 | }); 101 | } 102 | if (scope.features.legend.highlight) { 103 | highlighter = new Rickshaw.Graph.Behavior.Series.Highlight({ 104 | graph: scope.graph, 105 | legend: legend 106 | }); 107 | } 108 | } 109 | }; 110 | scope.graph = void 0; 111 | scope.$watch("options", function(newValue, oldValue) { 112 | if (!angular.equals(newValue, oldValue)) { 113 | update(); 114 | } 115 | }); 116 | scope.$watch("series", function(newValue, oldValue) { 117 | if (!angular.equals(newValue, oldValue)) { 118 | update(); 119 | } 120 | }); 121 | scope.$watch("features", function(newValue, oldValue) { 122 | if (!angular.equals(newValue, oldValue)) { 123 | update(); 124 | } 125 | }); 126 | scope.$on("DataUpdated", function() { 127 | return scope.graph.update(); 128 | }); 129 | return update(); 130 | }, 131 | controller: function($scope, $element, $attrs) {} 132 | }; 133 | }); 134 | 135 | }).call(this); 136 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/static/js/ws.coffee: -------------------------------------------------------------------------------- 1 | app = angular.module("ng-tank-report", ["angular-rickshaw"]) 2 | 3 | collect_subtree = (storage, subtree, ts) -> 4 | for key, node of subtree 5 | if typeof node is 'number' or typeof node is 'array' 6 | storage[key].push 7 | x: ts 8 | y: node 9 | else 10 | collect_subtree(storage[key], node, ts) 11 | 12 | collect_stats = (stats) -> 13 | result = {} 14 | for statItem in stats 15 | for metric, value of statItem.metrics 16 | result[metric] = [] if not result[metric] 17 | result[metric].push 18 | x: +statItem.ts 19 | y: +value 20 | result 21 | 22 | collect_monitoring = (monitoring) -> 23 | result = {} 24 | for monItem in monitoring 25 | for host, hostData of monItem.data 26 | result[host] = {comment: hostData.comment, metrics: {}} if not result[host] 27 | for metric, value of hostData.metrics 28 | subgroup = metric.split("_", 1) 29 | result[host].metrics[subgroup] = {} if not result[host].metrics[subgroup] 30 | result[host].metrics[subgroup][metric] = [] if not result[host].metrics[subgroup][metric] 31 | result[host].metrics[subgroup][metric].push 32 | x: +monItem.timestamp 33 | y: +value 34 | result 35 | 36 | collect_quantiles = (data) -> 37 | 38 | result = { 39 | overall: 40 | interval_real: 41 | q: {} 42 | tagged: {} 43 | } 44 | for dataItem in data 45 | for i in [0...dataItem.overall.interval_real.q.q.length] 46 | result.overall.interval_real.q[dataItem.overall.interval_real.q.q[i]] = [] if not result.overall.interval_real.q[dataItem.overall.interval_real.q.q[i]] 47 | result.overall.interval_real.q[dataItem.overall.interval_real.q.q[i]].push 48 | x: dataItem.ts 49 | y: dataItem.overall.interval_real.q.value[i] 50 | for tag, tagData of dataItem.tagged 51 | result.tagged[tag] = {interval_real: { q: {}}} if not result.tagged[tag] 52 | 53 | for i in [0...tagData.interval_real.q.q.length] 54 | result.tagged[tag].interval_real.q[tagData.interval_real.q.q[i]] = [] if not result.tagged[tag].interval_real.q[tagData.interval_real.q.q[i]] 55 | result.tagged[tag].interval_real.q[tagData.interval_real.q.q[i]].push 56 | x: dataItem.ts 57 | y: tagData.interval_real.q.value[i] 58 | result 59 | 60 | app.controller "TankReport", ($scope, $element) -> 61 | $scope.status = "Disconnected" 62 | $scope.data = document.cached_data.data 63 | $scope.uuid = document.cached_data.uuid 64 | $scope.updateData = (tankData) -> 65 | for ts, storages of tankData 66 | for storage, data of storages 67 | collect_subtree $scope.data[storage], data, +ts 68 | $scope.$broadcast 'DataUpdated' 69 | $scope.buildSeries = () -> 70 | cache = document.cached_data.data 71 | if cache.stats and cache.data and cache.monitoring 72 | overallData = {} # TODO 73 | taggedData = {} # TODO 74 | quantilesData = collect_quantiles(cache.data) 75 | statsData = collect_stats(cache.stats) 76 | monitoringData = collect_monitoring(cache.monitoring) 77 | else 78 | overallData = {} 79 | taggedData = {} 80 | quantilesData = {} 81 | statsData = {} 82 | monitoringData = {} 83 | setTimeout((() -> location.reload(true)), 3000) 84 | areaGraphs = ['CPU', 'Memory'] 85 | # $scope.monitoringData = ( 86 | # ({ 87 | # hostname: hostname 88 | # groups: ({ 89 | # name: groupName 90 | # features: 91 | # palette: 'spectrum14' 92 | # hover: {} 93 | # xAxis: {} 94 | # yAxis: {} 95 | # legend: 96 | # toggle: true 97 | # highlight: true 98 | # options: 99 | # renderer: if groupName in areaGraphs then 'area' else 'line' 100 | # series: ({ 101 | # name: name 102 | # data: data 103 | # } for name, data of series) 104 | # } for groupName, series of groups) 105 | # } for hostname, groups of monitoringData) 106 | # ) 107 | $scope.monitoringGraphData = ({ 108 | hostname: hostname 109 | charts: ({ 110 | name: subgroup 111 | features: 112 | palette: 'spectrum14' 113 | hover: {} 114 | xAxis: {} 115 | yAxis: {} 116 | ySecondaryAxis: 117 | scale: null 118 | legend: 119 | toggle: true 120 | highlight: true 121 | options: 122 | renderer: if subgroup in areaGraphs then "area" else "line" 123 | series: ({ 124 | name: metric 125 | data: series 126 | } for metric, series of metrics) 127 | }) for subgroup, metrics of hostData.metrics 128 | } for hostname, hostData of monitoringData 129 | ) 130 | $scope.quantilesGraphData = 131 | name: "Response time quantiles" 132 | features: 133 | palette: 'classic9' 134 | hover: {} 135 | xAxis: {} 136 | yAxis: {} 137 | legend: 138 | toggle: true 139 | highlight: true 140 | options: 141 | renderer: 'area' 142 | stack: false 143 | height: $element[0].offsetHeight - 45 - 62 144 | series: ({ 145 | name: name 146 | data: data 147 | } for name, data of quantilesData.overall.interval_real.q).sort (a, b) -> 148 | return if parseFloat(a.name) <= parseFloat(b.name) then 1 else -1 149 | $scope.rps = 150 | name: "Requests per second" 151 | features: 152 | palette: 'spectrum14' 153 | hover: {} 154 | xAxis: {} 155 | yAxis: {} 156 | legend: 157 | toggle: true 158 | highlight: true 159 | options: 160 | renderer: 'line' 161 | series: [{ 162 | name: "req/s" 163 | color: "red" 164 | data: statsData.reqps 165 | }] 166 | 167 | $scope.protoCodes = 168 | name: "Protocol return codes" 169 | features: 170 | palette: 'spectrum14' 171 | hover: {} 172 | xAxis: {} 173 | yAxis: {} 174 | legend: 175 | toggle: true 176 | highlight: true 177 | options: 178 | renderer: 'area' 179 | stack: true 180 | height: $element[0].offsetHeight - 45 - 62 181 | series: ({ 182 | name: name 183 | data: data 184 | } for name, data of overallData.http_codes).sort (a, b) -> 185 | return if parseFloat(a.name) <= parseFloat(b.name) then 1 else -1 186 | 187 | $scope.netCodes = 188 | name: "Network return codes" 189 | features: 190 | palette: 'spectrum14' 191 | hover: {} 192 | xAxis: {} 193 | yAxis: {} 194 | legend: 195 | toggle: true 196 | highlight: true 197 | options: 198 | renderer: 'area' 199 | stack: true 200 | height: $element[0].offsetHeight - 45 - 62 201 | series: ({ 202 | name: name 203 | data: data 204 | } for name, data of overallData.net_codes).sort (a, b) -> 205 | return if parseFloat(a.name) <= parseFloat(b.name) then 1 else -1 206 | 207 | $scope.buildSeries() 208 | # 209 | # conn = new io.connect("http://#{window.location.host}", 210 | # 'reconnection limit' : 1000 211 | # 'max reconnection attempts' : 'Infinity' 212 | # ) 213 | # setInterval(( 214 | # () ->conn.emit('heartbeat') 215 | # ), 3000) 216 | # conn.on 'connect', () => 217 | # console.log("Connection opened...") 218 | # $scope.status = "Connected" 219 | # 220 | # $scope.$apply() 221 | # 222 | # conn.on 'disconnect', () => 223 | # console.log("Connection closed...") 224 | # $scope.status = "Disonnected" 225 | # $scope.$apply() 226 | # conn.on 'reload', () => 227 | # location.reload(true) 228 | # conn.on 'message', (msg) => 229 | # tankData = JSON.parse msg 230 | # if tankData.uuid and $scope.uuid != tankData.uuid 231 | # location.reload(true) 232 | # else 233 | # $scope.updateData(tankData.data) 234 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/static/css/rickshaw.css: -------------------------------------------------------------------------------- 1 | .rickshaw_graph .detail { 2 | pointer-events: none; 3 | position: absolute; 4 | top: 0; 5 | z-index: 2; 6 | background: rgba(0, 0, 0, 0.1); 7 | bottom: 0; 8 | width: 1px; 9 | transition: opacity 0.25s linear; 10 | -moz-transition: opacity 0.25s linear; 11 | -o-transition: opacity 0.25s linear; 12 | -webkit-transition: opacity 0.25s linear; 13 | } 14 | .rickshaw_graph .detail.inactive { 15 | opacity: 0; 16 | } 17 | .rickshaw_graph .detail .item.active { 18 | opacity: 1; 19 | } 20 | .rickshaw_graph .detail .x_label { 21 | font-family: Arial, sans-serif; 22 | border-radius: 3px; 23 | padding: 6px; 24 | opacity: 0.5; 25 | border: 1px solid #e0e0e0; 26 | font-size: 12px; 27 | position: absolute; 28 | background: white; 29 | white-space: nowrap; 30 | } 31 | .rickshaw_graph .detail .x_label.left { 32 | left: 0; 33 | } 34 | .rickshaw_graph .detail .x_label.right { 35 | right: 0; 36 | } 37 | .rickshaw_graph .detail .item { 38 | position: absolute; 39 | z-index: 2; 40 | border-radius: 3px; 41 | padding: 0.25em; 42 | font-size: 12px; 43 | font-family: Arial, sans-serif; 44 | opacity: 0; 45 | background: rgba(0, 0, 0, 0.4); 46 | color: white; 47 | border: 1px solid rgba(0, 0, 0, 0.4); 48 | margin-left: 1em; 49 | margin-right: 1em; 50 | margin-top: -1em; 51 | white-space: nowrap; 52 | } 53 | .rickshaw_graph .detail .item.left { 54 | left: 0; 55 | } 56 | .rickshaw_graph .detail .item.right { 57 | right: 0; 58 | } 59 | .rickshaw_graph .detail .item.active { 60 | opacity: 1; 61 | background: rgba(0, 0, 0, 0.8); 62 | } 63 | .rickshaw_graph .detail .item:after { 64 | position: absolute; 65 | display: block; 66 | width: 0; 67 | height: 0; 68 | 69 | content: ""; 70 | 71 | border: 5px solid transparent; 72 | } 73 | .rickshaw_graph .detail .item.left:after { 74 | top: 1em; 75 | left: -5px; 76 | margin-top: -5px; 77 | border-right-color: rgba(0, 0, 0, 0.8); 78 | border-left-width: 0; 79 | } 80 | .rickshaw_graph .detail .item.right:after { 81 | top: 1em; 82 | right: -5px; 83 | margin-top: -5px; 84 | border-left-color: rgba(0, 0, 0, 0.8); 85 | border-right-width: 0; 86 | } 87 | .rickshaw_graph .detail .dot { 88 | width: 4px; 89 | height: 4px; 90 | margin-left: -3px; 91 | margin-top: -3.5px; 92 | border-radius: 5px; 93 | position: absolute; 94 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.6); 95 | box-sizing: content-box; 96 | -moz-box-sizing: content-box; 97 | background: white; 98 | border-width: 2px; 99 | border-style: solid; 100 | display: none; 101 | background-clip: padding-box; 102 | } 103 | .rickshaw_graph .detail .dot.active { 104 | display: block; 105 | } 106 | /* graph */ 107 | 108 | .rickshaw_graph { 109 | position: relative; 110 | } 111 | .rickshaw_graph svg { 112 | display: block; 113 | overflow: hidden; 114 | } 115 | 116 | /* ticks */ 117 | 118 | .rickshaw_graph .x_tick { 119 | position: absolute; 120 | top: 0; 121 | bottom: 0; 122 | width: 0px; 123 | border-left: 1px dotted rgba(0, 0, 0, 0.2); 124 | pointer-events: none; 125 | } 126 | .rickshaw_graph .x_tick .title { 127 | position: absolute; 128 | font-size: 12px; 129 | font-family: Arial, sans-serif; 130 | opacity: 0.5; 131 | white-space: nowrap; 132 | margin-left: 3px; 133 | bottom: 1px; 134 | } 135 | 136 | /* annotations */ 137 | 138 | .rickshaw_annotation_timeline { 139 | height: 1px; 140 | border-top: 1px solid #e0e0e0; 141 | margin-top: 10px; 142 | position: relative; 143 | } 144 | .rickshaw_annotation_timeline .annotation { 145 | position: absolute; 146 | height: 6px; 147 | width: 6px; 148 | margin-left: -2px; 149 | top: -3px; 150 | border-radius: 5px; 151 | background-color: rgba(0, 0, 0, 0.25); 152 | } 153 | .rickshaw_graph .annotation_line { 154 | position: absolute; 155 | top: 0; 156 | bottom: -6px; 157 | width: 0px; 158 | border-left: 2px solid rgba(0, 0, 0, 0.3); 159 | display: none; 160 | } 161 | .rickshaw_graph .annotation_line.active { 162 | display: block; 163 | } 164 | 165 | .rickshaw_graph .annotation_range { 166 | background: rgba(0, 0, 0, 0.1); 167 | display: none; 168 | position: absolute; 169 | top: 0; 170 | bottom: -6px; 171 | } 172 | .rickshaw_graph .annotation_range.active { 173 | display: block; 174 | } 175 | .rickshaw_graph .annotation_range.active.offscreen { 176 | display: none; 177 | } 178 | 179 | .rickshaw_annotation_timeline .annotation .content { 180 | background: white; 181 | color: black; 182 | opacity: 0.9; 183 | padding: 5px 5px; 184 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.8); 185 | border-radius: 3px; 186 | position: relative; 187 | z-index: 20; 188 | font-size: 12px; 189 | padding: 6px 8px 8px; 190 | top: 18px; 191 | left: -11px; 192 | width: 160px; 193 | display: none; 194 | cursor: pointer; 195 | } 196 | .rickshaw_annotation_timeline .annotation .content:before { 197 | content: "\25b2"; 198 | position: absolute; 199 | top: -11px; 200 | color: white; 201 | text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.8); 202 | } 203 | .rickshaw_annotation_timeline .annotation.active, 204 | .rickshaw_annotation_timeline .annotation:hover { 205 | background-color: rgba(0, 0, 0, 0.8); 206 | cursor: none; 207 | } 208 | .rickshaw_annotation_timeline .annotation .content:hover { 209 | z-index: 50; 210 | } 211 | .rickshaw_annotation_timeline .annotation.active .content { 212 | display: block; 213 | } 214 | .rickshaw_annotation_timeline .annotation:hover .content { 215 | display: block; 216 | z-index: 50; 217 | } 218 | .rickshaw_graph .y_axis, 219 | .rickshaw_graph .x_axis_d3 { 220 | fill: none; 221 | } 222 | .rickshaw_graph .y_ticks .tick line, 223 | .rickshaw_graph .x_ticks_d3 .tick { 224 | stroke: rgba(0, 0, 0, 0.16); 225 | stroke-width: 2px; 226 | shape-rendering: crisp-edges; 227 | pointer-events: none; 228 | } 229 | .rickshaw_graph .y_grid .tick, 230 | .rickshaw_graph .x_grid_d3 .tick { 231 | z-index: -1; 232 | stroke: rgba(0, 0, 0, 0.20); 233 | stroke-width: 1px; 234 | stroke-dasharray: 1 1; 235 | } 236 | .rickshaw_graph .y_grid .tick[data-y-value="0"] { 237 | stroke-dasharray: 1 0; 238 | } 239 | .rickshaw_graph .y_grid path, 240 | .rickshaw_graph .x_grid_d3 path { 241 | fill: none; 242 | stroke: none; 243 | } 244 | .rickshaw_graph .y_ticks path, 245 | .rickshaw_graph .x_ticks_d3 path { 246 | fill: none; 247 | stroke: #808080; 248 | } 249 | .rickshaw_graph .y_ticks text, 250 | .rickshaw_graph .x_ticks_d3 text { 251 | opacity: 0.5; 252 | font-size: 12px; 253 | pointer-events: none; 254 | } 255 | .rickshaw_graph .x_tick.glow .title, 256 | .rickshaw_graph .y_ticks.glow text { 257 | fill: black; 258 | color: black; 259 | text-shadow: 260 | -1px 1px 0 rgba(255, 255, 255, 0.1), 261 | 1px -1px 0 rgba(255, 255, 255, 0.1), 262 | 1px 1px 0 rgba(255, 255, 255, 0.1), 263 | 0px 1px 0 rgba(255, 255, 255, 0.1), 264 | 0px -1px 0 rgba(255, 255, 255, 0.1), 265 | 1px 0px 0 rgba(255, 255, 255, 0.1), 266 | -1px 0px 0 rgba(255, 255, 255, 0.1), 267 | -1px -1px 0 rgba(255, 255, 255, 0.1); 268 | } 269 | .rickshaw_graph .x_tick.inverse .title, 270 | .rickshaw_graph .y_ticks.inverse text { 271 | fill: white; 272 | color: white; 273 | text-shadow: 274 | -1px 1px 0 rgba(0, 0, 0, 0.8), 275 | 1px -1px 0 rgba(0, 0, 0, 0.8), 276 | 1px 1px 0 rgba(0, 0, 0, 0.8), 277 | 0px 1px 0 rgba(0, 0, 0, 0.8), 278 | 0px -1px 0 rgba(0, 0, 0, 0.8), 279 | 1px 0px 0 rgba(0, 0, 0, 0.8), 280 | -1px 0px 0 rgba(0, 0, 0, 0.8), 281 | -1px -1px 0 rgba(0, 0, 0, 0.8); 282 | } 283 | .rickshaw_legend { 284 | font-family: Arial; 285 | font-size: 12px; 286 | color: white; 287 | background: #404040; 288 | display: inline-block; 289 | padding: 12px 5px; 290 | border-radius: 2px; 291 | position: relative; 292 | } 293 | .rickshaw_legend:hover { 294 | z-index: 10; 295 | } 296 | .rickshaw_legend .swatch { 297 | width: 10px; 298 | height: 10px; 299 | border: 1px solid rgba(0, 0, 0, 0.2); 300 | } 301 | .rickshaw_legend .line { 302 | clear: both; 303 | line-height: 140%; 304 | padding-right: 15px; 305 | } 306 | .rickshaw_legend .line .swatch { 307 | display: inline-block; 308 | margin-right: 3px; 309 | border-radius: 2px; 310 | } 311 | .rickshaw_legend .label { 312 | margin: 0; 313 | white-space: nowrap; 314 | display: inline; 315 | font-size: inherit; 316 | background-color: transparent; 317 | color: inherit; 318 | font-weight: normal; 319 | line-height: normal; 320 | padding: 0px; 321 | text-shadow: none; 322 | } 323 | .rickshaw_legend .action:hover { 324 | opacity: 0.6; 325 | } 326 | .rickshaw_legend .action { 327 | margin-right: 0.2em; 328 | font-size: 10px; 329 | opacity: 0.2; 330 | cursor: pointer; 331 | font-size: 14px; 332 | } 333 | .rickshaw_legend .line.disabled { 334 | opacity: 0.4; 335 | } 336 | .rickshaw_legend ul { 337 | list-style-type: none; 338 | margin: 0; 339 | padding: 0; 340 | margin: 2px; 341 | cursor: pointer; 342 | } 343 | .rickshaw_legend li { 344 | display: inline-block; 345 | padding: 0 0 0 2px; 346 | min-width: 80px; 347 | white-space: nowrap; 348 | } 349 | .rickshaw_legend li:hover { 350 | background: rgba(255, 255, 255, 0.08); 351 | border-radius: 3px; 352 | } 353 | .rickshaw_legend li:active { 354 | background: rgba(255, 255, 255, 0.2); 355 | border-radius: 3px; 356 | } 357 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/static/js/ws.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.9.1 2 | (function() { 3 | var app, collect_monitoring, collect_quantiles, collect_stats, collect_subtree, 4 | indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; 5 | 6 | app = angular.module("ng-tank-report", ["angular-rickshaw"]); 7 | 8 | collect_subtree = function(storage, subtree, ts) { 9 | var key, node, results; 10 | results = []; 11 | for (key in subtree) { 12 | node = subtree[key]; 13 | if (typeof node === 'number' || typeof node === 'array') { 14 | results.push(storage[key].push({ 15 | x: ts, 16 | y: node 17 | })); 18 | } else { 19 | results.push(collect_subtree(storage[key], node, ts)); 20 | } 21 | } 22 | return results; 23 | }; 24 | 25 | collect_stats = function(stats) { 26 | var j, len, metric, ref, result, statItem, value; 27 | result = {}; 28 | for (j = 0, len = stats.length; j < len; j++) { 29 | statItem = stats[j]; 30 | ref = statItem.metrics; 31 | for (metric in ref) { 32 | value = ref[metric]; 33 | if (!result[metric]) { 34 | result[metric] = []; 35 | } 36 | result[metric].push({ 37 | x: +statItem.ts, 38 | y: +value 39 | }); 40 | } 41 | } 42 | return result; 43 | }; 44 | 45 | collect_monitoring = function(monitoring) { 46 | var host, hostData, j, len, metric, monItem, ref, ref1, result, subgroup, value; 47 | result = {}; 48 | for (j = 0, len = monitoring.length; j < len; j++) { 49 | monItem = monitoring[j]; 50 | ref = monItem.data; 51 | for (host in ref) { 52 | hostData = ref[host]; 53 | if (!result[host]) { 54 | result[host] = { 55 | comment: hostData.comment, 56 | metrics: {} 57 | }; 58 | } 59 | ref1 = hostData.metrics; 60 | for (metric in ref1) { 61 | value = ref1[metric]; 62 | subgroup = metric.split("_", 1); 63 | if (!result[host].metrics[subgroup]) { 64 | result[host].metrics[subgroup] = {}; 65 | } 66 | if (!result[host].metrics[subgroup][metric]) { 67 | result[host].metrics[subgroup][metric] = []; 68 | } 69 | result[host].metrics[subgroup][metric].push({ 70 | x: +monItem.timestamp, 71 | y: +value 72 | }); 73 | } 74 | } 75 | } 76 | return result; 77 | }; 78 | 79 | collect_quantiles = function(data) { 80 | var dataItem, i, j, k, l, len, ref, ref1, ref2, result, tag, tagData; 81 | result = { 82 | overall: { 83 | interval_real: { 84 | q: {} 85 | } 86 | }, 87 | tagged: {} 88 | }; 89 | for (j = 0, len = data.length; j < len; j++) { 90 | dataItem = data[j]; 91 | for (i = k = 0, ref = dataItem.overall.interval_real.q.q.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) { 92 | if (!result.overall.interval_real.q[dataItem.overall.interval_real.q.q[i]]) { 93 | result.overall.interval_real.q[dataItem.overall.interval_real.q.q[i]] = []; 94 | } 95 | result.overall.interval_real.q[dataItem.overall.interval_real.q.q[i]].push({ 96 | x: dataItem.ts, 97 | y: dataItem.overall.interval_real.q.value[i] 98 | }); 99 | } 100 | ref1 = dataItem.tagged; 101 | for (tag in ref1) { 102 | tagData = ref1[tag]; 103 | if (!result.tagged[tag]) { 104 | result.tagged[tag] = { 105 | interval_real: { 106 | q: {} 107 | } 108 | }; 109 | } 110 | for (i = l = 0, ref2 = tagData.interval_real.q.q.length; 0 <= ref2 ? l < ref2 : l > ref2; i = 0 <= ref2 ? ++l : --l) { 111 | if (!result.tagged[tag].interval_real.q[tagData.interval_real.q.q[i]]) { 112 | result.tagged[tag].interval_real.q[tagData.interval_real.q.q[i]] = []; 113 | } 114 | result.tagged[tag].interval_real.q[tagData.interval_real.q.q[i]].push({ 115 | x: dataItem.ts, 116 | y: tagData.interval_real.q.value[i] 117 | }); 118 | } 119 | } 120 | } 121 | return result; 122 | }; 123 | 124 | app.controller("TankReport", function($scope, $element) { 125 | $scope.status = "Disconnected"; 126 | $scope.data = document.cached_data.data; 127 | $scope.uuid = document.cached_data.uuid; 128 | $scope.updateData = function(tankData) { 129 | var data, storage, storages, ts; 130 | for (ts in tankData) { 131 | storages = tankData[ts]; 132 | for (storage in storages) { 133 | data = storages[storage]; 134 | collect_subtree($scope.data[storage], data, +ts); 135 | } 136 | } 137 | return $scope.$broadcast('DataUpdated'); 138 | }; 139 | $scope.buildSeries = function() { 140 | var areaGraphs, cache, data, hostData, hostname, metric, metrics, monitoringData, name, overallData, quantilesData, series, statsData, subgroup, taggedData; 141 | cache = document.cached_data.data; 142 | if (cache.stats && cache.data && cache.monitoring) { 143 | overallData = {}; 144 | taggedData = {}; 145 | quantilesData = collect_quantiles(cache.data); 146 | statsData = collect_stats(cache.stats); 147 | monitoringData = collect_monitoring(cache.monitoring); 148 | } else { 149 | overallData = {}; 150 | taggedData = {}; 151 | quantilesData = {}; 152 | statsData = {}; 153 | monitoringData = {}; 154 | setTimeout((function() { 155 | return location.reload(true); 156 | }), 3000); 157 | } 158 | areaGraphs = ['CPU', 'Memory']; 159 | $scope.monitoringGraphData = (function() { 160 | var results; 161 | results = []; 162 | for (hostname in monitoringData) { 163 | hostData = monitoringData[hostname]; 164 | results.push({ 165 | hostname: hostname, 166 | charts: (function() { 167 | var ref, results1; 168 | ref = hostData.metrics; 169 | results1 = []; 170 | for (subgroup in ref) { 171 | metrics = ref[subgroup]; 172 | results1.push({ 173 | name: subgroup, 174 | features: { 175 | palette: 'spectrum14', 176 | hover: {}, 177 | xAxis: {}, 178 | yAxis: {}, 179 | ySecondaryAxis: { 180 | scale: null 181 | }, 182 | legend: { 183 | toggle: true, 184 | highlight: true 185 | } 186 | }, 187 | options: { 188 | renderer: indexOf.call(areaGraphs, subgroup) >= 0 ? "area" : "line" 189 | }, 190 | series: (function() { 191 | var results2; 192 | results2 = []; 193 | for (metric in metrics) { 194 | series = metrics[metric]; 195 | results2.push({ 196 | name: metric, 197 | data: series 198 | }); 199 | } 200 | return results2; 201 | })() 202 | }); 203 | } 204 | return results1; 205 | })() 206 | }); 207 | } 208 | return results; 209 | })(); 210 | $scope.quantilesGraphData = { 211 | name: "Response time quantiles", 212 | features: { 213 | palette: 'classic9', 214 | hover: {}, 215 | xAxis: {}, 216 | yAxis: {}, 217 | legend: { 218 | toggle: true, 219 | highlight: true 220 | } 221 | }, 222 | options: { 223 | renderer: 'area', 224 | stack: false, 225 | height: $element[0].offsetHeight - 45 - 62 226 | }, 227 | series: ((function() { 228 | var ref, results; 229 | ref = quantilesData.overall.interval_real.q; 230 | results = []; 231 | for (name in ref) { 232 | data = ref[name]; 233 | results.push({ 234 | name: name, 235 | data: data 236 | }); 237 | } 238 | return results; 239 | })()).sort(function(a, b) { 240 | if (parseFloat(a.name) <= parseFloat(b.name)) { 241 | return 1; 242 | } else { 243 | return -1; 244 | } 245 | }) 246 | }; 247 | $scope.rps = { 248 | name: "Requests per second", 249 | features: { 250 | palette: 'spectrum14', 251 | hover: {}, 252 | xAxis: {}, 253 | yAxis: {}, 254 | legend: { 255 | toggle: true, 256 | highlight: true 257 | } 258 | }, 259 | options: { 260 | renderer: 'line' 261 | }, 262 | series: [ 263 | { 264 | name: "req/s", 265 | color: "red", 266 | data: statsData.reqps 267 | } 268 | ] 269 | }; 270 | $scope.protoCodes = { 271 | name: "Protocol return codes", 272 | features: { 273 | palette: 'spectrum14', 274 | hover: {}, 275 | xAxis: {}, 276 | yAxis: {}, 277 | legend: { 278 | toggle: true, 279 | highlight: true 280 | } 281 | }, 282 | options: { 283 | renderer: 'area', 284 | stack: true, 285 | height: $element[0].offsetHeight - 45 - 62 286 | }, 287 | series: ((function() { 288 | var ref, results; 289 | ref = overallData.http_codes; 290 | results = []; 291 | for (name in ref) { 292 | data = ref[name]; 293 | results.push({ 294 | name: name, 295 | data: data 296 | }); 297 | } 298 | return results; 299 | })()).sort(function(a, b) { 300 | if (parseFloat(a.name) <= parseFloat(b.name)) { 301 | return 1; 302 | } else { 303 | return -1; 304 | } 305 | }) 306 | }; 307 | return $scope.netCodes = { 308 | name: "Network return codes", 309 | features: { 310 | palette: 'spectrum14', 311 | hover: {}, 312 | xAxis: {}, 313 | yAxis: {}, 314 | legend: { 315 | toggle: true, 316 | highlight: true 317 | } 318 | }, 319 | options: { 320 | renderer: 'area', 321 | stack: true, 322 | height: $element[0].offsetHeight - 45 - 62 323 | }, 324 | series: ((function() { 325 | var ref, results; 326 | ref = overallData.net_codes; 327 | results = []; 328 | for (name in ref) { 329 | data = ref[name]; 330 | results.push({ 331 | name: name, 332 | data: data 333 | }); 334 | } 335 | return results; 336 | })()).sort(function(a, b) { 337 | if (parseFloat(a.name) <= parseFloat(b.name)) { 338 | return 1; 339 | } else { 340 | return -1; 341 | } 342 | }) 343 | }; 344 | }; 345 | return $scope.buildSeries(); 346 | }); 347 | 348 | }).call(this); 349 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/static/js/vendor/cubism.min.js: -------------------------------------------------------------------------------- 1 | (function(a){function d(a){return a}function e(){}function j(a){return Math.floor(a/1e3)}function k(a){var b=a.indexOf("|"),c=a.substring(0,b),d=c.lastIndexOf(","),e=c.lastIndexOf(",",d-1),f=c.lastIndexOf(",",e-1),g=c.substring(f+1,e)*1e3,h=c.substring(d+1)*1e3;return a.substring(b+1).split(",").slice(1).map(function(a){return+a})}function l(a){if(!(a instanceof e))throw new Error("invalid context");this.context=a}function o(a,b){return function(c,d,e,f){a(new Date(+c+b),new Date(+d+b),e,f)}}function p(a,b){l.call(this,a),b=+b;var c=b+"";this.valueOf=function(){return b},this.toString=function(){return c}}function r(a,b){function c(b,c){if(c instanceof l){if(b.context!==c.context)throw new Error("mismatch context")}else c=new p(b.context,c);l.call(this,b.context),this.left=b,this.right=c,this.toString=function(){return b+" "+a+" "+c}}var d=c.prototype=Object.create(l.prototype);return d.valueAt=function(a){return b(this.left.valueAt(a),this.right.valueAt(a))},d.shift=function(a){return new c(this.left.shift(a),this.right.shift(a))},d.on=function(a,b){return arguments.length<2?this.left.on(a):(this.left.on(a,b),this.right.on(a,b),this)},function(a){return new c(this,a)}}function u(a){return a&16777214}function v(a){return(a+1&16777214)-1}function z(a){a.style("position","absolute").style("top",0).style("bottom",0).style("width","1px").style("pointer-events","none")}function A(a){return a+"px"}var b=a.cubism={version:"1.6.0"},c=0;b.option=function(a,c){var d=b.options(a);return d.length?d[0]:c},b.options=function(a,b){var c=location.search.substring(1).split("&"),d=[],e=-1,f=c.length,g;while(++e0&&a.focus(--o);break;case 39:o==null&&(o=d-2),o=c)return c;if(a<=b)return b;var d,e,f;for(f=a;f<=c;f++){d=avail_rsts.indexOf(f);if(d>-1){e=avail_rsts[d];break}}var g;for(f=a;f>=b;f--){d=avail_rsts.indexOf(f);if(d>-1){g=avail_rsts[d];break}}return e-ae?3600:(i=f(c),d>g&&i<900?900:d>h&&i<60?60:i)}var d={},e=this;auth_string="Basic "+btoa(a+":"+c),avail_rsts=[1,60,900,3600];var j=function(a){function d(b,d,e){var f="compose="+a+"&start_time="+b+"&end_time="+d+"&resolution="+g(b,d,e);return c+"?"+f}function e(a,b,c,d){var e=[];for(i=a;i<=b;i+=c){var f=[];while(d.length&&d[0].measure_time<=i)f.push(d.shift().value);var g;f.length?g=f.reduce(function(a,b){return a+b})/f.length:g=e.length?e[e.length-1]:0,e.push(g)}return e}var c="https://metrics-api.librato.com/v1/metrics";return request={},request.fire=function(a,c,f,g){function i(j){d3.json(j).header("X-Requested-With","XMLHttpRequest").header("Authorization",auth_string).header("Librato-User-Agent","cubism/"+b.version).get(function(b,j){if(!b){if(j.measurements.length===0)return;j.measurements[0].series.forEach(function(a){h.push(a)});var k="query"in j&&"next_time"in j.query;if(k)i(d(j.query.next_time,c,f));else{var l=e(a,c,f,h);g(l)}}})}var h=[];i(d(a,c,f))},request};return d.metric=function(a){return e.metric(function(b,c,d,e){j(a).fire(h(b),h(c),h(d),function(a){e(null,a)})},a+="")},d.toString=function(){return"librato"},d};var h=function(a){return Math.floor(a/1e3)};f.graphite=function(a){arguments.length||(a="");var b={},c=this;return b.metric=function(b){var d="sum",e=c.metric(function(c,e,f,g){var h=b;f!==1e4&&(h="summarize("+h+",'"+(f%36e5?f%6e4?f/1e3+"sec":f/6e4+"min":f/36e5+"hour")+"','"+d+"')"),d3.text(a+"/render?format=raw"+"&target="+encodeURIComponent("alias("+h+",'')")+"&from="+j(c-2*f)+"&until="+j(e-1e3),function(a){if(!a)return g(new Error("unable to load data"));g(null,k(a))})},b+="");return e.summarize=function(a){return d=a,e},e},b.find=function(b,c){d3.json(a+"/metrics/find?format=completer"+"&query="+encodeURIComponent(b),function(a){if(!a)return c(new Error("unable to find metrics"));c(null,a.metrics.map(function(a){return a.path}))})},b.toString=function(){return a},b},f.gangliaWeb=function(a){var b="",c="/ganglia2/";arguments.length&&(a.host&&(b=a.host),a.uriPathPrefix&&(c=a.uriPathPrefix,c[0]!="/"&&(c="/"+c),c[c.length-1]!="/"&&(c+="/")));var d={},e=this;return d.metric=function(a){var d=a.clusterName,f=a.metricName,g=a.hostName,h=a.isReport||!1,i=a.titleGenerator||function(a){return"clusterName:"+d+" metricName:"+f+(g?" hostName:"+g:"")},j=a.onChangeCallback,k=h?"g":"m",l=e.metric(function(a,e,h,i){function j(){return"c="+d+"&"+k+"="+f+(g?"&h="+g:"")+"&cs="+a/1e3+"&ce="+e/1e3+"&step="+h/1e3+"&graphlot=1"}d3.json(b+c+"graph.php?"+j(),function(a){if(!a)return i(new Error("Unable to fetch GangliaWeb data"));i(null,a[0].data)})},i(a));return l.toString=function(){return i(a)},j&&l.on("change",j),l},d.toString=function(){return b+c},d};var m=l.prototype;b.metric=l,m.valueAt=function(){return NaN},m.alias=function(a){return this.toString=function(){return a},this},m.extent=function(){var a=0,b=this.context.size(),c,d=Infinity,e=-Infinity;while(++ae&&(e=c);return[d,e]},m.on=function(a,b){return arguments.length<2?null:this},m.shift=function(){return this},m.on=function(){return arguments.length<2?null:this},f.metric=function(a,b){function r(b,c){var d=Math.min(j,Math.round((b-g)/i));if(!d||q)return;q=!0,d=Math.min(j,d+n);var f=new Date(c-d*i);a(f,c,i,function(a,b){q=!1;if(a)return console.warn(a);var d=isFinite(g)?Math.round((f-g)/i):0;for(var h=0,j=b.length;h1&&(e.toString=function(){return b}),e};var n=6,q=p.prototype=Object.create(l.prototype);q.valueAt=function(){return+this},q.extent=function(){return[+this,+this]},m.add=r("+",function(a,b){return a+b}),m.subtract=r("-",function(a,b){return a-b}),m.multiply=r("*",function(a,b){return a*b}),m.divide=r("/",function(a,b){return a/b}),f.horizon=function(){function o(o){o.on("mousemove.horizon",function(){a.focus(Math.round(d3.mouse(this)[0]))}).on("mouseout.horizon",function(){a.focus(null)}),o.append("canvas").attr("width",f).attr("height",g),o.append("span").attr("class","title").text(k),o.append("span").attr("class","value"),o.each(function(k,o){function B(c,d){w.save();var i=r.extent();A=i.every(isFinite),t!=null&&(i=t);var j=0,k=Math.max(-i[0],i[1]);if(this===a){if(k==y){j=f-n;var l=(c-u)/v;if(l=0)continue;w.fillRect(x,h(-C),1,q-h(-C))}}}w.restore()}function C(a){a==null&&(a=f-1);var b=r.valueAt(a);x.datum(b).text(isNaN(b)?null:l)}var p=this,q=++c,r=typeof i=="function"?i.call(p,k,o):i,s=typeof m=="function"?m.call(p,k,o):m,t=typeof j=="function"?j.call(p,k,o):j,u=-Infinity,v=a.step(),w=d3.select(p).select("canvas"),x=d3.select(p).select(".value"),y,z=s.length>>1,A;w.datum({id:q,metric:r}),w=w.node().getContext("2d"),a.on("change.horizon-"+q,B),a.on("focus.horizon-"+q,C),r.on("change.horizon-"+q,function(a,b){B(a,b),C(),A&&r.on("change.horizon-"+q,d)})})}var a=this,b="offset",e=document.createElement("canvas"),f=e.width=a.size(),g=e.height=30,h=d3.scale.linear().interpolate(d3.interpolateRound),i=d,j=null,k=d,l=d3.format(".2s"),m=["#08519c","#3182bd","#6baed6","#bdd7e7","#bae4b3","#74c476","#31a354","#006d2c"];return o.remove=function(b){function c(b){b.metric.on("change.horizon-"+b.id,null),a.on("change.horizon-"+b.id,null),a.on("focus.horizon-"+b.id,null)}b.on("mousemove.horizon",null).on("mouseout.horizon",null),b.selectAll("canvas").each(c).remove(),b.selectAll(".title,.value").remove()},o.mode=function(a){return arguments.length?(b=a+"",o):b},o.height=function(a){return arguments.length?(e.height=g=+a,o):g},o.metric=function(a){return arguments.length?(i=a,o):i},o.scale=function(a){return arguments.length?(h=a,o):h},o.extent=function(a){return arguments.length?(j=a,o):j},o.title=function(a){return arguments.length?(k=a,o):k},o.format=function(a){return arguments.length?(l=a,o):l},o.colors=function(a){return arguments.length?(m=a,o):m},o},f.comparison=function(){function o(o){o.on("mousemove.comparison",function(){a.focus(Math.round(d3.mouse(this)[0]))}).on("mouseout.comparison",function(){a.focus(null)}),o.append("canvas").attr("width",b).attr("height",e),o.append("span").attr("class","title").text(j),o.append("span").attr("class","value primary"),o.append("span").attr("class","value change"),o.each(function(j,o){function B(c,d){x.save(),x.clearRect(0,0,b,e);var g=r.extent(),h=s.extent(),i=t==null?g:t;f.domain(i).range([e,0]),A=g.concat(h).every(isFinite);var j=c/a.step()&1?v:u;x.fillStyle=m[2];for(var k=0,l=b;kp&&x.fillRect(j(k),p,1,o-p)}x.fillStyle=m[3];for(k=0;kp&&x.fillRect(j(k),o-n,1,n)}x.restore()}function C(a){a==null&&(a=b-1);var c=r.valueAt(a),d=s.valueAt(a),e=(c-d)/d;y.datum(c).text(isNaN(c)?null:k),z.datum(e).text(isNaN(e)?null:l).attr("class","value change "+(e>0?"positive":e<0?"negative":""))}function D(a,b){B(a,b),C(),A&&(r.on("change.comparison-"+q,d),s.on("change.comparison-"+q,d))}var p=this,q=++c,r=typeof g=="function"?g.call(p,j,o):g,s=typeof h=="function"?h.call(p,j,o):h,t=typeof i=="function"?i.call(p,j,o):i,w=d3.select(p),x=w.select("canvas"),y=w.select(".value.primary"),z=w.select(".value.change"),A;x.datum({id:q,primary:r,secondary:s}),x=x.node().getContext("2d"),r.on("change.comparison-"+q,D),s.on("change.comparison-"+q,D),a.on("change.comparison-"+q,B),a.on("focus.comparison-"+q,C)})}var a=this,b=a.size(),e=120,f=d3.scale.linear().interpolate(d3.interpolateRound),g=function(a){return a[0]},h=function(a){return a[1]},i=null,j=d,k=s,l=t,m=["#9ecae1","#225b84","#a1d99b","#22723a"],n=1.5;return o.remove=function(b){function c(b){b.primary.on("change.comparison-"+b.id,null),b.secondary.on("change.comparison-"+b.id,null),a.on("change.comparison-"+b.id,null),a.on("focus.comparison-"+b.id,null)}b.on("mousemove.comparison",null).on("mouseout.comparison",null),b.selectAll("canvas").each(c).remove(),b.selectAll(".title,.value").remove()},o.height=function(a){return arguments.length?(e=+a,o):e},o.primary=function(a){return arguments.length?(g=a,o):g},o.secondary=function(a){return arguments.length?(h=a,o):h},o.scale=function(a){return arguments.length?(f=a,o):f},o.extent=function(a){return arguments.length?(i=a,o):i},o.title=function(a){return arguments.length?(j=a,o):j},o.formatPrimary=function(a){return arguments.length?(k=a,o):k},o.formatChange=function(a){return arguments.length?(l=a,o):l},o.colors=function(a){return arguments.length?(m=a,o):m},o.strokeWidth=function(a){return arguments.length?(n=a,o):n},o};var s=d3.format(".2s"),t=d3.format("+.0%");f.axis=function(){function g(e){var h=++c,i,j=e.append("svg").datum({id:h}).attr("width",a.size()).attr("height",Math.max(28,-g.tickSize())).append("g").attr("transform","translate(0,"+(d.orient()==="top"?27:4)+")").call(d);a.on("change.axis-"+h,function(){j.call(d),i||(i=d3.select(j.node().appendChild(j.selectAll("text").node().cloneNode(!0))).style("display","none").text(null))}),a.on("focus.axis-"+h,function(a){if(i)if(a==null)i.style("display","none"),j.selectAll("text").style("fill-opacity",null);else{i.style("display",null).attr("x",a).text(f(b.invert(a)));var c=i.node().getComputedTextLength()+6;j.selectAll("text").style("fill-opacity",function(d){return Math.abs(b(d)-a)li>a:hover,.dropdown-menu>li>a:focus{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-o-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#357ebd));background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f3f3f3));background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:-o-linear-gradient(top,#222 0,#282828 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#222),to(#282828));background-image:linear-gradient(to bottom,#222 0,#282828 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:-o-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#3071a9));background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-o-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#3278b3));background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);background-repeat:repeat-x;border-color:#3278b3}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-o-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#357ebd));background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} -------------------------------------------------------------------------------- /yandextank/plugins/Report/static/css/bootstrap-theme.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"bootstrap-theme.css","sources":["less/theme.less","less/mixins/vendor-prefixes.less","bootstrap-theme.css","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"AAeA;;;;;;EAME,0CAAA;EC+CA,6FAAA;EACQ,qFAAA;EC5DT;AFiBC;;;;;;;;;;;;EC0CA,0DAAA;EACQ,kDAAA;EC7CT;AFqCC;;EAEE,wBAAA;EEnCH;AFwCD;EG/CI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EA+B2C,2BAAA;EAA2B,oBAAA;EE7BvE;AFAC;;EAEE,2BAAA;EACA,8BAAA;EEEH;AFCC;;EAEE,2BAAA;EACA,uBAAA;EECH;AFEC;;EAEE,2BAAA;EACA,wBAAA;EEAH;AFeD;EGhDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EE0BD;AFxBC;;EAEE,2BAAA;EACA,8BAAA;EE0BH;AFvBC;;EAEE,2BAAA;EACA,uBAAA;EEyBH;AFtBC;;EAEE,2BAAA;EACA,wBAAA;EEwBH;AFRD;EGjDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EEkDD;AFhDC;;EAEE,2BAAA;EACA,8BAAA;EEkDH;AF/CC;;EAEE,2BAAA;EACA,uBAAA;EEiDH;AF9CC;;EAEE,2BAAA;EACA,wBAAA;EEgDH;AF/BD;EGlDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EE0ED;AFxEC;;EAEE,2BAAA;EACA,8BAAA;EE0EH;AFvEC;;EAEE,2BAAA;EACA,uBAAA;EEyEH;AFtEC;;EAEE,2BAAA;EACA,wBAAA;EEwEH;AFtDD;EGnDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EEkGD;AFhGC;;EAEE,2BAAA;EACA,8BAAA;EEkGH;AF/FC;;EAEE,2BAAA;EACA,uBAAA;EEiGH;AF9FC;;EAEE,2BAAA;EACA,wBAAA;EEgGH;AF7ED;EGpDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EE0HD;AFxHC;;EAEE,2BAAA;EACA,8BAAA;EE0HH;AFvHC;;EAEE,2BAAA;EACA,uBAAA;EEyHH;AFtHC;;EAEE,2BAAA;EACA,wBAAA;EEwHH;AF7FD;;ECbE,oDAAA;EACQ,4CAAA;EC8GT;AFvFD;;EGvEI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHsEF,2BAAA;EE6FD;AF3FD;;;EG5EI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4EF,2BAAA;EEiGD;AFvFD;EG1FI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ECnBF,qEAAA;EJ4GA,oBAAA;EC9CA,6FAAA;EACQ,qFAAA;EC4IT;AFlGD;EG1FI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EF2CF,0DAAA;EACQ,kDAAA;ECqJT;AF/FD;;EAEE,gDAAA;EEiGD;AF7FD;EG5GI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ECnBF,qEAAA;EFgOD;AFrGD;EG5GI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EF2CF,yDAAA;EACQ,iDAAA;EC0KT;AF9GD;;EAWI,2CAAA;EEuGH;AFlGD;;;EAGE,kBAAA;EEoGD;AF1FD;EACE,+CAAA;EC3FA,4FAAA;EACQ,oFAAA;ECwLT;AFlFD;EGtJI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8IF,uBAAA;EE8FD;AFzFD;EGvJI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8IF,uBAAA;EEsGD;AFhGD;EGxJI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8IF,uBAAA;EE8GD;AFvGD;EGzJI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8IF,uBAAA;EEsHD;AFtGD;EGlKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED2QH;AFnGD;EG5KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDkRH;AFzGD;EG7KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDyRH;AF/GD;EG9KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDgSH;AFrHD;EG/KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDuSH;AF3HD;EGhLI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED8SH;AF9HD;EGnJI,+MAAA;EACA,0MAAA;EACA,uMAAA;EDoRH;AF1HD;EACE,oBAAA;EC/IA,oDAAA;EACQ,4CAAA;EC4QT;AF3HD;;;EAGE,+BAAA;EGpME,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHkMF,uBAAA;EEiID;AFvHD;ECjKE,mDAAA;EACQ,2CAAA;EC2RT;AFjHD;EG1NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED8UH;AFvHD;EG3NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDqVH;AF7HD;EG5NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED4VH;AFnID;EG7NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDmWH;AFzID;EG9NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED0WH;AF/ID;EG/NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDiXH;AF9ID;EGvOI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHqOF,uBAAA;EC1LA,2FAAA;EACQ,mFAAA;EC+UT","sourcesContent":["\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &:disabled,\n &[disabled] {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-bg, 5%); @end-color: darken(@navbar-default-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-bg; @end-color: lighten(@navbar-inverse-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n}\n\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n &::-moz-placeholder { color: @color; // Firefox\n opacity: 1; } // See https://github.com/twbs/bootstrap/pull/11526\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n",null,"// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} -------------------------------------------------------------------------------- /yandextank/plugins/Report/templates/offline.jade: -------------------------------------------------------------------------------- 1 | !!! 5 2 | {% autoescape None %} 3 | html(lang="en") 4 | head 5 | meta(charset="utf-8") 6 | meta(http-equiv="X-UA-Compatible", content="IE=edge") 7 | meta(name="viewport", content="width=device-width, initial-scale=1") 8 | title Yandex.Tank online report 9 | 10 | link(rel="stylesheet", href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css") 11 | link(rel="stylesheet", href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css") 12 | 13 | link(rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/rickshaw/1.4.6/rickshaw.min.css") 14 | script(src="https://code.jquery.com/jquery.min.js") 15 | script(src="https://code.angularjs.org/1.2.24/angular.min.js") 16 | script(src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js") 17 | script(src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js") 18 | 19 | script(src="https://cdnjs.cloudflare.com/ajax/libs/rickshaw/1.4.6/rickshaw.min.js") 20 | 21 | style(type='text/css'). 22 | .rickshaw_legend li { 23 | display: inline-block; 24 | padding: 0 0 0 2px; 25 | min-width: 80px; 26 | white-space: nowrap; 27 | } 28 | 29 | script(type="text/javascript"). 30 | 31 | document.cached_data = {{cached_data}}; 32 | (function() { 33 | // angular-rickshaw.js 34 | "use strict"; 35 | angular.module("angular-rickshaw", []).directive("rickshaw", function($compile) { 36 | return { 37 | restrict: "EA", 38 | scope: { 39 | options: "=rickshawOptions", 40 | series: "=rickshawSeries", 41 | features: "=rickshawFeatures" 42 | }, 43 | link: function(scope, element, attrs) { 44 | var getSettings, update; 45 | getSettings = function(el) { 46 | var settings; 47 | settings = angular.copy(scope.options); 48 | settings.element = el; 49 | settings.series = scope.series; 50 | return settings; 51 | }; 52 | update = function() { 53 | var graphEl, highlighter, hoverConfig, hoverDetail, i, legend, legendEl, mainEl, palette, settings, shelving, time, xAxis, xAxisConfig, yAxis, yAxisConfig, yAxisSecondary, yAxisSecondaryConfig; 54 | mainEl = angular.element(element); 55 | mainEl.append(graphEl); 56 | mainEl.empty(); 57 | graphEl = $compile("
")(scope); 58 | mainEl.append(graphEl); 59 | settings = getSettings(graphEl[0]); 60 | Rickshaw.Series.zeroFill(settings.series); 61 | scope.graph = new Rickshaw.Graph(settings); 62 | if (scope.features && scope.features.hover) { 63 | hoverConfig = { 64 | graph: scope.graph 65 | }; 66 | hoverConfig.xFormatter = scope.features.hover.xFormatter; 67 | hoverConfig.yFormatter = scope.features.hover.yFormatter; 68 | hoverConfig.formatter = scope.features.hover.formatter; 69 | hoverDetail = new Rickshaw.Graph.HoverDetail(hoverConfig); 70 | } 71 | if (scope.features && scope.features.palette) { 72 | palette = new Rickshaw.Color.Palette({ 73 | scheme: scope.features.palette 74 | }); 75 | i = 0; 76 | while (i < settings.series.length) { 77 | settings.series[i].color = palette.color(); 78 | i++; 79 | } 80 | } 81 | scope.graph.render(); 82 | if (scope.features && scope.features.xAxis) { 83 | xAxisConfig = { 84 | graph: scope.graph 85 | }; 86 | if (scope.features.xAxis.timeUnit) { 87 | time = new Rickshaw.Fixtures.Time(); 88 | xAxisConfig.timeUnit = time.unit(scope.features.xAxis.timeUnit); 89 | } 90 | xAxis = new Rickshaw.Graph.Axis.Time(xAxisConfig); 91 | xAxis.render(); 92 | } 93 | if (scope.features && scope.features.yAxis) { 94 | yAxisConfig = { 95 | graph: scope.graph, 96 | orientation: 'left' 97 | }; 98 | if (scope.features.yAxis.tickFormat) { 99 | yAxisConfig.tickFormat = Rickshaw.Fixtures.Number[scope.features.yAxis.tickFormat]; 100 | } 101 | yAxis = new Rickshaw.Graph.Axis.Y(yAxisConfig); 102 | yAxis.render(); 103 | } 104 | if (scope.features && scope.features.yAxisSecondary) { 105 | yAxisSecondaryConfig = { 106 | graph: scope.graph, 107 | orientation: 'right' 108 | }; 109 | if (scope.features.yAxisSecondary.tickFormat) { 110 | yAxisSecondaryConfig.tickFormat = Rickshaw.Fixtures.Number[scope.features.yAxisSecondary.tickFormat]; 111 | } 112 | yAxisSecondary = new Rickshaw.Graph.Axis.Y(yAxisSecondaryConfig); 113 | yAxisSecondary.render(); 114 | } 115 | if (scope.features && scope.features.legend) { 116 | legendEl = $compile("
")(scope); 117 | mainEl.append(legendEl); 118 | legend = new Rickshaw.Graph.Legend({ 119 | graph: scope.graph, 120 | element: legendEl[0] 121 | }); 122 | if (scope.features.legend.toggle) { 123 | shelving = new Rickshaw.Graph.Behavior.Series.Toggle({ 124 | graph: scope.graph, 125 | legend: legend 126 | }); 127 | } 128 | if (scope.features.legend.highlight) { 129 | highlighter = new Rickshaw.Graph.Behavior.Series.Highlight({ 130 | graph: scope.graph, 131 | legend: legend 132 | }); 133 | } 134 | } 135 | }; 136 | scope.graph = void 0; 137 | scope.$watch("options", function(newValue, oldValue) { 138 | if (!angular.equals(newValue, oldValue)) { 139 | update(); 140 | } 141 | }); 142 | scope.$watch("series", function(newValue, oldValue) { 143 | if (!angular.equals(newValue, oldValue)) { 144 | update(); 145 | } 146 | }); 147 | scope.$watch("features", function(newValue, oldValue) { 148 | if (!angular.equals(newValue, oldValue)) { 149 | update(); 150 | } 151 | }); 152 | scope.$on("DataUpdated", function() { 153 | return scope.graph.update(); 154 | }); 155 | return update(); 156 | }, 157 | controller: function($scope, $element, $attrs) {} 158 | }; 159 | }); 160 | 161 | // ws.js 162 | var app, collect_monitoring, collect_quantiles, collect_stats, collect_subtree, 163 | indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; 164 | 165 | app = angular.module("ng-tank-report", ["angular-rickshaw"]); 166 | 167 | collect_subtree = function(storage, subtree, ts) { 168 | var key, node, results; 169 | results = []; 170 | for (key in subtree) { 171 | node = subtree[key]; 172 | if (typeof node === 'number' || typeof node === 'array') { 173 | results.push(storage[key].push({ 174 | x: ts, 175 | y: node 176 | })); 177 | } else { 178 | results.push(collect_subtree(storage[key], node, ts)); 179 | } 180 | } 181 | return results; 182 | }; 183 | 184 | collect_stats = function(stats) { 185 | var j, len, metric, ref, result, statItem, value; 186 | result = {}; 187 | for (j = 0, len = stats.length; j < len; j++) { 188 | statItem = stats[j]; 189 | ref = statItem.metrics; 190 | for (metric in ref) { 191 | value = ref[metric]; 192 | if (!result[metric]) { 193 | result[metric] = []; 194 | } 195 | result[metric].push({ 196 | x: +statItem.ts, 197 | y: +value 198 | }); 199 | } 200 | } 201 | return result; 202 | }; 203 | 204 | collect_monitoring = function(monitoring) { 205 | var host, hostData, j, len, metric, monItem, ref, ref1, result, subgroup, value; 206 | result = {}; 207 | for (j = 0, len = monitoring.length; j < len; j++) { 208 | monItem = monitoring[j]; 209 | ref = monItem.data; 210 | for (host in ref) { 211 | hostData = ref[host]; 212 | if (!result[host]) { 213 | result[host] = { 214 | comment: hostData.comment, 215 | metrics: {} 216 | }; 217 | } 218 | ref1 = hostData.metrics; 219 | for (metric in ref1) { 220 | value = ref1[metric]; 221 | subgroup = metric.split("_", 1); 222 | if (!result[host].metrics[subgroup]) { 223 | result[host].metrics[subgroup] = {}; 224 | } 225 | if (!result[host].metrics[subgroup][metric]) { 226 | result[host].metrics[subgroup][metric] = []; 227 | } 228 | result[host].metrics[subgroup][metric].push({ 229 | x: +monItem.timestamp, 230 | y: +value 231 | }); 232 | } 233 | } 234 | } 235 | return result; 236 | }; 237 | 238 | collect_quantiles = function(data) { 239 | var dataItem, i, j, k, l, len, ref, ref1, ref2, result, tag, tagData; 240 | result = { 241 | overall: { 242 | interval_real: { 243 | q: {} 244 | } 245 | }, 246 | tagged: {} 247 | }; 248 | for (j = 0, len = data.length; j < len; j++) { 249 | dataItem = data[j]; 250 | for (i = k = 0, ref = dataItem.overall.interval_real.q.q.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) { 251 | if (!result.overall.interval_real.q[dataItem.overall.interval_real.q.q[i]]) { 252 | result.overall.interval_real.q[dataItem.overall.interval_real.q.q[i]] = []; 253 | } 254 | result.overall.interval_real.q[dataItem.overall.interval_real.q.q[i]].push({ 255 | x: dataItem.ts, 256 | y: dataItem.overall.interval_real.q.value[i] 257 | }); 258 | } 259 | ref1 = dataItem.tagged; 260 | for (tag in ref1) { 261 | tagData = ref1[tag]; 262 | if (!result.tagged[tag]) { 263 | result.tagged[tag] = { 264 | interval_real: { 265 | q: {} 266 | } 267 | }; 268 | } 269 | for (i = l = 0, ref2 = tagData.interval_real.q.q.length; 0 <= ref2 ? l < ref2 : l > ref2; i = 0 <= ref2 ? ++l : --l) { 270 | if (!result.tagged[tag].interval_real.q[tagData.interval_real.q.q[i]]) { 271 | result.tagged[tag].interval_real.q[tagData.interval_real.q.q[i]] = []; 272 | } 273 | result.tagged[tag].interval_real.q[tagData.interval_real.q.q[i]].push({ 274 | x: dataItem.ts, 275 | y: tagData.interval_real.q.value[i] 276 | }); 277 | } 278 | } 279 | } 280 | return result; 281 | }; 282 | 283 | app.controller("TankReport", function($scope, $element) { 284 | $scope.status = "Disconnected"; 285 | $scope.data = document.cached_data.data; 286 | $scope.uuid = document.cached_data.uuid; 287 | $scope.updateData = function(tankData) { 288 | var data, storage, storages, ts; 289 | for (ts in tankData) { 290 | storages = tankData[ts]; 291 | for (storage in storages) { 292 | data = storages[storage]; 293 | collect_subtree($scope.data[storage], data, +ts); 294 | } 295 | } 296 | return $scope.$broadcast('DataUpdated'); 297 | }; 298 | $scope.buildSeries = function() { 299 | var areaGraphs, cache, data, hostData, hostname, metric, metrics, monitoringData, name, overallData, quantilesData, series, statsData, subgroup, taggedData; 300 | cache = document.cached_data.data; 301 | if (cache.stats && cache.data && cache.monitoring) { 302 | overallData = {}; 303 | taggedData = {}; 304 | quantilesData = collect_quantiles(cache.data); 305 | statsData = collect_stats(cache.stats); 306 | monitoringData = collect_monitoring(cache.monitoring); 307 | } else { 308 | overallData = {}; 309 | taggedData = {}; 310 | quantilesData = {}; 311 | statsData = {}; 312 | monitoringData = {}; 313 | setTimeout((function() { 314 | return location.reload(true); 315 | }), 3000); 316 | } 317 | areaGraphs = ['CPU', 'Memory']; 318 | $scope.monitoringGraphData = (function() { 319 | var results; 320 | results = []; 321 | for (hostname in monitoringData) { 322 | hostData = monitoringData[hostname]; 323 | results.push({ 324 | hostname: hostname, 325 | charts: (function() { 326 | var ref, results1; 327 | ref = hostData.metrics; 328 | results1 = []; 329 | for (subgroup in ref) { 330 | metrics = ref[subgroup]; 331 | results1.push({ 332 | name: subgroup, 333 | features: { 334 | palette: 'spectrum14', 335 | hover: {}, 336 | xAxis: {}, 337 | yAxis: {}, 338 | ySecondaryAxis: { 339 | scale: null 340 | }, 341 | legend: { 342 | toggle: true, 343 | highlight: true 344 | } 345 | }, 346 | options: { 347 | renderer: 'multi' 348 | }, 349 | series: ((function() { 350 | var results2; 351 | results2 = []; 352 | for (metric in metrics) { 353 | series = metrics[metric]; 354 | results2.push({ 355 | name: metric, 356 | data: series, 357 | renderer: indexOf.call(areaGraphs, subgroup) >= 0 ? "area" : "line" 358 | }); 359 | } 360 | return results2; 361 | })()).concat([ 362 | { 363 | name: "Requests per second", 364 | color: "red", 365 | renderer: "line", 366 | data: statsData.reqps 367 | } 368 | ]) 369 | }); 370 | } 371 | return results1; 372 | })() 373 | }); 374 | } 375 | return results; 376 | })(); 377 | $scope.quantilesGraphData = { 378 | name: "Response time quantiles", 379 | features: { 380 | palette: 'classic9', 381 | hover: {}, 382 | xAxis: {}, 383 | yAxis: {}, 384 | legend: { 385 | toggle: true, 386 | highlight: true 387 | } 388 | }, 389 | options: { 390 | renderer: 'area', 391 | stack: false, 392 | height: $element[0].offsetHeight - 45 - 62 393 | }, 394 | series: ((function() { 395 | var ref, results; 396 | ref = quantilesData.overall.interval_real.q; 397 | results = []; 398 | for (name in ref) { 399 | data = ref[name]; 400 | results.push({ 401 | name: name, 402 | data: data 403 | }); 404 | } 405 | return results; 406 | })()).sort(function(a, b) { 407 | if (parseFloat(a.name) <= parseFloat(b.name)) { 408 | return 1; 409 | } else { 410 | return -1; 411 | } 412 | }) 413 | }; 414 | $scope.rps = { 415 | name: "Requests per second", 416 | features: { 417 | palette: 'spectrum14', 418 | hover: {}, 419 | xAxis: {}, 420 | yAxis: {}, 421 | legend: { 422 | toggle: true, 423 | highlight: true 424 | } 425 | }, 426 | options: { 427 | renderer: 'line' 428 | }, 429 | series: [ 430 | { 431 | name: "req/s", 432 | color: "red", 433 | data: statsData.reqps 434 | } 435 | ] 436 | }; 437 | $scope.protoCodes = { 438 | name: "Protocol return codes", 439 | features: { 440 | palette: 'spectrum14', 441 | hover: {}, 442 | xAxis: {}, 443 | yAxis: {}, 444 | legend: { 445 | toggle: true, 446 | highlight: true 447 | } 448 | }, 449 | options: { 450 | renderer: 'area', 451 | stack: true, 452 | height: $element[0].offsetHeight - 45 - 62 453 | }, 454 | series: ((function() { 455 | var ref, results; 456 | ref = overallData.http_codes; 457 | results = []; 458 | for (name in ref) { 459 | data = ref[name]; 460 | results.push({ 461 | name: name, 462 | data: data 463 | }); 464 | } 465 | return results; 466 | })()).sort(function(a, b) { 467 | if (parseFloat(a.name) <= parseFloat(b.name)) { 468 | return 1; 469 | } else { 470 | return -1; 471 | } 472 | }) 473 | }; 474 | return $scope.netCodes = { 475 | name: "Network return codes", 476 | features: { 477 | palette: 'spectrum14', 478 | hover: {}, 479 | xAxis: {}, 480 | yAxis: {}, 481 | legend: { 482 | toggle: true, 483 | highlight: true 484 | } 485 | }, 486 | options: { 487 | renderer: 'area', 488 | stack: true, 489 | height: $element[0].offsetHeight - 45 - 62 490 | }, 491 | series: ((function() { 492 | var ref, results; 493 | ref = overallData.net_codes; 494 | results = []; 495 | for (name in ref) { 496 | data = ref[name]; 497 | results.push({ 498 | name: name, 499 | data: data 500 | }); 501 | } 502 | return results; 503 | })()).sort(function(a, b) { 504 | if (parseFloat(a.name) <= parseFloat(b.name)) { 505 | return 1; 506 | } else { 507 | return -1; 508 | } 509 | }) 510 | }; 511 | }; 512 | return $scope.buildSeries(); 513 | }); 514 | 515 | }).call(this); 516 | body 517 | .app.container(ng-app="ng-tank-report", ng-controller="TankReport") 518 | h1.page-heading Yandex.Tank online report 519 | #status {{!status}} 520 | #quantiles 521 | h2.part-heading Response time metrics 522 | .row 523 | .col-md-10 524 | .panel.panel-default 525 | .panel-heading 526 | h3.panel-title {{!quantilesGraphData.name}} 527 | .panel-body 528 | rickshaw( 529 | rickshaw-series="quantilesGraphData.series", 530 | rickshaw-options="quantilesGraphData.options", 531 | rickshaw-features="quantilesGraphData.features" 532 | ) 533 | #monitoring 534 | h2.part-heading Monitoring metrics 535 | .host(ng-repeat="host in monitoringGraphData") 536 | h3 {{!host.hostname}} 537 | .row 538 | .col-md-10(ng-repeat="chart in host.charts") 539 | .panel.panel-default 540 | .panel-heading 541 | h3.panel-title {{!chart.name}} 542 | .panel-body 543 | rickshaw( 544 | rickshaw-series="chart.series", 545 | rickshaw-options="chart.options", 546 | rickshaw-features="chart.features" 547 | ) 548 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/static/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.2.0 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | .btn-default, 8 | .btn-primary, 9 | .btn-success, 10 | .btn-info, 11 | .btn-warning, 12 | .btn-danger { 13 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 14 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 16 | } 17 | .btn-default:active, 18 | .btn-primary:active, 19 | .btn-success:active, 20 | .btn-info:active, 21 | .btn-warning:active, 22 | .btn-danger:active, 23 | .btn-default.active, 24 | .btn-primary.active, 25 | .btn-success.active, 26 | .btn-info.active, 27 | .btn-warning.active, 28 | .btn-danger.active { 29 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 31 | } 32 | .btn:active, 33 | .btn.active { 34 | background-image: none; 35 | } 36 | .btn-default { 37 | text-shadow: 0 1px 0 #fff; 38 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 39 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); 40 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); 41 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 42 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 43 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 44 | background-repeat: repeat-x; 45 | border-color: #dbdbdb; 46 | border-color: #ccc; 47 | } 48 | .btn-default:hover, 49 | .btn-default:focus { 50 | background-color: #e0e0e0; 51 | background-position: 0 -15px; 52 | } 53 | .btn-default:active, 54 | .btn-default.active { 55 | background-color: #e0e0e0; 56 | border-color: #dbdbdb; 57 | } 58 | .btn-default:disabled, 59 | .btn-default[disabled] { 60 | background-color: #e0e0e0; 61 | background-image: none; 62 | } 63 | .btn-primary { 64 | background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%); 65 | background-image: -o-linear-gradient(top, #428bca 0%, #2d6ca2 100%); 66 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#2d6ca2)); 67 | background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%); 68 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0); 69 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 70 | background-repeat: repeat-x; 71 | border-color: #2b669a; 72 | } 73 | .btn-primary:hover, 74 | .btn-primary:focus { 75 | background-color: #2d6ca2; 76 | background-position: 0 -15px; 77 | } 78 | .btn-primary:active, 79 | .btn-primary.active { 80 | background-color: #2d6ca2; 81 | border-color: #2b669a; 82 | } 83 | .btn-primary:disabled, 84 | .btn-primary[disabled] { 85 | background-color: #2d6ca2; 86 | background-image: none; 87 | } 88 | .btn-success { 89 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 90 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); 91 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); 92 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 93 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 94 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 95 | background-repeat: repeat-x; 96 | border-color: #3e8f3e; 97 | } 98 | .btn-success:hover, 99 | .btn-success:focus { 100 | background-color: #419641; 101 | background-position: 0 -15px; 102 | } 103 | .btn-success:active, 104 | .btn-success.active { 105 | background-color: #419641; 106 | border-color: #3e8f3e; 107 | } 108 | .btn-success:disabled, 109 | .btn-success[disabled] { 110 | background-color: #419641; 111 | background-image: none; 112 | } 113 | .btn-info { 114 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 115 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 116 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); 117 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 118 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 119 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 120 | background-repeat: repeat-x; 121 | border-color: #28a4c9; 122 | } 123 | .btn-info:hover, 124 | .btn-info:focus { 125 | background-color: #2aabd2; 126 | background-position: 0 -15px; 127 | } 128 | .btn-info:active, 129 | .btn-info.active { 130 | background-color: #2aabd2; 131 | border-color: #28a4c9; 132 | } 133 | .btn-info:disabled, 134 | .btn-info[disabled] { 135 | background-color: #2aabd2; 136 | background-image: none; 137 | } 138 | .btn-warning { 139 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 140 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 141 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); 142 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 143 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 144 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 145 | background-repeat: repeat-x; 146 | border-color: #e38d13; 147 | } 148 | .btn-warning:hover, 149 | .btn-warning:focus { 150 | background-color: #eb9316; 151 | background-position: 0 -15px; 152 | } 153 | .btn-warning:active, 154 | .btn-warning.active { 155 | background-color: #eb9316; 156 | border-color: #e38d13; 157 | } 158 | .btn-warning:disabled, 159 | .btn-warning[disabled] { 160 | background-color: #eb9316; 161 | background-image: none; 162 | } 163 | .btn-danger { 164 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 165 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 166 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); 167 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 168 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 169 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 170 | background-repeat: repeat-x; 171 | border-color: #b92c28; 172 | } 173 | .btn-danger:hover, 174 | .btn-danger:focus { 175 | background-color: #c12e2a; 176 | background-position: 0 -15px; 177 | } 178 | .btn-danger:active, 179 | .btn-danger.active { 180 | background-color: #c12e2a; 181 | border-color: #b92c28; 182 | } 183 | .btn-danger:disabled, 184 | .btn-danger[disabled] { 185 | background-color: #c12e2a; 186 | background-image: none; 187 | } 188 | .thumbnail, 189 | .img-thumbnail { 190 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 191 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 192 | } 193 | .dropdown-menu > li > a:hover, 194 | .dropdown-menu > li > a:focus { 195 | background-color: #e8e8e8; 196 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 197 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 198 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 199 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 200 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 201 | background-repeat: repeat-x; 202 | } 203 | .dropdown-menu > .active > a, 204 | .dropdown-menu > .active > a:hover, 205 | .dropdown-menu > .active > a:focus { 206 | background-color: #357ebd; 207 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); 208 | background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); 209 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd)); 210 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 211 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 212 | background-repeat: repeat-x; 213 | } 214 | .navbar-default { 215 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 216 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); 217 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); 218 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 219 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 220 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 221 | background-repeat: repeat-x; 222 | border-radius: 4px; 223 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 224 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 225 | } 226 | .navbar-default .navbar-nav > .active > a { 227 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); 228 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); 229 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f3f3f3)); 230 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%); 231 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0); 232 | background-repeat: repeat-x; 233 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 234 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 235 | } 236 | .navbar-brand, 237 | .navbar-nav > li > a { 238 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 239 | } 240 | .navbar-inverse { 241 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 242 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); 243 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); 244 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 245 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 246 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 247 | background-repeat: repeat-x; 248 | } 249 | .navbar-inverse .navbar-nav > .active > a { 250 | background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%); 251 | background-image: -o-linear-gradient(top, #222 0%, #282828 100%); 252 | background-image: -webkit-gradient(linear, left top, left bottom, from(#222), to(#282828)); 253 | background-image: linear-gradient(to bottom, #222 0%, #282828 100%); 254 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0); 255 | background-repeat: repeat-x; 256 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 257 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 258 | } 259 | .navbar-inverse .navbar-brand, 260 | .navbar-inverse .navbar-nav > li > a { 261 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 262 | } 263 | .navbar-static-top, 264 | .navbar-fixed-top, 265 | .navbar-fixed-bottom { 266 | border-radius: 0; 267 | } 268 | .alert { 269 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 270 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 271 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 272 | } 273 | .alert-success { 274 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 275 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 276 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); 277 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 278 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 279 | background-repeat: repeat-x; 280 | border-color: #b2dba1; 281 | } 282 | .alert-info { 283 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 284 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 285 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); 286 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 287 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 288 | background-repeat: repeat-x; 289 | border-color: #9acfea; 290 | } 291 | .alert-warning { 292 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 293 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 294 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); 295 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 296 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 297 | background-repeat: repeat-x; 298 | border-color: #f5e79e; 299 | } 300 | .alert-danger { 301 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 302 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 303 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); 304 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 305 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 306 | background-repeat: repeat-x; 307 | border-color: #dca7a7; 308 | } 309 | .progress { 310 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 311 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 312 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); 313 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 314 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 315 | background-repeat: repeat-x; 316 | } 317 | .progress-bar { 318 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%); 319 | background-image: -o-linear-gradient(top, #428bca 0%, #3071a9 100%); 320 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3071a9)); 321 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); 322 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); 323 | background-repeat: repeat-x; 324 | } 325 | .progress-bar-success { 326 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 327 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); 328 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); 329 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 330 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 331 | background-repeat: repeat-x; 332 | } 333 | .progress-bar-info { 334 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 335 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 336 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); 337 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 338 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 339 | background-repeat: repeat-x; 340 | } 341 | .progress-bar-warning { 342 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 343 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 344 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); 345 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 346 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 347 | background-repeat: repeat-x; 348 | } 349 | .progress-bar-danger { 350 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 351 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); 352 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); 353 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 354 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 355 | background-repeat: repeat-x; 356 | } 357 | .progress-bar-striped { 358 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 359 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 360 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 361 | } 362 | .list-group { 363 | border-radius: 4px; 364 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 365 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 366 | } 367 | .list-group-item.active, 368 | .list-group-item.active:hover, 369 | .list-group-item.active:focus { 370 | text-shadow: 0 -1px 0 #3071a9; 371 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%); 372 | background-image: -o-linear-gradient(top, #428bca 0%, #3278b3 100%); 373 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3278b3)); 374 | background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); 375 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); 376 | background-repeat: repeat-x; 377 | border-color: #3278b3; 378 | } 379 | .panel { 380 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 381 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 382 | } 383 | .panel-default > .panel-heading { 384 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 385 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 386 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 387 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 388 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 389 | background-repeat: repeat-x; 390 | } 391 | .panel-primary > .panel-heading { 392 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); 393 | background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); 394 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd)); 395 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 396 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 397 | background-repeat: repeat-x; 398 | } 399 | .panel-success > .panel-heading { 400 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 401 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); 403 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 405 | background-repeat: repeat-x; 406 | } 407 | .panel-info > .panel-heading { 408 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 409 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 410 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); 411 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 412 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 413 | background-repeat: repeat-x; 414 | } 415 | .panel-warning > .panel-heading { 416 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 417 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 418 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); 419 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 420 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 421 | background-repeat: repeat-x; 422 | } 423 | .panel-danger > .panel-heading { 424 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 425 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 426 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); 427 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 428 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 429 | background-repeat: repeat-x; 430 | } 431 | .well { 432 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 433 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 434 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); 435 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 436 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 437 | background-repeat: repeat-x; 438 | border-color: #dcdcdc; 439 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 440 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 441 | } 442 | /*# sourceMappingURL=bootstrap-theme.css.map */ 443 | -------------------------------------------------------------------------------- /yandextank/plugins/Report/static/js/vendor/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.2.0 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.2.0",d.prototype.close=function(b){function c(){f.detach().trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",c).emulateTransitionEnd(150):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.2.0",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),d[e](null==f[b]?this.options[b]:f[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b).on("keydown.bs.carousel",a.proxy(this.keydown,this)),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.2.0",c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},c.prototype.keydown=function(a){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.to=function(b){var c=this,d=this.getItemIndex(this.$active=this.$element.find(".item.active"));return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=e[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:g});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,f&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(e)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:g});return a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one("bsTransitionEnd",function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger(m)),f&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(b=!b),e||d.data("bs.collapse",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};c.VERSION="3.2.0",c.DEFAULTS={toggle:!0},c.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},c.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var c=a.Event("show.bs.collapse");if(this.$element.trigger(c),!c.isDefaultPrevented()){var d=this.$parent&&this.$parent.find("> .panel > .in");if(d&&d.length){var e=d.data("bs.collapse");if(e&&e.transitioning)return;b.call(d,"hide"),e||d.data("bs.collapse",null)}var f=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[f](0),this.transitioning=1;var g=function(){this.$element.removeClass("collapsing").addClass("collapse in")[f](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return g.call(this);var h=a.camelCase(["scroll",f].join("-"));this.$element.one("bsTransitionEnd",a.proxy(g,this)).emulateTransitionEnd(350)[f](this.$element[0][h])}}},c.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},c.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var d=a.fn.collapse;a.fn.collapse=b,a.fn.collapse.Constructor=c,a.fn.collapse.noConflict=function(){return a.fn.collapse=d,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(c){var d,e=a(this),f=e.attr("data-target")||c.preventDefault()||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),g=a(f),h=g.data("bs.collapse"),i=h?"toggle":e.data(),j=e.attr("data-parent"),k=j&&a(j);h&&h.transitioning||(k&&k.find('[data-toggle="collapse"][data-parent="'+j+'"]').not(e).addClass("collapsed"),e[g.hasClass("in")?"addClass":"removeClass"]("collapsed")),b.call(g,i)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.2.0",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('