├── src ├── __init__.py ├── api │ ├── __init__.py │ ├── util │ │ ├── __init__.py │ │ ├── timeutils.py │ │ ├── settings.py │ │ └── RDP.py │ └── controller │ │ ├── __init__.py │ │ ├── BaseStaticFileHandler.py │ │ ├── ServerListController.py │ │ ├── TopKeysController.py │ │ ├── TopCommandsController.py │ │ ├── MemoryController.py │ │ ├── CommandsController.py │ │ ├── InfoController.py │ │ └── BaseController.py ├── dataprovider │ ├── __init__.py │ ├── dataprovider.py │ └── sqliteprovider.py ├── db │ ├── redislive.sqlite │ └── schema.sql ├── www │ ├── images │ │ └── logo.png │ └── js │ │ ├── libs │ │ └── bootstrap │ │ │ ├── img │ │ │ ├── glyphicons-halflings.png │ │ │ └── glyphicons-halflings-white.png │ │ │ ├── less │ │ │ ├── grid.less │ │ │ ├── utilities.less │ │ │ ├── component-animations.less │ │ │ ├── close.less │ │ │ ├── layouts.less │ │ │ ├── hero-unit.less │ │ │ ├── breadcrumbs.less │ │ │ ├── accordion.less │ │ │ ├── scaffolding.less │ │ │ ├── wells.less │ │ │ ├── pager.less │ │ │ ├── thumbnails.less │ │ │ ├── tooltip.less │ │ │ ├── widget.less │ │ │ ├── badges.less │ │ │ ├── labels.less │ │ │ ├── pagination.less │ │ │ ├── alerts.less │ │ │ ├── popovers.less │ │ │ ├── code.less │ │ │ ├── bootstrap.less │ │ │ ├── modals.less │ │ │ ├── carousel.less │ │ │ ├── progress-bars.less │ │ │ ├── reset.less │ │ │ ├── dropdowns.less │ │ │ ├── tables.less │ │ │ ├── type.less │ │ │ ├── buttons.less │ │ │ ├── button-groups.less │ │ │ └── variables.less │ │ │ └── js │ │ │ ├── tests │ │ │ ├── unit │ │ │ │ ├── bootstrap-transition.js │ │ │ │ ├── bootstrap-collapse.js │ │ │ │ ├── bootstrap-scrollspy.js │ │ │ │ ├── bootstrap-alert.js │ │ │ │ ├── bootstrap-tab.js │ │ │ │ ├── bootstrap-dropdown.js │ │ │ │ ├── bootstrap-tooltip.js │ │ │ │ ├── bootstrap-modal.js │ │ │ │ ├── bootstrap-button.js │ │ │ │ ├── bootstrap-popover.js │ │ │ │ └── bootstrap-typeahead.js │ │ │ ├── index.html │ │ │ └── vendor │ │ │ │ └── qunit.css │ │ │ ├── bootstrap-transition.js │ │ │ ├── bootstrap-alert.js │ │ │ ├── bootstrap-dropdown.js │ │ │ ├── bootstrap-popover.js │ │ │ ├── bootstrap-button.js │ │ │ ├── README.md │ │ │ ├── bootstrap-tab.js │ │ │ ├── bootstrap-scrollspy.js │ │ │ ├── bootstrap-collapse.js │ │ │ ├── bootstrap-carousel.js │ │ │ ├── bootstrap-modal.js │ │ │ ├── bootstrap-typeahead.js │ │ │ └── bootstrap-tooltip.js │ │ ├── models │ │ ├── serverlist-model.js │ │ ├── memory-widget-model.js │ │ ├── commands-widget-model.js │ │ ├── top-keys-widget-model.js │ │ ├── top-commands-widget-model.js │ │ └── info-widget-model.js │ │ ├── views │ │ ├── serverlist-view.js │ │ ├── info-widget-view.js │ │ ├── top-commands-widget-view.js │ │ ├── top-keys-widget-view.js │ │ ├── memory-widget-view.js │ │ ├── commands-widget-view.js │ │ └── base-widget-view.js │ │ ├── google │ │ ├── visualization.js │ │ └── jsapi.css │ │ └── app.js ├── redis-live.conf.example ├── util │ ├── redis-fill-data2.py │ └── redis-fill-data.py └── redis-live.py ├── requirements.txt ├── design └── redis-live.png ├── .gitignore ├── Dockerfile ├── MIT-LICENSE.txt └── README.md /src/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/api/util/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/dataprovider/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/api/controller/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | argparse==1.2.1 2 | python-dateutil==1.5 3 | redis 4 | tornado==2.1.1 5 | -------------------------------------------------------------------------------- /design/redis-live.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snakeliwei/RedisLive/HEAD/design/redis-live.png -------------------------------------------------------------------------------- /src/db/redislive.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snakeliwei/RedisLive/HEAD/src/db/redislive.sqlite -------------------------------------------------------------------------------- /src/www/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snakeliwei/RedisLive/HEAD/src/www/images/logo.png -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snakeliwei/RedisLive/HEAD/src/www/js/libs/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snakeliwei/RedisLive/HEAD/src/www/js/libs/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /src/www/js/models/serverlist-model.js: -------------------------------------------------------------------------------- 1 | var ServerListModel = Backbone.Model.extend({ 2 | 3 | url : "api/servers", 4 | 5 | initialize : function(){ 6 | 7 | } 8 | 9 | }) 10 | -------------------------------------------------------------------------------- /src/www/js/models/memory-widget-model.js: -------------------------------------------------------------------------------- 1 | var MemoryWidgetModel = Backbone.Model.extend({ 2 | 3 | url : "api/memory", 4 | 5 | initialize : function(){ 6 | 7 | } 8 | 9 | }) 10 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/grid.less: -------------------------------------------------------------------------------- 1 | // Fixed (940px) 2 | #grid > .core(@gridColumnWidth, @gridGutterWidth); 3 | 4 | // Fluid (940px) 5 | #grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth); -------------------------------------------------------------------------------- /src/www/js/models/commands-widget-model.js: -------------------------------------------------------------------------------- 1 | var CommandsWidgetModel = Backbone.Model.extend({ 2 | 3 | url : "api/commands", 4 | 5 | initialize : function(){ 6 | 7 | } 8 | 9 | }) 10 | -------------------------------------------------------------------------------- /src/www/js/models/top-keys-widget-model.js: -------------------------------------------------------------------------------- 1 | var TopKeysWidgetModel = Backbone.Model.extend({ 2 | 3 | url : "api/topkeys", 4 | 5 | initialize : function(){ 6 | 7 | } 8 | 9 | }) 10 | -------------------------------------------------------------------------------- /src/www/js/models/top-commands-widget-model.js: -------------------------------------------------------------------------------- 1 | var TopCommandsWidgetModel = Backbone.Model.extend({ 2 | 3 | url : "api/topcommands", 4 | 5 | initialize : function(){ 6 | 7 | } 8 | 9 | }) 10 | -------------------------------------------------------------------------------- /src/www/js/models/info-widget-model.js: -------------------------------------------------------------------------------- 1 | var InfoWidgetModel = Backbone.Model.extend({ 2 | 3 | url: "api/info", 4 | 5 | initialize: function() { 6 | }, 7 | 8 | defaults: { 9 | "databases" : {} 10 | } 11 | 12 | }) 13 | -------------------------------------------------------------------------------- /src/api/controller/BaseStaticFileHandler.py: -------------------------------------------------------------------------------- 1 | import tornado.web 2 | 3 | class BaseStaticFileHandler(tornado.web.StaticFileHandler): 4 | def compute_etag(self): 5 | return None 6 | 7 | def get_cache_time(self, path, modified, mime_type): 8 | return None 9 | 10 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/utilities.less: -------------------------------------------------------------------------------- 1 | // UTILITY CLASSES 2 | // --------------- 3 | 4 | // Quick floats 5 | .pull-right { 6 | float: right; 7 | } 8 | .pull-left { 9 | float: left; 10 | } 11 | 12 | // Toggling content 13 | .hide { 14 | display: none; 15 | } 16 | .show { 17 | display: block; 18 | } 19 | 20 | // Visibility 21 | .invisible { 22 | visibility: hidden; 23 | } 24 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/component-animations.less: -------------------------------------------------------------------------------- 1 | // COMPONENT ANIMATIONS 2 | // -------------------- 3 | 4 | .fade { 5 | .transition(opacity .15s linear); 6 | opacity: 0; 7 | &.in { 8 | opacity: 1; 9 | } 10 | } 11 | 12 | .collapse { 13 | .transition(height .35s ease); 14 | position:relative; 15 | overflow:hidden; 16 | height: 0; 17 | &.in { 18 | height: auto; 19 | } 20 | } -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/close.less: -------------------------------------------------------------------------------- 1 | // CLOSE ICONS 2 | // ----------- 3 | 4 | .close { 5 | float: right; 6 | font-size: 20px; 7 | font-weight: bold; 8 | line-height: @baseLineHeight; 9 | color: @black; 10 | text-shadow: 0 1px 0 rgba(255,255,255,1); 11 | .opacity(20); 12 | &:hover { 13 | color: @black; 14 | text-decoration: none; 15 | .opacity(40); 16 | cursor: pointer; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/tests/unit/bootstrap-transition.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-transition") 4 | 5 | test("should be defined on jquery support object", function () { 6 | ok($.support.transition != undefined, 'transition object is defined') 7 | }) 8 | 9 | test("should provide an end object", function () { 10 | ok($.support.transition ? $.support.transition.end : true, 'end string is defined') 11 | }) 12 | 13 | }) -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/layouts.less: -------------------------------------------------------------------------------- 1 | // 2 | // Layouts 3 | // Fixed-width and fluid (with sidebar) layouts 4 | // -------------------------------------------- 5 | 6 | 7 | // Container (centered, fixed-width layouts) 8 | .container { 9 | .container-fixed(); 10 | } 11 | 12 | // Fluid layouts (left aligned, with sidebar, min- & max-width content) 13 | .container-fluid { 14 | padding-left: @gridGutterWidth; 15 | padding-right: @gridGutterWidth; 16 | .clearfix(); 17 | } -------------------------------------------------------------------------------- /src/db/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE info(datetime text,info text, server text); 2 | 3 | CREATE TABLE keys(datetime text, expire number, persist number, server text); 4 | 5 | CREATE TABLE memory(datetime text,current real,max real, server text); 6 | 7 | CREATE TABLE "monitor" ("datetime" datetime,"command" text,"arguments" text,"server" text, keyname text); 8 | 9 | CREATE INDEX "monitor_dateTime_Index" ON "monitor" ("datetime" DESC); 10 | 11 | CREATE INDEX serverIndex on monitor(server); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | 3 | # Packages 4 | *.egg 5 | *.egg-info 6 | dist 7 | build 8 | eggs 9 | parts 10 | bin 11 | var 12 | sdist 13 | develop-eggs 14 | .installed.cfg 15 | 16 | # Installer logs 17 | pip-log.txt 18 | 19 | # Unit test / coverage reports 20 | .coverage 21 | .tox 22 | 23 | #Translations 24 | *.mo 25 | 26 | #Mr Developer 27 | .mr.developer.cfg 28 | 29 | .DS_Store 30 | 31 | *.sublime-workspace 32 | *.sublime-project 33 | src/redis-live.conf 34 | 35 | 36 | # Vim 37 | *.sw[po] 38 | venv 39 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | MAINTAINER Lyndon li 3 | RUN apk add --update py-pip && rm -rf /var/cache/apk/* 4 | RUN mkdir -p /redislive 5 | COPY . /redislive 6 | RUN cd /redislive \ 7 | && pip install -r requirements.txt 8 | 9 | WORKDIR /redislive/src 10 | RUN mv redis-live.conf.example redis-live.conf 11 | 12 | EXPOSE 8888 13 | 14 | # Configure container to run as an executable 15 | CMD ["./redis-monitor.py", "--duration=120", "--quiet"] 16 | 17 | ENTRYPOINT ["./redis-live.py"] 18 | -------------------------------------------------------------------------------- /src/redis-live.conf.example: -------------------------------------------------------------------------------- 1 | { 2 | "RedisServers": 3 | [ 4 | { 5 | "server": "154.17.59.99", 6 | "port" : 6379 7 | }, 8 | 9 | { 10 | "server": "localhost", 11 | "port" : 6380, 12 | "password" : "some-password" 13 | } 14 | ], 15 | 16 | "DataStoreType" : "redis", 17 | 18 | "RedisStatsServer": 19 | { 20 | "server" : "ec2-184-72-166-144.compute-1.amazonaws.com", 21 | "port" : 6385 22 | }, 23 | 24 | "SqliteStatsStore" : 25 | { 26 | "path": "to your sql lite file" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/util/redis-fill-data2.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import redis 4 | from time import strftime 5 | import time 6 | import random 7 | 8 | def monitor(): 9 | redisHost = "127.0.0.1" 10 | redisPort = 6381 11 | redisClient = redis.StrictRedis(host=redisHost, port=redisPort, db=0) 12 | 13 | while True: 14 | x=1 15 | redisClient.set("Key:" + `x`, x) 16 | redisClient.set("KeyYU:" + `x`, x) 17 | redisClient.set("Key:" + `x`, x) 18 | redisClient.set("KeyYU:" + `x`, x) 19 | 20 | if __name__ == '__main__': 21 | monitor() -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/hero-unit.less: -------------------------------------------------------------------------------- 1 | // HERO UNIT 2 | // --------- 3 | 4 | .hero-unit { 5 | padding: 60px; 6 | margin-bottom: 30px; 7 | background-color: @heroUnitBackground; 8 | .border-radius(6px); 9 | h1 { 10 | margin-bottom: 0; 11 | font-size: 60px; 12 | line-height: 1; 13 | color: @heroUnitHeadingColor; 14 | letter-spacing: -1px; 15 | } 16 | p { 17 | font-size: 18px; 18 | font-weight: 200; 19 | line-height: @baseLineHeight * 1.5; 20 | color: @heroUnitLeadColor; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/breadcrumbs.less: -------------------------------------------------------------------------------- 1 | // BREADCRUMBS 2 | // ----------- 3 | 4 | .breadcrumb { 5 | padding: 7px 14px; 6 | margin: 0 0 @baseLineHeight; 7 | list-style: none; 8 | #gradient > .vertical(@white, #f5f5f5); 9 | border: 1px solid #ddd; 10 | .border-radius(3px); 11 | .box-shadow(inset 0 1px 0 @white); 12 | li { 13 | display: inline-block; 14 | .ie7-inline-block(); 15 | text-shadow: 0 1px 0 @white; 16 | } 17 | .divider { 18 | padding: 0 5px; 19 | color: @grayLight; 20 | } 21 | .active a { 22 | color: @grayDark; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/accordion.less: -------------------------------------------------------------------------------- 1 | // ACCORDION 2 | // --------- 3 | 4 | 5 | // Parent container 6 | .accordion { 7 | margin-bottom: @baseLineHeight; 8 | } 9 | 10 | // Group == heading + body 11 | .accordion-group { 12 | margin-bottom: 2px; 13 | border: 1px solid #e5e5e5; 14 | .border-radius(4px); 15 | } 16 | .accordion-heading { 17 | border-bottom: 0; 18 | } 19 | .accordion-heading .accordion-toggle { 20 | display: block; 21 | padding: 8px 15px; 22 | } 23 | 24 | // Inner needs the styles because you can't animate properly with any styles on the element 25 | .accordion-inner { 26 | padding: 9px 15px; 27 | border-top: 1px solid #e5e5e5; 28 | } 29 | -------------------------------------------------------------------------------- /src/www/js/views/serverlist-view.js: -------------------------------------------------------------------------------- 1 | var ServerList = Backbone.View.extend({ 2 | 3 | initialize : function() { 4 | this.$el.empty() 5 | this.model.on("change", this.render, this) 6 | this.$el.on("change", this.ServerChanged) 7 | this.model.fetch() 8 | } 9 | 10 | , ServerChanged : function(){ 11 | $(document).trigger("ServerChange", $(this).val()) 12 | } 13 | 14 | , render : function() { 15 | var model = this.model.toJSON() 16 | , self = this 17 | 18 | $.each(model.servers,function(index, obj){ 19 | self.$el.append("") 20 | }) 21 | 22 | self.$el.trigger("change") 23 | } 24 | 25 | }) -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/scaffolding.less: -------------------------------------------------------------------------------- 1 | // Scaffolding 2 | // Basic and global styles for generating a grid system, structural layout, and page templates 3 | // ------------------------------------------------------------------------------------------- 4 | 5 | 6 | // Body reset 7 | // ---------- 8 | 9 | body { 10 | margin: 0; 11 | padding-top: 70px; 12 | font-family: @baseFontFamily; 13 | font-size: @baseFontSize; 14 | line-height: @baseLineHeight; 15 | color: @textColor; 16 | background-color: @bodyBackground; 17 | } 18 | 19 | 20 | // Links 21 | // ----- 22 | 23 | a { 24 | color: @linkColor; 25 | text-decoration: none; 26 | } 27 | a:hover { 28 | color: @linkColorHover; 29 | text-decoration: underline; 30 | } 31 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/wells.less: -------------------------------------------------------------------------------- 1 | // WELLS 2 | // ----- 3 | 4 | .well { 5 | min-height: 20px; 6 | padding: 0px; 7 | margin-bottom: 0px; 8 | background-color: #DAEAF1; 9 | border: 1px solid #DAEAF1; 10 | //border: 1px solid rgba(0,0,0,.05); 11 | .border-radius(4px); 12 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); 13 | blockquote { 14 | border-color: #ddd; 15 | border-color: rgba(0,0,0,.15); 16 | } 17 | height:65px; 18 | } 19 | 20 | // Sizes 21 | .well-large { 22 | padding: 24px; 23 | .border-radius(6px); 24 | } 25 | .well-small { 26 | padding: 10px; 27 | .border-radius(0px); 28 | } 29 | 30 | .alert 31 | { 32 | background-color: #E70B20 !important; 33 | color:white !important; 34 | } 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/pager.less: -------------------------------------------------------------------------------- 1 | // PAGER 2 | // ----- 3 | 4 | .pager { 5 | margin-left: 0; 6 | margin-bottom: @baseLineHeight; 7 | list-style: none; 8 | text-align: center; 9 | .clearfix(); 10 | } 11 | .pager li { 12 | display: inline; 13 | } 14 | .pager a { 15 | display: inline-block; 16 | padding: 5px 14px; 17 | background-color: #fff; 18 | border: 1px solid #ddd; 19 | .border-radius(15px); 20 | } 21 | .pager a:hover { 22 | text-decoration: none; 23 | background-color: #f5f5f5; 24 | } 25 | .pager .next a { 26 | float: right; 27 | } 28 | .pager .previous a { 29 | float: left; 30 | } 31 | .pager .disabled a, 32 | .pager .disabled a:hover { 33 | color: @grayLight; 34 | background-color: #fff; 35 | cursor: default; 36 | } -------------------------------------------------------------------------------- /src/api/util/timeutils.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | # Original fix for Py2.6: https://github.com/mozilla/mozdownload/issues/73 3 | def total_seconds(td): 4 | # Keep backward compatibility with Python 2.6 which doesn't have 5 | # this method 6 | if hasattr(td, 'total_seconds'): 7 | return td.total_seconds() 8 | else: 9 | return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 10 | 11 | def convert_to_epoch(timestamp): 12 | if (type(timestamp) is datetime.date): 13 | timestamp = datetime.datetime.fromordinal(timestamp.toordinal()) 14 | timestamp = timestamp.replace(tzinfo=None) 15 | diff = (timestamp - datetime.datetime(1970, 1, 1)) 16 | seconds = int(total_seconds(diff)) 17 | return seconds 18 | -------------------------------------------------------------------------------- /src/dataprovider/dataprovider.py: -------------------------------------------------------------------------------- 1 | from api.util import settings 2 | import sqliteprovider 3 | import redisprovider 4 | 5 | 6 | # TODO: Confirm there's not some implementation detail I've missed, then 7 | # ditch the classes here. 8 | class RedisLiveDataProvider(object): 9 | 10 | @staticmethod 11 | def get_provider(): 12 | """Returns a data provider based on the settings file. 13 | 14 | Valid providers are currently Redis and SQLite. 15 | """ 16 | data_store_type = settings.get_data_store_type() 17 | 18 | # FIXME: Should use a global variable for "redis" here. 19 | if data_store_type == "redis": 20 | return redisprovider.RedisStatsProvider() 21 | else: 22 | return sqliteprovider.RedisStatsProvider() 23 | -------------------------------------------------------------------------------- /src/www/js/google/visualization.js: -------------------------------------------------------------------------------- 1 | if (window['google'] != undefined && window['google']['loader'] != undefined) { 2 | if (!window['google']['visualization']) { 3 | window['google']['visualization'] = {}; 4 | google.visualization.Version = '1.0'; 5 | google.visualization.JSHash = 'abbdd6ab2d343a51d49841bf93d022fb'; 6 | google.visualization.LoadArgs = 'file\75visualization\46v\0751\46packages\75corechart'; 7 | } 8 | // google.loader.writeLoadTag("css", google.loader.ServiceBase + "/api/visualization/1.0/abbdd6ab2d343a51d49841bf93d022fb/ui+en.css", false); 9 | // google.loader.writeLoadTag("script", google.loader.ServiceBase + "/api/visualization/1.0/abbdd6ab2d343a51d49841bf93d022fb/format+en,default+en,ui+en,corechart+en.I.js", false); 10 | } 11 | -------------------------------------------------------------------------------- /src/api/util/settings.py: -------------------------------------------------------------------------------- 1 | from __future__ import with_statement 2 | import os 3 | import json 4 | 5 | 6 | def get_settings(): 7 | """Parses the settings from redis-live.conf. 8 | """ 9 | # TODO: Consider YAML. Human writable, machine readable. 10 | with open(os.getenv("REDISLIVE_CONFIG", "redis-live.conf")) as config: 11 | return json.load(config) 12 | 13 | 14 | def get_redis_servers(): 15 | config = get_settings() 16 | return config["RedisServers"] 17 | 18 | 19 | def get_redis_stats_server(): 20 | config = get_settings() 21 | return config["RedisStatsServer"] 22 | 23 | 24 | def get_data_store_type(): 25 | config = get_settings() 26 | return config["DataStoreType"] 27 | 28 | 29 | def get_sqlite_stats_store(): 30 | config = get_settings() 31 | return config["SqliteStatsStore"] 32 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/thumbnails.less: -------------------------------------------------------------------------------- 1 | // THUMBNAILS 2 | // ---------- 3 | 4 | .thumbnails { 5 | margin-left: -@gridGutterWidth; 6 | list-style: none; 7 | .clearfix(); 8 | } 9 | .thumbnails > li { 10 | float: left; 11 | margin: 0 0 @baseLineHeight @gridGutterWidth; 12 | } 13 | .thumbnail { 14 | display: block; 15 | padding: 4px; 16 | line-height: 1; 17 | border: 1px solid #ddd; 18 | .border-radius(4px); 19 | .box-shadow(0 1px 1px rgba(0,0,0,.075)); 20 | } 21 | // Add a hover state for linked versions only 22 | a.thumbnail:hover { 23 | border-color: @linkColor; 24 | .box-shadow(0 1px 4px rgba(0,105,214,.25)); 25 | } 26 | // Images and captions 27 | .thumbnail > img { 28 | display: block; 29 | max-width: 100%; 30 | margin-left: auto; 31 | margin-right: auto; 32 | } 33 | .thumbnail .caption { 34 | padding: 9px; 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/api/controller/ServerListController.py: -------------------------------------------------------------------------------- 1 | from BaseController import BaseController 2 | from api.util import settings 3 | 4 | class ServerListController(BaseController): 5 | 6 | def get(self): 7 | servers = {"servers": self.read_server_config()} 8 | self.write(servers) 9 | 10 | def read_server_config(self): 11 | """Returns a list of servers with the 'id' field added. 12 | """ 13 | # TODO: Move this into the settings module so everything benefits. 14 | server_list = [] 15 | redis_servers = settings.get_redis_servers() 16 | 17 | for server in redis_servers: 18 | if 'password' not in server: 19 | server['password'] = None 20 | 21 | server_id = "%(server)s:%(port)s" % server 22 | s = dict(server=server['server'], port=server['port'], password=server['password'], id=server_id) 23 | server_list.append(s) 24 | 25 | return server_list 26 | -------------------------------------------------------------------------------- /src/api/controller/TopKeysController.py: -------------------------------------------------------------------------------- 1 | from BaseController import BaseController 2 | import tornado.ioloop 3 | import tornado.web 4 | import dateutil.parser 5 | import datetime 6 | 7 | 8 | class TopKeysController(BaseController): 9 | 10 | def get(self): 11 | return_data = dict(data=[], timestamp=datetime.datetime.now().isoformat()) 12 | 13 | server = self.get_argument("server") 14 | from_date = self.get_argument("from", None) 15 | to_date = self.get_argument("to", None) 16 | 17 | if not from_date or not to_date: 18 | end = datetime.datetime.now() 19 | delta = datetime.timedelta(seconds=120) 20 | start = end - delta 21 | else: 22 | start = dateutil.parser.parse(from_date) 23 | end = dateutil.parser.parse(to_date) 24 | 25 | for data in self.stats_provider.get_top_keys_stats(server, start, end): 26 | return_data['data'].append([data[0], data[1]]) 27 | 28 | self.write(return_data) 29 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/tooltip.less: -------------------------------------------------------------------------------- 1 | // TOOLTIP 2 | // ------= 3 | 4 | .tooltip { 5 | position: absolute; 6 | z-index: @zindexTooltip; 7 | display: block; 8 | visibility: visible; 9 | padding: 5px; 10 | font-size: 11px; 11 | .opacity(0); 12 | &.in { .opacity(80); } 13 | &.top { margin-top: -2px; } 14 | &.right { margin-left: 2px; } 15 | &.bottom { margin-top: 2px; } 16 | &.left { margin-left: -2px; } 17 | &.top .tooltip-arrow { #popoverArrow > .top(); } 18 | &.left .tooltip-arrow { #popoverArrow > .left(); } 19 | &.bottom .tooltip-arrow { #popoverArrow > .bottom(); } 20 | &.right .tooltip-arrow { #popoverArrow > .right(); } 21 | } 22 | .tooltip-inner { 23 | max-width: 200px; 24 | padding: 3px 8px; 25 | color: @white; 26 | text-align: center; 27 | text-decoration: none; 28 | background-color: @black; 29 | .border-radius(4px); 30 | } 31 | .tooltip-arrow { 32 | position: absolute; 33 | width: 0; 34 | height: 0; 35 | } 36 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/tests/unit/bootstrap-collapse.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-collapse") 4 | 5 | test("should be defined on jquery object", function () { 6 | ok($(document.body).collapse, 'collapse method is defined') 7 | }) 8 | 9 | test("should return element", function () { 10 | ok($(document.body).collapse()[0] == document.body, 'document.body returned') 11 | }) 12 | 13 | test("should show a collapsed element", function () { 14 | var el = $('
').collapse('show') 15 | ok(el.hasClass('in'), 'has class in') 16 | ok(/height/.test(el.attr('style')), 'has height set') 17 | }) 18 | 19 | test("should hide a collapsed element", function () { 20 | var el = $('
').collapse('hide') 21 | ok(!el.hasClass('in'), 'does not have class in') 22 | ok(/height/.test(el.attr('style')), 'has height set') 23 | }) 24 | 25 | }) -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/widget.less: -------------------------------------------------------------------------------- 1 | // WIDGETS 2 | // ---------- 3 | 4 | .widget { 5 | //display: block; 6 | //padding: 10px; 7 | //margin:10px; 8 | //line-height: 1; 9 | //border: 1px solid #ddd; 10 | //.border-radius(4px); 11 | //.box-shadow(0 1px 1px rgba(0,0,0,.075)); 12 | } 13 | 14 | .info-box{ 15 | min-height: 20px; 16 | min-width: 250px; 17 | padding: 5px; 18 | margin-bottom: 0px; 19 | background-color: #DAEAF1; 20 | border: 1px solid #DAEAF1; 21 | //border: 1px solid rgba(0,0,0,.05); 22 | .border-radius(0px); 23 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); 24 | blockquote { 25 | border-color: #ddd; 26 | border-color: rgba(0,0,0,.15); 27 | } 28 | height:65px; 29 | } 30 | 31 | .date-control{ 32 | display: inline-block; 33 | position: relative; 34 | top:-10px; 35 | } 36 | 37 | .well-value { 38 | font-size: 60px; 39 | padding: 10px; 40 | weight:bold; 41 | color: #1581AA; 42 | //color: #000; 43 | } 44 | 45 | 46 | .server-list{ 47 | margin-top: 7px !important; 48 | } -------------------------------------------------------------------------------- /src/api/controller/TopCommandsController.py: -------------------------------------------------------------------------------- 1 | from BaseController import BaseController 2 | import tornado.ioloop 3 | import tornado.web 4 | import dateutil.parser 5 | import datetime 6 | 7 | 8 | class TopCommandsController(BaseController): 9 | 10 | def get(self): 11 | return_data = dict(data=[], 12 | timestamp=datetime.datetime.now().isoformat()) 13 | 14 | server = self.get_argument("server") 15 | from_date = self.get_argument("from", None) 16 | to_date = self.get_argument("to", None) 17 | 18 | if not from_date or not to_date: 19 | end = datetime.datetime.now() 20 | delta = datetime.timedelta(seconds=120) 21 | start = end - delta 22 | else: 23 | start = dateutil.parser.parse(from_date) 24 | end = dateutil.parser.parse(to_date) 25 | 26 | for data in self.stats_provider.get_top_commands_stats(server, start, 27 | end): 28 | return_data['data'].append([data[0], data[1]]) 29 | 30 | self.write(return_data) 31 | -------------------------------------------------------------------------------- /MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Nitin Kumar 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to 8 | do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/tests/unit/bootstrap-scrollspy.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-scrollspy") 4 | 5 | test("should be defined on jquery object", function () { 6 | ok($(document.body).scrollspy, 'scrollspy method is defined') 7 | }) 8 | 9 | test("should return element", function () { 10 | ok($(document.body).scrollspy()[0] == document.body, 'document.body returned') 11 | }) 12 | 13 | test("should switch active class on scroll", function () { 14 | var sectionHTML = '
' 15 | , $section = $(sectionHTML).append('#qunit-fixture') 16 | , topbarHTML ='
' 17 | + '
' 18 | + '
' 19 | + '

Bootstrap

' 20 | + '' 23 | + '
' 24 | + '
' 25 | + '
' 26 | , $topbar = $(topbarHTML).scrollspy() 27 | 28 | ok($topbar.find('.active', true)) 29 | }) 30 | 31 | }) -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/badges.less: -------------------------------------------------------------------------------- 1 | // BADGES 2 | // ------ 3 | 4 | // Base 5 | /*.badge { 6 | padding: 1px 9px 2px; 7 | font-size: @baseFontSize * .925; 8 | font-weight: bold; 9 | white-space: nowrap; 10 | color: @white; 11 | background-color: @grayLight; 12 | .border-radius(9px); 13 | } 14 | */ 15 | /*// Hover state 16 | .badge:hover { 17 | color: @white; 18 | text-decoration: none; 19 | cursor: pointer; 20 | }*/ 21 | 22 | 23 | 24 | /*// Colors 25 | .badge-error { background-color: @errorText; } 26 | .badge-error:hover { background-color: darken(@errorText, 10%); } 27 | 28 | .badge-warning { background-color: @orange; } 29 | .badge-warning:hover { background-color: darken(@orange, 10%); } 30 | 31 | .badge-success { background-color: @successText; } 32 | .badge-success:hover { background-color: darken(@successText, 10%); } 33 | 34 | .badge-info { background-color: @infoText; } 35 | .badge-info:hover { background-color: darken(@infoText, 10%); } 36 | 37 | .badge-inverse { background-color: @grayDark; } 38 | .badge-inverse:hover { background-color: darken(@grayDark, 10%); }*/ -------------------------------------------------------------------------------- /src/util/redis-fill-data.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import redis 4 | from time import strftime 5 | import time 6 | import random 7 | 8 | def monitor(): 9 | redisHost = "127.0.0.1" 10 | redisPort = 6379 11 | redisClient = redis.StrictRedis(host=redisHost, port=redisPort, db=0) 12 | 13 | while True: 14 | 15 | x = random.randint(1, 100) 16 | y = random.randint(1, 20) 17 | 18 | if y==1: 19 | for z in xrange(1,x): 20 | redisClient.set("Key:" + `x`, x) 21 | elif y==2: 22 | for z in xrange(1,x): 23 | redisClient.get("Key:" + `x`) 24 | elif y==4: 25 | for z in xrange(1,x): 26 | redisClient.hset("HashKey:" + `x`, x, x) 27 | elif y==5: 28 | for z in xrange(1,(x/2)+2): 29 | redisClient.setex("Key:" + `x`, 1000, x) 30 | elif y==6: 31 | for z in xrange(1,x): 32 | redisClient.hexists("HashKey:" + `x`, y) 33 | elif y==7: 34 | for z in xrange(1,x): 35 | redisClient.setbit("BitSet:" + `x`, 1, 1) 36 | elif y==8: 37 | for z in xrange(1,x): 38 | redisClient.getbit("BitSet:" + `x`, 1) 39 | elif y==9: 40 | for z in xrange(1,x): 41 | redisClient.expire("Key:"+ `x`, 2000) 42 | elif y==11: 43 | redisClient.flushall() 44 | 45 | 46 | 47 | if __name__ == '__main__': 48 | monitor() -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/labels.less: -------------------------------------------------------------------------------- 1 | // LABELS 2 | // ------ 3 | 4 | // Base 5 | .label { 6 | padding: 1px 4px 2px; 7 | font-size: @baseFontSize * .846; 8 | font-weight: bold; 9 | line-height: 13px; // ensure proper line-height if floated 10 | color: @white; 11 | vertical-align: middle; 12 | white-space: nowrap; 13 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 14 | background-color: @grayLight; 15 | .border-radius(3px); 16 | } 17 | 18 | // Hover state 19 | .label:hover { 20 | color: @white; 21 | text-decoration: none; 22 | } 23 | 24 | // Colors 25 | .label-important { background-color: @errorText; } 26 | .label-important:hover { background-color: darken(@errorText, 10%); } 27 | 28 | .label-warning { background-color: @orange; } 29 | .label-warning:hover { background-color: darken(@orange, 10%); } 30 | 31 | .label-success { background-color: @successText; } 32 | .label-success:hover { background-color: darken(@successText, 10%); } 33 | 34 | .label-info { background-color: @infoText; } 35 | .label-info:hover { background-color: darken(@infoText, 10%); } 36 | 37 | .label-inverse { background-color: @grayDark; } 38 | .label-inverse:hover { background-color: darken(@grayDark, 10%); } -------------------------------------------------------------------------------- /src/api/util/RDP.py: -------------------------------------------------------------------------------- 1 | """ 2 | The Ramer-Douglas-Peucker algorithm roughly ported from the pseudo-code provided 3 | by http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm 4 | """ 5 | 6 | from math import sqrt 7 | 8 | def distance(a, b): 9 | return sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2) 10 | 11 | def point_line_distance(point, start, end): 12 | if (start == end): 13 | return distance(point, start) 14 | else: 15 | n = abs( 16 | (end[0] - start[0]) * (start[1] - point[1]) - (start[0] - point[0]) * (end[1] - start[1]) 17 | ) 18 | d = sqrt( 19 | (end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2 20 | ) 21 | return n / d 22 | 23 | def rdp(points, epsilon): 24 | """ 25 | Reduces a series of points to a simplified version that loses detail, but 26 | maintains the general shape of the series. 27 | """ 28 | dmax = 0.0 29 | index = 0 30 | for i in range(1, len(points) - 1): 31 | d = point_line_distance(points[i], points[0], points[-1]) 32 | if d > dmax: 33 | index = i 34 | dmax = d 35 | if dmax >= epsilon: 36 | results = rdp(points[:index+1], epsilon)[:-1] + rdp(points[index:], epsilon) 37 | else: 38 | results = [points[0], points[-1]] 39 | return results -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/pagination.less: -------------------------------------------------------------------------------- 1 | // PAGINATION 2 | // ---------- 3 | 4 | .pagination { 5 | height: @baseLineHeight * 2; 6 | margin: @baseLineHeight 0; 7 | } 8 | .pagination ul { 9 | display: inline-block; 10 | .ie7-inline-block(); 11 | margin-left: 0; 12 | margin-bottom: 0; 13 | .border-radius(3px); 14 | .box-shadow(0 1px 2px rgba(0,0,0,.05)); 15 | } 16 | .pagination li { 17 | display: inline; 18 | } 19 | .pagination a { 20 | float: left; 21 | padding: 0 14px; 22 | line-height: (@baseLineHeight * 2) - 2; 23 | text-decoration: none; 24 | border: 1px solid #ddd; 25 | border-left-width: 0; 26 | } 27 | .pagination a:hover, 28 | .pagination .active a { 29 | background-color: #f5f5f5; 30 | } 31 | .pagination .active a { 32 | color: @grayLight; 33 | cursor: default; 34 | } 35 | .pagination .disabled span, 36 | .pagination .disabled a, 37 | .pagination .disabled a:hover { 38 | color: @grayLight; 39 | background-color: transparent; 40 | cursor: default; 41 | } 42 | .pagination li:first-child a { 43 | border-left-width: 1px; 44 | .border-radius(3px 0 0 3px); 45 | } 46 | .pagination li:last-child a { 47 | .border-radius(0 3px 3px 0); 48 | } 49 | 50 | // Centered 51 | .pagination-centered { 52 | text-align: center; 53 | } 54 | .pagination-right { 55 | text-align: right; 56 | } 57 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/alerts.less: -------------------------------------------------------------------------------- 1 | // ALERT STYLES 2 | // ------------ 3 | 4 | // Base alert styles 5 | .alert { 6 | padding: 8px 35px 8px 14px; 7 | margin-bottom: @baseLineHeight; 8 | text-shadow: 0 1px 0 rgba(255,255,255,.5); 9 | background-color: @warningBackground; 10 | border: 1px solid @warningBorder; 11 | .border-radius(4px); 12 | color: @warningText; 13 | } 14 | .alert-heading { 15 | color: inherit; 16 | } 17 | 18 | // Adjust close link position 19 | .alert .close { 20 | position: relative; 21 | top: -2px; 22 | right: -21px; 23 | line-height: 18px; 24 | } 25 | 26 | // Alternate styles 27 | // ---------------- 28 | 29 | .alert-success { 30 | background-color: @successBackground; 31 | border-color: @successBorder; 32 | color: @successText; 33 | } 34 | .alert-danger, 35 | .alert-error { 36 | background-color: @errorBackground; 37 | border-color: @errorBorder; 38 | color: @errorText; 39 | } 40 | .alert-info { 41 | background-color: @infoBackground; 42 | border-color: @infoBorder; 43 | color: @infoText; 44 | } 45 | 46 | // Block alerts 47 | // ------------------------ 48 | .alert-block { 49 | padding-top: 14px; 50 | padding-bottom: 14px; 51 | } 52 | .alert-block > p, 53 | .alert-block > ul { 54 | margin-bottom: 0; 55 | } 56 | .alert-block p + p { 57 | margin-top: 5px; 58 | } 59 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/popovers.less: -------------------------------------------------------------------------------- 1 | // POPOVERS 2 | // -------- 3 | 4 | .popover { 5 | position: absolute; 6 | top: 0; 7 | left: 0; 8 | z-index: @zindexPopover; 9 | display: none; 10 | padding: 5px; 11 | &.top { margin-top: -5px; } 12 | &.right { margin-left: 5px; } 13 | &.bottom { margin-top: 5px; } 14 | &.left { margin-left: -5px; } 15 | &.top .arrow { #popoverArrow > .top(); } 16 | &.right .arrow { #popoverArrow > .right(); } 17 | &.bottom .arrow { #popoverArrow > .bottom(); } 18 | &.left .arrow { #popoverArrow > .left(); } 19 | .arrow { 20 | position: absolute; 21 | width: 0; 22 | height: 0; 23 | } 24 | } 25 | .popover-inner { 26 | padding: 3px; 27 | width: 280px; 28 | overflow: hidden; 29 | background: @black; // has to be full background declaration for IE fallback 30 | background: rgba(0,0,0,.8); 31 | .border-radius(6px); 32 | .box-shadow(0 3px 7px rgba(0,0,0,0.3)); 33 | } 34 | .popover-title { 35 | padding: 9px 15px; 36 | line-height: 1; 37 | background-color: #f5f5f5; 38 | border-bottom:1px solid #eee; 39 | .border-radius(3px 3px 0 0); 40 | } 41 | .popover-content { 42 | padding: 14px; 43 | background-color: @white; 44 | .border-radius(0 0 3px 3px); 45 | .background-clip(padding-box); 46 | p, ul, ol { 47 | margin-bottom: 0; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/api/controller/MemoryController.py: -------------------------------------------------------------------------------- 1 | from BaseController import BaseController 2 | import tornado.ioloop 3 | import tornado.web 4 | import dateutil.parser 5 | import datetime 6 | 7 | 8 | class MemoryController(BaseController): 9 | 10 | def get(self): 11 | server = self.get_argument("server") 12 | from_date = self.get_argument("from", None) 13 | to_date = self.get_argument("to", None) 14 | 15 | return_data = dict(data=[], 16 | timestamp=datetime.datetime.now().isoformat()) 17 | 18 | if not from_date or not to_date: 19 | end = datetime.datetime.now() 20 | delta = datetime.timedelta(seconds=60) 21 | start = end - delta 22 | else: 23 | start = dateutil.parser.parse(from_date) 24 | end = dateutil.parser.parse(to_date) 25 | 26 | combined_data = [] 27 | # TODO: These variables aren't currently used; should they be removed? 28 | prev_max=0 29 | prev_current=0 30 | counter=0 31 | 32 | for data in self.stats_provider.get_memory_info(server, start, end): 33 | combined_data.append([data[0], data[1], data[2]]) 34 | 35 | for data in combined_data: 36 | d = [self.datetime_to_list(data[0]), data[1], data[2]] 37 | return_data['data'].append(d) 38 | 39 | self.write(return_data) 40 | 41 | -------------------------------------------------------------------------------- /src/www/js/views/info-widget-view.js: -------------------------------------------------------------------------------- 1 | /* Info Widget 2 | * ====================== */ 3 | 4 | var InfoWidget = BaseWidget.extend({ 5 | 6 | initialize : function() { 7 | 8 | this.Name = "Info Widget" 9 | 10 | this.init() 11 | this.updateFrequency = 5000 // every 5 seconds 12 | 13 | // templates 14 | var templateSource = $("#info-widget-template").html() 15 | , popOverTemplateSource = $("#popover-template").html() 16 | , infoTemplateSource = $("#info-template").html() 17 | 18 | this.template = Handlebars.compile(templateSource) 19 | this.popOverTemplate = Handlebars.compile(popOverTemplateSource) 20 | this.infoTemplate = Handlebars.compile(infoTemplateSource) 21 | 22 | } 23 | 24 | , render: function() { 25 | 26 | var model = this.model.toJSON() 27 | , markUp = this.template(model) 28 | , popoverMarkup = this.popOverTemplate(model.databases) 29 | , infoMarkup = this.infoTemplate(model) 30 | 31 | $(this.el).html(markUp) 32 | 33 | $('#total-keys').popover({ 34 | "title" : "databases" 35 | , "content" : popoverMarkup 36 | }) 37 | 38 | $('#misc-info').popover({ 39 | "title" : "info" 40 | , "content" : infoMarkup 41 | , "placement" : "bottom" 42 | }) 43 | } 44 | 45 | }) -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/code.less: -------------------------------------------------------------------------------- 1 | // Code.less 2 | // Code typography styles for the and
 elements
 3 | // --------------------------------------------------------
 4 | 
 5 | // Inline and block code styles
 6 | code,
 7 | pre {
 8 |   padding: 0 3px 2px;
 9 |   #font > #family > .monospace;
10 |   font-size: @baseFontSize - 1;
11 |   color: @grayDark;
12 |   .border-radius(3px);
13 | }
14 | 
15 | // Inline code
16 | code {
17 |   padding: 2px 4px;
18 |   color: #d14;
19 |   background-color: #f7f7f9;
20 |   border: 1px solid #e1e1e8;
21 | }
22 | 
23 | // Blocks of code
24 | pre {
25 |   display: block;
26 |   padding: (@baseLineHeight - 1) / 2;
27 |   margin: 0 0 @baseLineHeight / 2;
28 |   font-size: @baseFontSize * .925; // 13px to 12px
29 |   line-height: @baseLineHeight;
30 |   background-color: #f5f5f5;
31 |   border: 1px solid #ccc; // fallback for IE7-8
32 |   border: 1px solid rgba(0,0,0,.15);
33 |   .border-radius(4px);
34 |   white-space: pre;
35 |   white-space: pre-wrap;
36 |   word-break: break-all;
37 |   word-wrap: break-word;
38 | 
39 |   // Make prettyprint styles more spaced out for readability
40 |   &.prettyprint {
41 |     margin-bottom: @baseLineHeight;
42 |   }
43 | 
44 |   // Account for some code outputs that place code tags in pre tags
45 |   code {
46 |     padding: 0;
47 |     color: inherit;
48 |     background-color: transparent;
49 |     border: 0;
50 |   }
51 | }
52 | 
53 | // Enable scrollable blocks of code
54 | .pre-scrollable {
55 |   max-height: 340px;
56 |   overflow-y: scroll;
57 | }


--------------------------------------------------------------------------------
/src/redis-live.py:
--------------------------------------------------------------------------------
 1 | #! /usr/bin/env python
 2 | 
 3 | import tornado.ioloop
 4 | import tornado.options
 5 | from tornado.options import define, options
 6 | import tornado.web
 7 | 
 8 | from api.controller.BaseStaticFileHandler import BaseStaticFileHandler
 9 | 
10 | from api.controller.ServerListController import ServerListController
11 | from api.controller.InfoController import InfoController
12 | from api.controller.MemoryController import MemoryController
13 | from api.controller.CommandsController import CommandsController
14 | from api.controller.TopCommandsController import TopCommandsController
15 | from api.controller.TopKeysController import TopKeysController
16 | 
17 | if __name__ == "__main__":
18 |     define("port", default=8888, help="run on the given port", type=int)
19 |     define("debug", default=0, help="debug mode", type=int)
20 |     tornado.options.parse_command_line()
21 | 
22 |     # Bootup
23 |     handlers = [
24 |     (r"/api/servers", ServerListController),
25 |     (r"/api/info", InfoController),
26 |     (r"/api/memory", MemoryController),
27 |     (r"/api/commands", CommandsController),
28 |     (r"/api/topcommands", TopCommandsController),
29 |     (r"/api/topkeys", TopKeysController),
30 |     (r"/(.*)", BaseStaticFileHandler, {"path": "www", "default_filename": "index.html"})
31 |     ]
32 | 
33 |     server_settings = {'debug': options.debug}
34 |     application = tornado.web.Application(handlers, **server_settings)
35 |     application.listen(options.port)
36 |     tornado.ioloop.IOLoop.instance().start()
37 | 


--------------------------------------------------------------------------------
/src/www/js/libs/bootstrap/js/tests/unit/bootstrap-alert.js:
--------------------------------------------------------------------------------
 1 | $(function () {
 2 | 
 3 |     module("bootstrap-alerts")
 4 | 
 5 |       test("should be defined on jquery object", function () {
 6 |         ok($(document.body).alert, 'alert method is defined')
 7 |       })
 8 | 
 9 |       test("should return element", function () {
10 |         ok($(document.body).alert()[0] == document.body, 'document.body returned')
11 |       })
12 | 
13 |       test("should fade element out on clicking .close", function () {
14 |         var alertHTML = '
' 15 | + '×' 16 | + '

Holy guacamole! Best check yo self, you\'re not looking too good.

' 17 | + '
' 18 | , alert = $(alertHTML).alert() 19 | 20 | alert.find('.close').click() 21 | 22 | ok(!alert.hasClass('in'), 'remove .in class on .close click') 23 | }) 24 | 25 | test("should remove element when clicking .close", function () { 26 | $.support.transition = false 27 | 28 | var alertHTML = '
' 29 | + '×' 30 | + '

Holy guacamole! Best check yo self, you\'re not looking too good.

' 31 | + '
' 32 | , alert = $(alertHTML).appendTo('#qunit-fixture').alert() 33 | 34 | ok($('#qunit-fixture').find('.alert-message').length, 'element added to dom') 35 | 36 | alert.find('.close').click() 37 | 38 | ok(!$('#qunit-fixture').find('.alert-message').length, 'element removed from dom') 39 | }) 40 | 41 | }) -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/tests/unit/bootstrap-tab.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-tabs") 4 | 5 | test("should be defined on jquery object", function () { 6 | ok($(document.body).tab, 'tabs method is defined') 7 | }) 8 | 9 | test("should return element", function () { 10 | ok($(document.body).tab()[0] == document.body, 'document.body returned') 11 | }) 12 | 13 | test("should activate element by tab id", function () { 14 | var tabsHTML = 15 | '' 19 | 20 | $('
').appendTo("#qunit-fixture") 21 | 22 | $(tabsHTML).find('li:last a').tab('show') 23 | equals($("#qunit-fixture").find('.active').attr('id'), "profile") 24 | 25 | $(tabsHTML).find('li:first a').tab('show') 26 | equals($("#qunit-fixture").find('.active').attr('id'), "home") 27 | }) 28 | 29 | test("should activate element by tab id", function () { 30 | var pillsHTML = 31 | '' 35 | 36 | $('
').appendTo("#qunit-fixture") 37 | 38 | $(pillsHTML).find('li:last a').tab('show') 39 | equals($("#qunit-fixture").find('.active').attr('id'), "profile") 40 | 41 | $(pillsHTML).find('li:first a').tab('show') 42 | equals($("#qunit-fixture").find('.active').attr('id'), "home") 43 | }) 44 | 45 | }) -------------------------------------------------------------------------------- /src/www/js/views/top-commands-widget-view.js: -------------------------------------------------------------------------------- 1 | var TopCommandsWidget = BaseWidget.extend({ 2 | 3 | initialize : function() { 4 | 5 | this.Name = "Top Commands Widget" 6 | 7 | this.init() 8 | 9 | // templates 10 | var templateSelector = "#top-commands-widget-template" 11 | , templateSource = $(templateSelector).html() 12 | 13 | this.template = Handlebars.compile(templateSource) 14 | this.$el.empty().html(this.template()) 15 | 16 | // chart 17 | this.chart = new google.visualization.ColumnChart($("#top-commands-widget-chart").empty().get(0)) 18 | this.dataTable = new google.visualization.DataTable() 19 | this.dataTable.addColumn('string', 'command') 20 | this.dataTable.addColumn('number', 'count') 21 | 22 | } 23 | 24 | , render : function() { 25 | 26 | var model = this.model.toJSON() 27 | , markUp = this.template(model) 28 | , self = this 29 | 30 | this.dataTable.removeRows(0, this.dataTable.getNumberOfRows()) 31 | 32 | this.dataTable.addRows(model.data) 33 | 34 | //https://developers.google.com/chart/interactive/docs/gallery/columnchart#Configuration_Options 35 | var options = { 36 | title : '' 37 | , colors : ['#006B9F', '#008FD5', '#454545', '#E70B20' ] 38 | , chartArea: { 'left' : 100, 'top' : 10, 'width': '90%', 'height': '200' } 39 | , height: 250 40 | , animation: { duration : 500, easing : 'linear' } 41 | , legend: { position: 'none' } 42 | } 43 | 44 | this.chart.draw(this.dataTable, options) 45 | } 46 | }) -------------------------------------------------------------------------------- /src/www/js/views/top-keys-widget-view.js: -------------------------------------------------------------------------------- 1 | var TopKeysWidget = BaseWidget.extend({ 2 | 3 | initialize : function() { 4 | 5 | this.Name = "Top Keys Widget" 6 | 7 | this.init() 8 | 9 | // templates 10 | var templateSelector = "#top-keys-widget-template" 11 | , templateSource = $(templateSelector).html() 12 | 13 | this.template = Handlebars.compile(templateSource) 14 | this.$el.empty().html(this.template()) 15 | 16 | // chart 17 | this.chart = new google.visualization.ColumnChart($("#top-keys-widget-chart").empty().get(0)) 18 | this.dataTable = new google.visualization.DataTable() 19 | 20 | } 21 | 22 | , render : function() { 23 | 24 | var model = this.model.toJSON() 25 | , markUp = this.template(model) 26 | , self = this 27 | 28 | this.dataTable.removeRows( 0, this.dataTable.getNumberOfRows() ) 29 | this.dataTable.removeColumns(0, this.dataTable.getNumberOfColumns()) 30 | 31 | this.dataTable.addColumn('string', 'key') 32 | this.dataTable.addColumn('number', 'count') 33 | this.dataTable.addRows(model.data) 34 | 35 | //https://developers.google.com/chart/interactive/docs/gallery/columnchart#Configuration_Options 36 | var options = { 37 | title : '' 38 | , colors : [ '#008FD5', '#006B9F', '#454545', '#E70B20' ] 39 | , chartArea: { 'left' : 100, 'top' : 10, 'width': '90%', 'height': '200' } 40 | , height : 250 41 | , animation: { duration : 500, easing : 'linear' } 42 | , legend: { position: 'none' } 43 | } 44 | 45 | this.chart.draw(this.dataTable, options) 46 | } 47 | }) -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/bootstrap.less: -------------------------------------------------------------------------------- 1 | 2 | /*! 3 | * Bootstrap v2.0.2 4 | * 5 | * Copyright 2012 Twitter, Inc 6 | * Licensed under the Apache License v2.0 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 10 | */ 11 | 12 | // CSS Reset 13 | @import "reset.less"; 14 | 15 | // Core variables and mixins 16 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc 17 | @import "mixins.less"; 18 | 19 | // Grid system and page structure 20 | @import "scaffolding.less"; 21 | @import "grid.less"; 22 | @import "layouts.less"; 23 | 24 | // Base CSS 25 | @import "type.less"; 26 | @import "code.less"; 27 | @import "forms.less"; 28 | @import "tables.less"; 29 | 30 | // Components: common 31 | @import "sprites.less"; 32 | @import "dropdowns.less"; 33 | @import "wells.less"; 34 | @import "component-animations.less"; 35 | @import "close.less"; 36 | 37 | // Components: Buttons & Alerts 38 | @import "buttons.less"; 39 | @import "button-groups.less"; 40 | @import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less 41 | 42 | // Components: Nav 43 | @import "navs.less"; 44 | @import "navbar.less"; 45 | @import "breadcrumbs.less"; 46 | @import "pagination.less"; 47 | @import "pager.less"; 48 | 49 | // Components: Popovers 50 | @import "modals.less"; 51 | @import "tooltip.less"; 52 | @import "popovers.less"; 53 | 54 | // Components: Misc 55 | @import "thumbnails.less"; 56 | @import "labels.less"; 57 | @import "badges.less"; 58 | @import "progress-bars.less"; 59 | @import "accordion.less"; 60 | @import "carousel.less"; 61 | @import "hero-unit.less"; 62 | 63 | // Utility classes 64 | @import "utilities.less"; // Has to be last to override when necessary 65 | 66 | 67 | // Custom 68 | @import "widget.less"; 69 | -------------------------------------------------------------------------------- /src/api/controller/CommandsController.py: -------------------------------------------------------------------------------- 1 | from BaseController import BaseController 2 | import tornado.ioloop 3 | import tornado.web 4 | import dateutil.parser 5 | from datetime import datetime, timedelta 6 | 7 | 8 | class CommandsController(BaseController): 9 | 10 | def get(self): 11 | """Serves a GET request. 12 | """ 13 | return_data = dict(data=[], timestamp=datetime.now().isoformat()) 14 | 15 | server = self.get_argument("server") 16 | from_date = self.get_argument("from", None) 17 | to_date = self.get_argument("to", None) 18 | 19 | if not from_date or not to_date: 20 | end = datetime.now() 21 | delta = timedelta(seconds=120) 22 | start = end - delta 23 | else: 24 | start = dateutil.parser.parse(from_date) 25 | end = dateutil.parser.parse(to_date) 26 | 27 | difference = end - start 28 | # added to support python version < 2.7, otherwise timedelta has 29 | # total_seconds() 30 | difference_total_seconds = difference.days * 24 * 3600 31 | difference_total_seconds += difference.seconds 32 | difference_total_seconds += difference.microseconds / 1e6 33 | 34 | minutes = difference_total_seconds / 60 35 | hours = minutes / 60 36 | seconds = difference_total_seconds 37 | 38 | if hours > 120: 39 | group_by = "day" 40 | elif minutes > 120: 41 | group_by = "hour" 42 | elif seconds > 120: 43 | group_by = "minute" 44 | else: 45 | group_by = "second" 46 | 47 | combined_data = [] 48 | stats = self.stats_provider.get_command_stats(server, start, end, 49 | group_by) 50 | for data in stats: 51 | combined_data.append([data[1], data[0]]) 52 | 53 | for data in combined_data: 54 | return_data['data'].append([self.datetime_to_list(data[0]), data[1]]) 55 | 56 | self.write(return_data) 57 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap Plugin Test Suite 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 |

Bootstrap Plugin Test Suite

43 |

44 |

45 |
    46 |
    47 |
    48 | 49 | -------------------------------------------------------------------------------- /src/www/js/app.js: -------------------------------------------------------------------------------- 1 | /* Main App 2 | * ====================== */ 3 | 4 | var App = { 5 | 6 | init: function() { 7 | 8 | this.RegisterPartials() 9 | this.RegisterHelpers() 10 | 11 | var ServerDropDown = new ServerList({ 12 | el : $("#server-list") 13 | , model : new ServerListModel() 14 | }) 15 | 16 | var infoWidget = new InfoWidget({ 17 | el : $("#info-widget-placeholder") 18 | , model : new InfoWidgetModel() 19 | }) 20 | 21 | var memoryWidget = new MemoryWidget({ 22 | el : $("#memory-widget-placeholder") 23 | , model : new MemoryWidgetModel() 24 | }) 25 | 26 | var commandsWidget = new CommandsWidget({ 27 | el : $("#commands-widget-placeholder") 28 | , model : new CommandsWidgetModel() 29 | }) 30 | 31 | var topCommandsWidget = new TopCommandsWidget({ 32 | el : $("#top-commands-widget-placeholder") 33 | , model : new TopCommandsWidgetModel() 34 | }) 35 | 36 | var topKeysWidget = new TopKeysWidget({ 37 | el : $("#top-keys-widget-placeholder") 38 | , model : new TopKeysWidgetModel() 39 | }) 40 | } 41 | 42 | , RegisterPartials : function(){ 43 | 44 | Handlebars.registerPartial("date-dropdown", $("#date-dropdown-template").html()); 45 | 46 | } 47 | 48 | , RegisterHelpers : function(){ 49 | 50 | Handlebars.registerHelper('hash', function ( context, options ) { 51 | 52 | var ret = "" 53 | , counter = 0 54 | 55 | $.each(context, function ( key, value ) { 56 | 57 | if (typeof value != "object") { 58 | obj = { "key" : key, "value" : value , "index" : counter++ } 59 | ret = ret + options.fn(obj) 60 | } 61 | 62 | }) 63 | 64 | return ret 65 | }) 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/www/js/views/memory-widget-view.js: -------------------------------------------------------------------------------- 1 | var MemoryWidget = BaseWidget.extend({ 2 | 3 | initialize : function() { 4 | 5 | this.Name = "Memory Widget" 6 | 7 | this.init() 8 | 9 | // templates 10 | var templateSelector = "#memory-widget-template" 11 | , templateSource = $(templateSelector).html() 12 | 13 | this.template = Handlebars.compile(templateSource) 14 | this.$el.empty().html(this.template()) 15 | 16 | // chart 17 | this.chart = new google.visualization.LineChart($("#memory-widget-chart").empty().get(0)) 18 | this.dataTable = new google.visualization.DataTable() 19 | this.dataTable.addColumn('datetime', 'datetime') 20 | this.dataTable.addColumn('number', 'Max') 21 | this.dataTable.addColumn('number', 'Current') 22 | } 23 | 24 | , render : function() { 25 | 26 | var model = this.model.toJSON() 27 | , markUp = this.template(model) 28 | , self = this 29 | 30 | self.dataTable.removeRows(0,self.dataTable.getNumberOfRows()) 31 | 32 | $.each(model.data, function(index, obj){ 33 | 34 | // first item of the object contains datetime info 35 | // [ YYYY, MM, DD, HH, MM, SS ] 36 | var recordDate = new Date(obj[0][0], obj[0][1]-1, obj[0][2], obj[0][3], obj[0][4], obj[0][5]) 37 | 38 | if(self.dataTable) 39 | self.dataTable.addRow( [recordDate, obj[1], obj[2]] ) 40 | }) 41 | 42 | var pointSize = model.data.length > 120 ? 1 : 5 43 | , options = { 44 | title : '' 45 | , colors: [ '#1581AA', '#77BA44' ] 46 | , pointSize: pointSize 47 | , chartArea: { 'top' : 10, 'width' : '85%' } 48 | , width : "100%" 49 | , height : 200 50 | , animation : { duration : 500, easing : 'out' } 51 | } 52 | 53 | this.chart.draw(this.dataTable, options) 54 | } 55 | }) -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/bootstrap-transition.js: -------------------------------------------------------------------------------- 1 | /* =================================================== 2 | * bootstrap-transition.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#transitions 4 | * =================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | !function( $ ) { 21 | 22 | $(function () { 23 | 24 | "use strict" 25 | 26 | /* CSS TRANSITION SUPPORT (https://gist.github.com/373874) 27 | * ======================================================= */ 28 | 29 | $.support.transition = (function () { 30 | var thisBody = document.body || document.documentElement 31 | , thisStyle = thisBody.style 32 | , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined 33 | 34 | return support && { 35 | end: (function () { 36 | var transitionEnd = "TransitionEnd" 37 | if ( $.browser.webkit ) { 38 | transitionEnd = "webkitTransitionEnd" 39 | } else if ( $.browser.mozilla ) { 40 | transitionEnd = "transitionend" 41 | } else if ( $.browser.opera ) { 42 | transitionEnd = "oTransitionEnd" 43 | } 44 | return transitionEnd 45 | }()) 46 | } 47 | })() 48 | 49 | }) 50 | 51 | }( window.jQuery ); -------------------------------------------------------------------------------- /src/www/js/views/commands-widget-view.js: -------------------------------------------------------------------------------- 1 | var CommandsWidget = BaseWidget.extend({ 2 | 3 | initialize : function() { 4 | 5 | this.Name = "Commands Widget" 6 | 7 | this.init() 8 | 9 | // templates 10 | var templateSelector = "#commands-widget-template" 11 | , templateSource = $(templateSelector).html() 12 | 13 | this.template = Handlebars.compile(templateSource) 14 | this.$el.empty().html(this.template()) 15 | 16 | // chart 17 | this.chart = new google.visualization.AreaChart($("#commands-widget-chart").empty().get(0)) 18 | this.dataTable = new google.visualization.DataTable() 19 | this.dataTable.addColumn('datetime', 'datetime') 20 | this.dataTable.addColumn('number', 'Commands Processed') 21 | } 22 | 23 | , render : function() { 24 | 25 | var model = this.model.toJSON() 26 | , markUp = this.template(model) 27 | , self = this 28 | 29 | self.dataTable.removeRows(0,self.dataTable.getNumberOfRows()) 30 | 31 | $.each(model.data, function(index, obj){ 32 | 33 | // first item of the object contains datetime info 34 | // [ YYYY, MM, DD, HH, MM, SS ] 35 | var recordDate = new Date(obj[0][0], obj[0][1]-1, obj[0][2], obj[0][3], obj[0][4], obj[0][5]) 36 | 37 | if(self.dataTable) 38 | self.dataTable.addRow( [recordDate, obj[1]] ) 39 | }) 40 | 41 | var pointSize = model.data.length > 120 ? 1 : 5 42 | , options = { 43 | title : '' 44 | , colors: [ '#17BECF', '#9EDAE5' ] 45 | , areaOpacity : .9 46 | , pointSize: pointSize 47 | , chartArea: { 'top' : 10, 'width' : '85%' } 48 | , width : "100%" 49 | , height : 200 50 | , animation : { duration : 500, easing: 'out' } 51 | , vAxis: { minValue : 0 } 52 | } 53 | 54 | this.chart.draw(this.dataTable, options) 55 | } 56 | }) -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/tests/unit/bootstrap-dropdown.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-dropdowns") 4 | 5 | test("should be defined on jquery object", function () { 6 | ok($(document.body).dropdown, 'dropdown method is defined') 7 | }) 8 | 9 | test("should return element", function () { 10 | ok($(document.body).dropdown()[0] == document.body, 'document.body returned') 11 | }) 12 | 13 | test("should add class open to menu if clicked", function () { 14 | var dropdownHTML = '' 25 | , dropdown = $(dropdownHTML).find('[data-toggle="dropdown"]').dropdown().click() 26 | 27 | ok(dropdown.parent('.dropdown').hasClass('open'), 'open class added on click') 28 | }) 29 | 30 | test("should remove open class if body clicked", function () { 31 | var dropdownHTML = '' 42 | , dropdown = $(dropdownHTML) 43 | .appendTo('#qunit-fixture') 44 | .find('[data-toggle="dropdown"]') 45 | .dropdown() 46 | .click() 47 | ok(dropdown.parent('.dropdown').hasClass('open'), 'open class added on click') 48 | $('body').click() 49 | ok(!dropdown.parent('.dropdown').hasClass('open'), 'open class removed') 50 | dropdown.remove() 51 | }) 52 | 53 | }) -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/modals.less: -------------------------------------------------------------------------------- 1 | // MODALS 2 | // ------ 3 | 4 | // Recalculate z-index where appropriate 5 | .modal-open { 6 | .dropdown-menu { z-index: @zindexDropdown + @zindexModal; } 7 | .dropdown.open { *z-index: @zindexDropdown + @zindexModal; } 8 | .popover { z-index: @zindexPopover + @zindexModal; } 9 | .tooltip { z-index: @zindexTooltip + @zindexModal; } 10 | } 11 | 12 | // Background 13 | .modal-backdrop { 14 | position: fixed; 15 | top: 0; 16 | right: 0; 17 | bottom: 0; 18 | left: 0; 19 | z-index: @zindexModalBackdrop; 20 | background-color: @black; 21 | // Fade for backdrop 22 | &.fade { opacity: 0; } 23 | } 24 | 25 | .modal-backdrop, 26 | .modal-backdrop.fade.in { 27 | .opacity(80); 28 | } 29 | 30 | // Base modal 31 | .modal { 32 | position: fixed; 33 | top: 50%; 34 | left: 50%; 35 | z-index: @zindexModal; 36 | overflow: auto; 37 | width: 560px; 38 | margin: -250px 0 0 -280px; 39 | background-color: @white; 40 | border: 1px solid #999; 41 | border: 1px solid rgba(0,0,0,.3); 42 | *border: 1px solid #999; /* IE6-7 */ 43 | .border-radius(6px); 44 | .box-shadow(0 3px 7px rgba(0,0,0,0.3)); 45 | .background-clip(padding-box); 46 | &.fade { 47 | .transition(e('opacity .3s linear, top .3s ease-out')); 48 | top: -25%; 49 | } 50 | &.fade.in { top: 50%; } 51 | } 52 | .modal-header { 53 | padding: 9px 15px; 54 | border-bottom: 1px solid #eee; 55 | // Close icon 56 | .close { margin-top: 2px; } 57 | } 58 | 59 | // Body (where all modal content resises) 60 | .modal-body { 61 | overflow-y: auto; 62 | max-height: 400px; 63 | padding: 15px; 64 | } 65 | // Remove bottom margin if need be 66 | .modal-form { 67 | margin-bottom: 0; 68 | } 69 | 70 | // Footer (for actions) 71 | .modal-footer { 72 | padding: 14px 15px 15px; 73 | margin-bottom: 0; 74 | text-align: right; // right align buttons 75 | background-color: #f5f5f5; 76 | border-top: 1px solid #ddd; 77 | .border-radius(0 0 6px 6px); 78 | .box-shadow(inset 0 1px 0 @white); 79 | .clearfix(); // clear it in case folks use .pull-* classes on buttons 80 | 81 | // Properly space out buttons 82 | .btn + .btn { 83 | margin-left: 5px; 84 | margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs 85 | } 86 | // but override that for button groups 87 | .btn-group .btn + .btn { 88 | margin-left: -1px; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/carousel.less: -------------------------------------------------------------------------------- 1 | // CAROUSEL 2 | // -------- 3 | 4 | .carousel { 5 | position: relative; 6 | margin-bottom: @baseLineHeight; 7 | line-height: 1; 8 | } 9 | 10 | .carousel-inner { 11 | overflow: hidden; 12 | width: 100%; 13 | position: relative; 14 | } 15 | 16 | .carousel { 17 | 18 | .item { 19 | display: none; 20 | position: relative; 21 | .transition(.6s ease-in-out left); 22 | } 23 | 24 | // Account for jankitude on images 25 | .item > img { 26 | display: block; 27 | line-height: 1; 28 | } 29 | 30 | .active, 31 | .next, 32 | .prev { display: block; } 33 | 34 | .active { 35 | left: 0; 36 | } 37 | 38 | .next, 39 | .prev { 40 | position: absolute; 41 | top: 0; 42 | width: 100%; 43 | } 44 | 45 | .next { 46 | left: 100%; 47 | } 48 | .prev { 49 | left: -100%; 50 | } 51 | .next.left, 52 | .prev.right { 53 | left: 0; 54 | } 55 | 56 | .active.left { 57 | left: -100%; 58 | } 59 | .active.right { 60 | left: 100%; 61 | } 62 | 63 | } 64 | 65 | // Left/right controls for nav 66 | // --------------------------- 67 | 68 | .carousel-control { 69 | position: absolute; 70 | top: 40%; 71 | left: 15px; 72 | width: 40px; 73 | height: 40px; 74 | margin-top: -20px; 75 | font-size: 60px; 76 | font-weight: 100; 77 | line-height: 30px; 78 | color: @white; 79 | text-align: center; 80 | background: @grayDarker; 81 | border: 3px solid @white; 82 | .border-radius(23px); 83 | .opacity(50); 84 | 85 | // we can't have this transition here 86 | // because webkit cancels the carousel 87 | // animation if you trip this while 88 | // in the middle of another animation 89 | // ;_; 90 | // .transition(opacity .2s linear); 91 | 92 | // Reposition the right one 93 | &.right { 94 | left: auto; 95 | right: 15px; 96 | } 97 | 98 | // Hover state 99 | &:hover { 100 | color: @white; 101 | text-decoration: none; 102 | .opacity(90); 103 | } 104 | } 105 | 106 | // Caption for text below images 107 | // ----------------------------- 108 | 109 | .carousel-caption { 110 | position: absolute; 111 | left: 0; 112 | right: 0; 113 | bottom: 0; 114 | padding: 10px 15px 5px; 115 | background: @grayDark; 116 | background: rgba(0,0,0,.75); 117 | } 118 | .carousel-caption h4, 119 | .carousel-caption p { 120 | color: @white; 121 | } 122 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/tests/unit/bootstrap-tooltip.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-tooltip") 4 | 5 | test("should be defined on jquery object", function () { 6 | var div = $("
    ") 7 | ok(div.tooltip, 'popover method is defined') 8 | }) 9 | 10 | test("should return element", function () { 11 | var div = $("
    ") 12 | ok(div.tooltip() == div, 'document.body returned') 13 | }) 14 | 15 | test("should expose default settings", function () { 16 | ok(!!$.fn.tooltip.defaults, 'defaults is defined') 17 | }) 18 | 19 | test("should remove title attribute", function () { 20 | var tooltip = $('').tooltip() 21 | ok(!tooltip.attr('title'), 'title tag was removed') 22 | }) 23 | 24 | test("should add data attribute for referencing original title", function () { 25 | var tooltip = $('').tooltip() 26 | equals(tooltip.attr('data-original-title'), 'Another tooltip', 'original title preserved in data attribute') 27 | }) 28 | 29 | test("should place tooltips relative to placement option", function () { 30 | $.support.transition = false 31 | var tooltip = $('') 32 | .appendTo('#qunit-fixture') 33 | .tooltip({placement: 'bottom'}) 34 | .tooltip('show') 35 | 36 | ok($(".tooltip").hasClass('fade bottom in'), 'has correct classes applied') 37 | tooltip.tooltip('hide') 38 | }) 39 | 40 | test("should always allow html entities", function () { 41 | $.support.transition = false 42 | var tooltip = $('') 43 | .appendTo('#qunit-fixture') 44 | .tooltip('show') 45 | 46 | ok($('.tooltip b').length, 'b tag was inserted') 47 | tooltip.tooltip('hide') 48 | ok(!$(".tooltip").length, 'tooltip removed') 49 | }) 50 | 51 | test("should respect custom classes", function () { 52 | var tooltip = $('') 53 | .appendTo('#qunit-fixture') 54 | .tooltip({ template: '
    '}) 55 | .tooltip('show') 56 | 57 | ok($('.tooltip').hasClass('some-class'), 'custom class is present') 58 | tooltip.tooltip('hide') 59 | ok(!$(".tooltip").length, 'tooltip removed') 60 | }) 61 | 62 | }) -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/progress-bars.less: -------------------------------------------------------------------------------- 1 | // PROGRESS BARS 2 | // ------------- 3 | 4 | 5 | // ANIMATIONS 6 | // ---------- 7 | 8 | // Webkit 9 | @-webkit-keyframes progress-bar-stripes { 10 | from { background-position: 0 0; } 11 | to { background-position: 40px 0; } 12 | } 13 | 14 | // Firefox 15 | @-moz-keyframes progress-bar-stripes { 16 | from { background-position: 0 0; } 17 | to { background-position: 40px 0; } 18 | } 19 | 20 | // IE9 21 | @-ms-keyframes progress-bar-stripes { 22 | from { background-position: 0 0; } 23 | to { background-position: 40px 0; } 24 | } 25 | 26 | // Spec 27 | @keyframes progress-bar-stripes { 28 | from { background-position: 0 0; } 29 | to { background-position: 40px 0; } 30 | } 31 | 32 | 33 | 34 | // THE BARS 35 | // -------- 36 | 37 | // Outer container 38 | .progress { 39 | overflow: hidden; 40 | height: 18px; 41 | margin-bottom: 18px; 42 | #gradient > .vertical(#f5f5f5, #f9f9f9); 43 | .box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); 44 | .border-radius(4px); 45 | } 46 | 47 | // Bar of progress 48 | .progress .bar { 49 | width: 0%; 50 | height: 18px; 51 | color: @white; 52 | font-size: 12px; 53 | text-align: center; 54 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 55 | #gradient > .vertical(#149bdf, #0480be); 56 | .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); 57 | .box-sizing(border-box); 58 | .transition(width .6s ease); 59 | } 60 | 61 | // Striped bars 62 | .progress-striped .bar { 63 | #gradient > .striped(#149bdf); 64 | .background-size(40px 40px); 65 | } 66 | 67 | // Call animation for the active one 68 | .progress.active .bar { 69 | -webkit-animation: progress-bar-stripes 2s linear infinite; 70 | -moz-animation: progress-bar-stripes 2s linear infinite; 71 | animation: progress-bar-stripes 2s linear infinite; 72 | } 73 | 74 | 75 | 76 | // COLORS 77 | // ------ 78 | 79 | // Danger (red) 80 | .progress-danger .bar { 81 | #gradient > .vertical(#ee5f5b, #c43c35); 82 | } 83 | .progress-danger.progress-striped .bar { 84 | #gradient > .striped(#ee5f5b); 85 | } 86 | 87 | // Success (green) 88 | .progress-success .bar { 89 | #gradient > .vertical(#62c462, #57a957); 90 | } 91 | .progress-success.progress-striped .bar { 92 | #gradient > .striped(#62c462); 93 | } 94 | 95 | // Info (teal) 96 | .progress-info .bar { 97 | #gradient > .vertical(#5bc0de, #339bb9); 98 | } 99 | .progress-info.progress-striped .bar { 100 | #gradient > .striped(#5bc0de); 101 | } 102 | 103 | // Warning (orange) 104 | .progress-warning .bar { 105 | #gradient > .vertical(lighten(@orange, 15%), @orange); 106 | } 107 | .progress-warning.progress-striped .bar { 108 | #gradient > .striped(lighten(@orange, 15%)); 109 | } 110 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/bootstrap-alert.js: -------------------------------------------------------------------------------- 1 | /* ========================================================== 2 | * bootstrap-alert.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#alerts 4 | * ========================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function( $ ){ 22 | 23 | "use strict" 24 | 25 | /* ALERT CLASS DEFINITION 26 | * ====================== */ 27 | 28 | var dismiss = '[data-dismiss="alert"]' 29 | , Alert = function ( el ) { 30 | $(el).on('click', dismiss, this.close) 31 | } 32 | 33 | Alert.prototype = { 34 | 35 | constructor: Alert 36 | 37 | , close: function ( e ) { 38 | var $this = $(this) 39 | , selector = $this.attr('data-target') 40 | , $parent 41 | 42 | if (!selector) { 43 | selector = $this.attr('href') 44 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 45 | } 46 | 47 | $parent = $(selector) 48 | $parent.trigger('close') 49 | 50 | e && e.preventDefault() 51 | 52 | $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) 53 | 54 | $parent 55 | .trigger('close') 56 | .removeClass('in') 57 | 58 | function removeElement() { 59 | $parent 60 | .trigger('closed') 61 | .remove() 62 | } 63 | 64 | $.support.transition && $parent.hasClass('fade') ? 65 | $parent.on($.support.transition.end, removeElement) : 66 | removeElement() 67 | } 68 | 69 | } 70 | 71 | 72 | /* ALERT PLUGIN DEFINITION 73 | * ======================= */ 74 | 75 | $.fn.alert = function ( option ) { 76 | return this.each(function () { 77 | var $this = $(this) 78 | , data = $this.data('alert') 79 | if (!data) $this.data('alert', (data = new Alert(this))) 80 | if (typeof option == 'string') data[option].call($this) 81 | }) 82 | } 83 | 84 | $.fn.alert.Constructor = Alert 85 | 86 | 87 | /* ALERT DATA-API 88 | * ============== */ 89 | 90 | $(function () { 91 | $('body').on('click.alert.data-api', dismiss, Alert.prototype.close) 92 | }) 93 | 94 | }( window.jQuery ); -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/bootstrap-dropdown.js: -------------------------------------------------------------------------------- 1 | /* ============================================================ 2 | * bootstrap-dropdown.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#dropdowns 4 | * ============================================================ 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================ */ 19 | 20 | 21 | !function( $ ){ 22 | 23 | "use strict" 24 | 25 | /* DROPDOWN CLASS DEFINITION 26 | * ========================= */ 27 | 28 | var toggle = '[data-toggle="dropdown"]' 29 | , Dropdown = function ( element ) { 30 | var $el = $(element).on('click.dropdown.data-api', this.toggle) 31 | $('html').on('click.dropdown.data-api', function () { 32 | $el.parent().removeClass('open') 33 | }) 34 | } 35 | 36 | Dropdown.prototype = { 37 | 38 | constructor: Dropdown 39 | 40 | , toggle: function ( e ) { 41 | var $this = $(this) 42 | , selector = $this.attr('data-target') 43 | , $parent 44 | , isActive 45 | 46 | if (!selector) { 47 | selector = $this.attr('href') 48 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 49 | } 50 | 51 | $parent = $(selector) 52 | $parent.length || ($parent = $this.parent()) 53 | 54 | isActive = $parent.hasClass('open') 55 | 56 | clearMenus() 57 | !isActive && $parent.toggleClass('open') 58 | 59 | return false 60 | } 61 | 62 | } 63 | 64 | function clearMenus() { 65 | $(toggle).parent().removeClass('open') 66 | } 67 | 68 | 69 | /* DROPDOWN PLUGIN DEFINITION 70 | * ========================== */ 71 | 72 | $.fn.dropdown = function ( option ) { 73 | return this.each(function () { 74 | var $this = $(this) 75 | , data = $this.data('dropdown') 76 | if (!data) $this.data('dropdown', (data = new Dropdown(this))) 77 | if (typeof option == 'string') data[option].call($this) 78 | }) 79 | } 80 | 81 | $.fn.dropdown.Constructor = Dropdown 82 | 83 | 84 | /* APPLY TO STANDARD DROPDOWN ELEMENTS 85 | * =================================== */ 86 | 87 | $(function () { 88 | $('html').on('click.dropdown.data-api', clearMenus) 89 | $('body').on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle) 90 | }) 91 | 92 | }( window.jQuery ); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RedisLive 2 | --------- 3 | original project and loading address: https://github.com/nkrode/RedisLive 4 | 5 | Visualize your redis instances, analyze query patterns and spikes. 6 | 7 | ![Redis Live](https://github.com/kumarnitin/RedisLive/blob/master/design/redis-live.png?raw=true "Redis Live") 8 | 9 | Setup Instructions 10 | ------------------ 11 | #### Installation 12 | 13 | Install Dependencies 14 | 15 | + [tornado](https://github.com/facebook/tornado) `pip install tornado` 16 | + [redis.py](https://github.com/andymccurdy/redis-py) `pip install redis` 17 | + [python-dateutil](http://labix.org/python-dateutil) `pip install python-dateutil` 18 | 19 | You'll also need argparse if you're running Python < 2.7: 20 | 21 | + [argparse](http://code.google.com/p/argparse/) `pip install argparse` 22 | 23 | 24 | Get RedisLive 25 | 26 | + Clone the repo `git clone https://github.com/kumarnitin/RedisLive.git` , or [download the latest release](https://github.com/kumarnitin/RedisLive/zipball/master) 27 | 28 | #### Configuration 29 | 30 | + edit redis-live.conf : 31 | + update the value of the key `RedisServers` to the redis instances you want to monitor. You can monitor multiple instances by appending more values to the RedisServers list. 32 | + update the value of the key `RedisStatsServer` to the redis instance you will use to store RedisLive data (this redis instance is different from the redis instances you are monitoring). 33 | + passwords can be added as an optional parameter for any redis instance 34 | 35 | if you don't have a spare redis instance to use to store Redis Live data, then you can configure to use sqlite by changing `"DataStoreType" : "sqlite"` 36 | 37 | #### Start RedisLive 38 | 39 | + start the monitoring script `./redis-monitor.py --duration=120` duration is in seconds (see caveat) 40 | + start the webserver `./redis-live.py` 41 | + RedisLive is now running @ `http://localhost:8888/index.html` 42 | 43 | 44 | #### Caveat on monitoring redis 45 | 46 | Currently the only hook into monitoring a redis instance is Redis [MONITOR](http://redis.io/commands/monitor) command, which streams back every command processed and reduces the throughput of the redis instance. It is recommended to run redis-monitor with --duration suitable for your redis deployment and scheduling it to run periodically as a cron job. 47 | 48 | 49 | Authors 50 | ------- 51 | 52 | **Nitin Kumar** 53 | 54 | + http://twitter.com/nkrode 55 | 56 | Contributors 57 | ------------ 58 | + [splee](https://github.com/splee) (Lee McFadden) 59 | + [bialecki](https://github.com/bialecki) (Andrew Bialecki) 60 | + [reustle](https://github.com/reustle) (Shane Reustle) 61 | + [markdube](https://github.com/markdube) (Mark Dube) 62 | + [skreuzer](https://github.com/skreuzer) (Steven Kreuzer) 63 | + [snikch](https://github.com/snikch) (Mal Curtis) 64 | + [quiver](https://github.com/quiver) (George) 65 | 66 | License 67 | ------- 68 | RedisLive is released under the MIT license: 69 | + www.opensource.org/licenses/MIT 70 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/reset.less: -------------------------------------------------------------------------------- 1 | // Reset.less 2 | // Adapted from Normalize.css http://github.com/necolas/normalize.css 3 | // ------------------------------------------------------------------------ 4 | 5 | // Display in IE6-9 and FF3 6 | // ------------------------- 7 | 8 | article, 9 | aside, 10 | details, 11 | figcaption, 12 | figure, 13 | footer, 14 | header, 15 | hgroup, 16 | nav, 17 | section { 18 | display: block; 19 | } 20 | 21 | // Display block in IE6-9 and FF3 22 | // ------------------------- 23 | 24 | audio, 25 | canvas, 26 | video { 27 | display: inline-block; 28 | *display: inline; 29 | *zoom: 1; 30 | } 31 | 32 | // Prevents modern browsers from displaying 'audio' without controls 33 | // ------------------------- 34 | 35 | audio:not([controls]) { 36 | display: none; 37 | } 38 | 39 | // Base settings 40 | // ------------------------- 41 | 42 | html { 43 | font-size: 100%; 44 | -webkit-text-size-adjust: 100%; 45 | -ms-text-size-adjust: 100%; 46 | } 47 | // Focus states 48 | a:focus { 49 | .tab-focus(); 50 | } 51 | // Hover & Active 52 | a:hover, 53 | a:active { 54 | outline: 0; 55 | } 56 | 57 | // Prevents sub and sup affecting line-height in all browsers 58 | // ------------------------- 59 | 60 | sub, 61 | sup { 62 | position: relative; 63 | font-size: 75%; 64 | line-height: 0; 65 | vertical-align: baseline; 66 | } 67 | sup { 68 | top: -0.5em; 69 | } 70 | sub { 71 | bottom: -0.25em; 72 | } 73 | 74 | // Img border in a's and image quality 75 | // ------------------------- 76 | 77 | img { 78 | height: auto; 79 | border: 0; 80 | -ms-interpolation-mode: bicubic; 81 | vertical-align: middle; 82 | } 83 | 84 | // Forms 85 | // ------------------------- 86 | 87 | // Font size in all browsers, margin changes, misc consistency 88 | button, 89 | input, 90 | select, 91 | textarea { 92 | margin: 0; 93 | font-size: 100%; 94 | vertical-align: middle; 95 | } 96 | button, 97 | input { 98 | *overflow: visible; // Inner spacing ie IE6/7 99 | line-height: normal; // FF3/4 have !important on line-height in UA stylesheet 100 | } 101 | button::-moz-focus-inner, 102 | input::-moz-focus-inner { // Inner padding and border oddities in FF3/4 103 | padding: 0; 104 | border: 0; 105 | } 106 | button, 107 | input[type="button"], 108 | input[type="reset"], 109 | input[type="submit"] { 110 | cursor: pointer; // Cursors on all buttons applied consistently 111 | -webkit-appearance: button; // Style clickable inputs in iOS 112 | } 113 | input[type="search"] { // Appearance in Safari/Chrome 114 | -webkit-appearance: textfield; 115 | -webkit-box-sizing: content-box; 116 | -moz-box-sizing: content-box; 117 | box-sizing: content-box; 118 | } 119 | input[type="search"]::-webkit-search-decoration, 120 | input[type="search"]::-webkit-search-cancel-button { 121 | -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5 122 | } 123 | textarea { 124 | overflow: auto; // Remove vertical scrollbar in IE6-9 125 | vertical-align: top; // Readability and alignment cross-browser 126 | } 127 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/tests/unit/bootstrap-modal.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-modal") 4 | 5 | test("should be defined on jquery object", function () { 6 | var div = $("") 7 | ok(div.modal, 'modal method is defined') 8 | }) 9 | 10 | test("should return element", function () { 11 | var div = $("") 12 | ok(div.modal() == div, 'document.body returned') 13 | $('#modal-test').remove() 14 | }) 15 | 16 | test("should expose defaults var for settings", function () { 17 | ok($.fn.modal.defaults, 'default object exposed') 18 | }) 19 | 20 | test("should insert into dom when show method is called", function () { 21 | stop() 22 | $.support.transition = false 23 | $("") 24 | .bind("shown", function () { 25 | ok($('#modal-test').length, 'modal insterted into dom') 26 | $(this).remove() 27 | start() 28 | }) 29 | .modal("show") 30 | }) 31 | 32 | test("should hide modal when hide is called", function () { 33 | stop() 34 | $.support.transition = false 35 | 36 | $("") 37 | .bind("shown", function () { 38 | ok($('#modal-test').is(":visible"), 'modal visible') 39 | ok($('#modal-test').length, 'modal insterted into dom') 40 | $(this).modal("hide") 41 | }) 42 | .bind("hidden", function() { 43 | ok(!$('#modal-test').is(":visible"), 'modal hidden') 44 | $('#modal-test').remove() 45 | start() 46 | }) 47 | .modal("show") 48 | }) 49 | 50 | test("should toggle when toggle is called", function () { 51 | stop() 52 | $.support.transition = false 53 | var div = $("") 54 | div 55 | .bind("shown", function () { 56 | ok($('#modal-test').is(":visible"), 'modal visible') 57 | ok($('#modal-test').length, 'modal insterted into dom') 58 | div.modal("toggle") 59 | }) 60 | .bind("hidden", function() { 61 | ok(!$('#modal-test').is(":visible"), 'modal hidden') 62 | div.remove() 63 | start() 64 | }) 65 | .modal("toggle") 66 | }) 67 | 68 | test("should remove from dom when click [data-dismiss=modal]", function () { 69 | stop() 70 | $.support.transition = false 71 | var div = $("") 72 | div 73 | .bind("shown", function () { 74 | ok($('#modal-test').is(":visible"), 'modal visible') 75 | ok($('#modal-test').length, 'modal insterted into dom') 76 | div.find('.close').click() 77 | }) 78 | .bind("hidden", function() { 79 | ok(!$('#modal-test').is(":visible"), 'modal hidden') 80 | div.remove() 81 | start() 82 | }) 83 | .modal("toggle") 84 | }) 85 | }) -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/tests/unit/bootstrap-button.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-buttons") 4 | 5 | test("should be defined on jquery object", function () { 6 | ok($(document.body).button, 'button method is defined') 7 | }) 8 | 9 | test("should return element", function () { 10 | ok($(document.body).button()[0] == document.body, 'document.body returned') 11 | }) 12 | 13 | test("should return set state to loading", function () { 14 | var btn = $('') 15 | equals(btn.html(), 'mdo', 'btn text equals mdo') 16 | btn.button('loading') 17 | equals(btn.html(), 'fat', 'btn text equals fat') 18 | stop() 19 | setTimeout(function () { 20 | ok(btn.attr('disabled'), 'btn is disabled') 21 | ok(btn.hasClass('disabled'), 'btn has disabled class') 22 | start() 23 | }, 0) 24 | }) 25 | 26 | test("should return reset state", function () { 27 | var btn = $('') 28 | equals(btn.html(), 'mdo', 'btn text equals mdo') 29 | btn.button('loading') 30 | equals(btn.html(), 'fat', 'btn text equals fat') 31 | stop() 32 | setTimeout(function () { 33 | ok(btn.attr('disabled'), 'btn is disabled') 34 | ok(btn.hasClass('disabled'), 'btn has disabled class') 35 | start() 36 | stop() 37 | }, 0) 38 | btn.button('reset') 39 | equals(btn.html(), 'mdo', 'btn text equals mdo') 40 | setTimeout(function () { 41 | ok(!btn.attr('disabled'), 'btn is not disabled') 42 | ok(!btn.hasClass('disabled'), 'btn does not have disabled class') 43 | start() 44 | }, 0) 45 | }) 46 | 47 | test("should toggle active", function () { 48 | var btn = $('') 49 | ok(!btn.hasClass('active'), 'btn does not have active class') 50 | btn.button('toggle') 51 | ok(btn.hasClass('active'), 'btn has class active') 52 | }) 53 | 54 | test("should toggle active when btn children are clicked", function () { 55 | var btn = $('') 56 | , inner = $('') 57 | btn 58 | .append(inner) 59 | .appendTo($('#qunit-fixture')) 60 | ok(!btn.hasClass('active'), 'btn does not have active class') 61 | inner.click() 62 | ok(btn.hasClass('active'), 'btn has class active') 63 | }) 64 | 65 | test("should toggle active when btn children are clicked within btn-group", function () { 66 | var btngroup = $('
    ') 67 | , btn = $('') 68 | , inner = $('') 69 | btngroup 70 | .append(btn.append(inner)) 71 | .appendTo($('#qunit-fixture')) 72 | ok(!btn.hasClass('active'), 'btn does not have active class') 73 | inner.click() 74 | ok(btn.hasClass('active'), 'btn has class active') 75 | }) 76 | 77 | }) -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/bootstrap-popover.js: -------------------------------------------------------------------------------- 1 | /* =========================================================== 2 | * bootstrap-popover.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#popovers 4 | * =========================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * =========================================================== */ 19 | 20 | 21 | !function( $ ) { 22 | 23 | "use strict" 24 | 25 | var Popover = function ( element, options ) { 26 | this.init('popover', element, options) 27 | } 28 | 29 | /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js 30 | ========================================== */ 31 | 32 | Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, { 33 | 34 | constructor: Popover 35 | 36 | , setContent: function () { 37 | var $tip = this.tip() 38 | , title = this.getTitle() 39 | , content = this.getContent() 40 | 41 | $tip.find('.popover-title')[ $.type(title) == 'object' ? 'append' : 'html' ](title) 42 | $tip.find('.popover-content > *')[ $.type(content) == 'object' ? 'append' : 'html' ](content) 43 | 44 | $tip.removeClass('fade top bottom left right in') 45 | } 46 | 47 | , hasContent: function () { 48 | return this.getTitle() || this.getContent() 49 | } 50 | 51 | , getContent: function () { 52 | var content 53 | , $e = this.$element 54 | , o = this.options 55 | 56 | content = $e.attr('data-content') 57 | || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content) 58 | 59 | content = content.toString().replace(/(^\s*|\s*$)/, "") 60 | 61 | return content 62 | } 63 | 64 | , tip: function() { 65 | if (!this.$tip) { 66 | this.$tip = $(this.options.template) 67 | } 68 | return this.$tip 69 | } 70 | 71 | }) 72 | 73 | 74 | /* POPOVER PLUGIN DEFINITION 75 | * ======================= */ 76 | 77 | $.fn.popover = function ( option ) { 78 | return this.each(function () { 79 | var $this = $(this) 80 | , data = $this.data('popover') 81 | , options = typeof option == 'object' && option 82 | if (!data) $this.data('popover', (data = new Popover(this, options))) 83 | if (typeof option == 'string') data[option]() 84 | }) 85 | } 86 | 87 | $.fn.popover.Constructor = Popover 88 | 89 | $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, { 90 | placement: 'right' 91 | , content: '' 92 | , template: '

    ' 93 | }) 94 | 95 | }( window.jQuery ); -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/bootstrap-button.js: -------------------------------------------------------------------------------- 1 | /* ============================================================ 2 | * bootstrap-button.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#buttons 4 | * ============================================================ 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================ */ 19 | 20 | !function( $ ){ 21 | 22 | "use strict" 23 | 24 | /* BUTTON PUBLIC CLASS DEFINITION 25 | * ============================== */ 26 | 27 | var Button = function ( element, options ) { 28 | this.$element = $(element) 29 | this.options = $.extend({}, $.fn.button.defaults, options) 30 | } 31 | 32 | Button.prototype = { 33 | 34 | constructor: Button 35 | 36 | , setState: function ( state ) { 37 | var d = 'disabled' 38 | , $el = this.$element 39 | , data = $el.data() 40 | , val = $el.is('input') ? 'val' : 'html' 41 | 42 | state = state + 'Text' 43 | data.resetText || $el.data('resetText', $el[val]()) 44 | 45 | $el[val](data[state] || this.options[state]) 46 | 47 | // push to event loop to allow forms to submit 48 | setTimeout(function () { 49 | state == 'loadingText' ? 50 | $el.addClass(d).attr(d, d) : 51 | $el.removeClass(d).removeAttr(d) 52 | }, 0) 53 | } 54 | 55 | , toggle: function () { 56 | var $parent = this.$element.parent('[data-toggle="buttons-radio"]') 57 | 58 | $parent && $parent 59 | .find('.active') 60 | .removeClass('active') 61 | 62 | this.$element.toggleClass('active') 63 | } 64 | 65 | } 66 | 67 | 68 | /* BUTTON PLUGIN DEFINITION 69 | * ======================== */ 70 | 71 | $.fn.button = function ( option ) { 72 | return this.each(function () { 73 | var $this = $(this) 74 | , data = $this.data('button') 75 | , options = typeof option == 'object' && option 76 | if (!data) $this.data('button', (data = new Button(this, options))) 77 | if (option == 'toggle') data.toggle() 78 | else if (option) data.setState(option) 79 | }) 80 | } 81 | 82 | $.fn.button.defaults = { 83 | loadingText: 'loading...' 84 | } 85 | 86 | $.fn.button.Constructor = Button 87 | 88 | 89 | /* BUTTON DATA-API 90 | * =============== */ 91 | 92 | $(function () { 93 | $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) { 94 | var $btn = $(e.target) 95 | if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') 96 | $btn.button('toggle') 97 | }) 98 | }) 99 | 100 | }( window.jQuery ); -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/README.md: -------------------------------------------------------------------------------- 1 | ## 2.0 BOOTSTRAP JS PHILOSOPHY 2 | These are the high-level design rules which guide the development of Bootstrap's plugin apis. 3 | 4 | --- 5 | 6 | ### DATA-ATTRIBUTE API 7 | 8 | We believe you should be able to use all plugins provided by Bootstrap purely through the markup API without writing a single line of javascript. 9 | 10 | We acknowledge that this isn't always the most performant and sometimes it may be desirable to turn this functionality off altogether. Therefore, as of 2.0 we provide the ability to disable the data attribute API by unbinding all events on the body namespaced with `'data-api'`. This looks like this: 11 | 12 | $('body').off('.data-api') 13 | 14 | To target a specific plugin, just include the plugins name as a namespace along with the data-api namespace like this: 15 | 16 | $('body').off('.alert.data-api') 17 | 18 | --- 19 | 20 | ### PROGRAMATIC API 21 | 22 | We also believe you should be able to use all plugins provided by Bootstrap purely through the JS API. 23 | 24 | All public APIs should be single, chainable methods, and return the collection acted upon. 25 | 26 | $(".btn.danger").button("toggle").addClass("fat") 27 | 28 | All methods should accept an optional options object, a string which targets a particular method, or null which initiates the default behavior: 29 | 30 | $("#myModal").modal() // initialized with defaults 31 | $("#myModal").modal({ keyboard: false }) // initialized with no keyboard 32 | $("#myModal").modal('show') // initializes and invokes show immediately afterqwe2 33 | 34 | --- 35 | 36 | ### OPTIONS 37 | 38 | Options should be sparse and add universal value. We should pick the right defaults. 39 | 40 | All plugins should have a default object which can be modified to affect all instances' default options. The defaults object should be available via `$.fn.plugin.defaults`. 41 | 42 | $.fn.modal.defaults = { … } 43 | 44 | An options definition should take the following form: 45 | 46 | *noun*: *adjective* - describes or modifies a quality of an instance 47 | 48 | examples: 49 | 50 | backdrop: true 51 | keyboard: false 52 | placement: 'top' 53 | 54 | --- 55 | 56 | ### EVENTS 57 | 58 | All events should have an infinitive and past participle form. The infinitive is fired just before an action takes place, the past participle on completion of the action. 59 | 60 | show | shown 61 | hide | hidden 62 | 63 | --- 64 | 65 | ### CONSTRUCTORS 66 | 67 | Each plugin should expose its raw constructor on a `Constructor` property -- accessed in the following way: 68 | 69 | 70 | $.fn.popover.Constructor 71 | 72 | --- 73 | 74 | ### DATA ACCESSOR 75 | 76 | Each plugin stores a copy of the invoked class on an object. This class instance can be accessed directly through jQuery's data API like this: 77 | 78 | $('[rel=popover]').data('popover') instanceof $.fn.popover.Constructor 79 | 80 | --- 81 | 82 | ### DATA ATTRIBUTES 83 | 84 | Data attributes should take the following form: 85 | 86 | - data-{{verb}}={{plugin}} - defines main interaction 87 | - data-target || href^=# - defined on "control" element (if element controls an element other than self) 88 | - data-{{noun}} - defines class instance options 89 | 90 | examples: 91 | 92 | // control other targets 93 | data-toggle="modal" data-target="#foo" 94 | data-toggle="collapse" data-target="#foo" data-parent="#bar" 95 | 96 | // defined on element they control 97 | data-spy="scroll" 98 | 99 | data-dismiss="modal" 100 | data-dismiss="alert" 101 | 102 | data-toggle="dropdown" 103 | 104 | data-toggle="button" 105 | data-toggle="buttons-checkbox" 106 | data-toggle="buttons-radio" -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/tests/unit/bootstrap-popover.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-popover") 4 | 5 | test("should be defined on jquery object", function () { 6 | var div = $('
    ') 7 | ok(div.popover, 'popover method is defined') 8 | }) 9 | 10 | test("should return element", function () { 11 | var div = $('
    ') 12 | ok(div.popover() == div, 'document.body returned') 13 | }) 14 | 15 | test("should render popover element", function () { 16 | $.support.transition = false 17 | var popover = $('@mdo') 18 | .appendTo('#qunit-fixture') 19 | .popover('show') 20 | 21 | ok($('.popover').length, 'popover was inserted') 22 | popover.popover('hide') 23 | ok(!$(".popover").length, 'popover removed') 24 | }) 25 | 26 | test("should store popover instance in popover data object", function () { 27 | $.support.transition = false 28 | var popover = $('@mdo') 29 | .popover() 30 | 31 | ok(!!popover.data('popover'), 'popover instance exists') 32 | }) 33 | 34 | test("should get title and content from options", function () { 35 | $.support.transition = false 36 | var popover = $('@fat') 37 | .appendTo('#qunit-fixture') 38 | .popover({ 39 | title: function () { 40 | return '@fat' 41 | } 42 | , content: function () { 43 | return 'loves writing tests (╯°□°)╯︵ ┻━┻' 44 | } 45 | }) 46 | 47 | popover.popover('show') 48 | 49 | ok($('.popover').length, 'popover was inserted') 50 | equals($('.popover .popover-title').text(), '@fat', 'title correctly inserted') 51 | equals($('.popover .popover-content').text(), 'loves writing tests (╯°□°)╯︵ ┻━┻', 'content correctly inserted') 52 | 53 | popover.popover('hide') 54 | ok(!$('.popover').length, 'popover was removed') 55 | $('#qunit-fixture').empty() 56 | }) 57 | 58 | test("should get title and content from attributes", function () { 59 | $.support.transition = false 60 | var popover = $('@mdo') 61 | .appendTo('#qunit-fixture') 62 | .popover() 63 | .popover('show') 64 | 65 | ok($('.popover').length, 'popover was inserted') 66 | equals($('.popover .popover-title').text(), '@mdo', 'title correctly inserted') 67 | equals($('.popover .popover-content').text(), "loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻", 'content correctly inserted') 68 | 69 | popover.popover('hide') 70 | ok(!$('.popover').length, 'popover was removed') 71 | $('#qunit-fixture').empty() 72 | }) 73 | 74 | test("should respect custom classes", function() { 75 | $.support.transition = false 76 | var popover = $('@fat') 77 | .appendTo('#qunit-fixture') 78 | .popover({ 79 | title: 'Test' 80 | , content: 'Test' 81 | , template: '

    ' 82 | }) 83 | 84 | popover.popover('show') 85 | 86 | ok($('.popover').length, 'popover was inserted') 87 | ok($('.popover').hasClass('foobar'), 'custom class is present') 88 | 89 | popover.popover('hide') 90 | ok(!$('.popover').length, 'popover was removed') 91 | $('#qunit-fixture').empty() 92 | }) 93 | }) -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/bootstrap-tab.js: -------------------------------------------------------------------------------- 1 | /* ======================================================== 2 | * bootstrap-tab.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#tabs 4 | * ======================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ======================================================== */ 19 | 20 | 21 | !function( $ ){ 22 | 23 | "use strict" 24 | 25 | /* TAB CLASS DEFINITION 26 | * ==================== */ 27 | 28 | var Tab = function ( element ) { 29 | this.element = $(element) 30 | } 31 | 32 | Tab.prototype = { 33 | 34 | constructor: Tab 35 | 36 | , show: function () { 37 | var $this = this.element 38 | , $ul = $this.closest('ul:not(.dropdown-menu)') 39 | , selector = $this.attr('data-target') 40 | , previous 41 | , $target 42 | 43 | if (!selector) { 44 | selector = $this.attr('href') 45 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 46 | } 47 | 48 | if ( $this.parent('li').hasClass('active') ) return 49 | 50 | previous = $ul.find('.active a').last()[0] 51 | 52 | $this.trigger({ 53 | type: 'show' 54 | , relatedTarget: previous 55 | }) 56 | 57 | $target = $(selector) 58 | 59 | this.activate($this.parent('li'), $ul) 60 | this.activate($target, $target.parent(), function () { 61 | $this.trigger({ 62 | type: 'shown' 63 | , relatedTarget: previous 64 | }) 65 | }) 66 | } 67 | 68 | , activate: function ( element, container, callback) { 69 | var $active = container.find('> .active') 70 | , transition = callback 71 | && $.support.transition 72 | && $active.hasClass('fade') 73 | 74 | function next() { 75 | $active 76 | .removeClass('active') 77 | .find('> .dropdown-menu > .active') 78 | .removeClass('active') 79 | 80 | element.addClass('active') 81 | 82 | if (transition) { 83 | element[0].offsetWidth // reflow for transition 84 | element.addClass('in') 85 | } else { 86 | element.removeClass('fade') 87 | } 88 | 89 | if ( element.parent('.dropdown-menu') ) { 90 | element.closest('li.dropdown').addClass('active') 91 | } 92 | 93 | callback && callback() 94 | } 95 | 96 | transition ? 97 | $active.one($.support.transition.end, next) : 98 | next() 99 | 100 | $active.removeClass('in') 101 | } 102 | } 103 | 104 | 105 | /* TAB PLUGIN DEFINITION 106 | * ===================== */ 107 | 108 | $.fn.tab = function ( option ) { 109 | return this.each(function () { 110 | var $this = $(this) 111 | , data = $this.data('tab') 112 | if (!data) $this.data('tab', (data = new Tab(this))) 113 | if (typeof option == 'string') data[option]() 114 | }) 115 | } 116 | 117 | $.fn.tab.Constructor = Tab 118 | 119 | 120 | /* TAB DATA-API 121 | * ============ */ 122 | 123 | $(function () { 124 | $('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) { 125 | e.preventDefault() 126 | $(this).tab('show') 127 | }) 128 | }) 129 | 130 | }( window.jQuery ); -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/dropdowns.less: -------------------------------------------------------------------------------- 1 | // DROPDOWN MENUS 2 | // -------------- 3 | 4 | // Use the .menu class on any
  1. element within the topbar or ul.tabs and you'll get some superfancy dropdowns 5 | .dropdown { 6 | position: relative; 7 | } 8 | .dropdown-toggle { 9 | // The caret makes the toggle a bit too tall in IE7 10 | *margin-bottom: -3px; 11 | } 12 | .dropdown-toggle:active, 13 | .open .dropdown-toggle { 14 | outline: 0; 15 | } 16 | 17 | // Dropdown arrow/caret 18 | // -------------------- 19 | .caret { 20 | display: inline-block; 21 | width: 0; 22 | height: 0; 23 | vertical-align: top; 24 | border-left: 4px solid transparent; 25 | border-right: 4px solid transparent; 26 | border-top: 4px solid @black; 27 | .opacity(30); 28 | content: ""; 29 | } 30 | 31 | // Place the caret 32 | .dropdown .caret { 33 | margin-top: 8px; 34 | margin-left: 2px; 35 | } 36 | .dropdown:hover .caret, 37 | .open.dropdown .caret { 38 | .opacity(100); 39 | } 40 | 41 | // The dropdown menu (ul) 42 | // ---------------------- 43 | .dropdown-menu { 44 | position: absolute; 45 | top: 100%; 46 | left: 0; 47 | z-index: @zindexDropdown; 48 | float: left; 49 | display: none; // none by default, but block on "open" of the menu 50 | min-width: 160px; 51 | padding: 4px 0; 52 | margin: 0; // override default ul 53 | list-style: none; 54 | background-color: @dropdownBackground; 55 | border-color: #ccc; 56 | border-color: rgba(0,0,0,.2); 57 | border-style: solid; 58 | border-width: 1px; 59 | .border-radius(0 0 5px 5px); 60 | .box-shadow(0 5px 10px rgba(0,0,0,.2)); 61 | -webkit-background-clip: padding-box; 62 | -moz-background-clip: padding; 63 | background-clip: padding-box; 64 | *border-right-width: 2px; 65 | *border-bottom-width: 2px; 66 | 67 | // Aligns the dropdown menu to right 68 | &.pull-right { 69 | right: 0; 70 | left: auto; 71 | } 72 | 73 | // Dividers (basically an hr) within the dropdown 74 | .divider { 75 | .nav-divider(); 76 | } 77 | 78 | // Links within the dropdown menu 79 | a { 80 | display: block; 81 | padding: 3px 15px; 82 | clear: both; 83 | font-weight: normal; 84 | line-height: @baseLineHeight; 85 | color: @dropdownLinkColor; 86 | white-space: nowrap; 87 | } 88 | } 89 | 90 | // Hover state 91 | // ----------- 92 | .dropdown-menu li > a:hover, 93 | .dropdown-menu .active > a, 94 | .dropdown-menu .active > a:hover { 95 | color: @dropdownLinkColorHover; 96 | text-decoration: none; 97 | background-color: @dropdownLinkBackgroundHover; 98 | } 99 | 100 | // Open state for the dropdown 101 | // --------------------------- 102 | .dropdown.open { 103 | // IE7's z-index only goes to the nearest positioned ancestor, which would 104 | // make the menu appear below buttons that appeared later on the page 105 | *z-index: @zindexDropdown; 106 | 107 | .dropdown-toggle { 108 | color: @white; 109 | background: #ccc; 110 | background: rgba(0,0,0,.3); 111 | } 112 | .dropdown-menu { 113 | display: block; 114 | } 115 | } 116 | 117 | // Right aligned dropdowns 118 | .pull-right .dropdown-menu { 119 | left: auto; 120 | right: 0; 121 | } 122 | 123 | // Allow for dropdowns to go bottom up (aka, dropup-menu) 124 | // ------------------------------------------------------ 125 | // Just add .dropup after the standard .dropdown class and you're set, bro. 126 | // TODO: abstract this so that the navbar fixed styles are not placed here? 127 | .dropup, 128 | .navbar-fixed-bottom .dropdown { 129 | // Reverse the caret 130 | .caret { 131 | border-top: 0; 132 | border-bottom: 4px solid @black; 133 | content: "\2191"; 134 | } 135 | // Different positioning for bottom up menu 136 | .dropdown-menu { 137 | top: auto; 138 | bottom: 100%; 139 | margin-bottom: 1px; 140 | } 141 | } 142 | 143 | // Typeahead 144 | // --------- 145 | .typeahead { 146 | margin-top: 2px; // give it some space to breathe 147 | .border-radius(4px); 148 | } 149 | -------------------------------------------------------------------------------- /src/api/controller/InfoController.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | from BaseController import BaseController 3 | import tornado.ioloop 4 | import tornado.web 5 | import re 6 | 7 | 8 | class InfoController(BaseController): 9 | def get(self): 10 | """Serves a GET request. 11 | """ 12 | server = self.get_argument("server") 13 | redis_info = self.stats_provider.get_info(server) 14 | databases=[] 15 | 16 | for key in sorted(redis_info.keys()): 17 | if key.startswith("db"): 18 | database = redis_info[key] 19 | database['name']=key 20 | databases.append(database) 21 | 22 | total_keys=0 23 | for database in databases: 24 | total_keys+=database.get("keys") 25 | 26 | if(total_keys==0): 27 | databases=[{"name" : "db0", "keys" : "0", "expires" : "0"}] 28 | 29 | redis_info['databases'] = databases 30 | redis_info['total_keys']= self.shorten_number(total_keys) 31 | 32 | uptime_seconds = redis_info['uptime_in_seconds'] 33 | redis_info['uptime'] = self.shorten_time(uptime_seconds) 34 | 35 | commands_processed = redis_info['total_commands_processed'] 36 | commands_processed = self.shorten_number(commands_processed) 37 | redis_info['total_commands_processed_human'] = commands_processed 38 | 39 | self.write(redis_info) 40 | 41 | def shorten_time(self, seconds): 42 | """Takes an integer number of seconds and rounds it to a human readable 43 | format. 44 | 45 | Args: 46 | seconds (int): Number of seconds to convert. 47 | """ 48 | if seconds < 60: 49 | # less than 1 minute 50 | val = str(seconds) + " sec" 51 | elif seconds < 3600: 52 | # if the seconds is less than 1hr 53 | num = self.rounded_number(seconds, 60) 54 | if num == "60": 55 | val = '1h' 56 | else: 57 | val = num + "m" 58 | elif (seconds < 60*60*24): 59 | # if the number is less than 1 day 60 | num = self.rounded_number(seconds, 60 * 60) 61 | if num == "24": 62 | val = "1d" 63 | else: 64 | val = num + "h" 65 | else: 66 | num = self.rounded_number(seconds, 60*60*24) 67 | val = num + "d" 68 | 69 | return val 70 | 71 | def shorten_number(self, number): 72 | """Shortens a number to a human readable format. 73 | 74 | Args: 75 | number (int): Number to convert. 76 | """ 77 | if number < 1000: 78 | return number 79 | elif number >= 1000 and number < 1000000: 80 | num = self.rounded_number(number, 1000) 81 | val = "1M" if num == "1000" else num + "K" 82 | return val 83 | elif number >= 1000000 and number < 1000000000: 84 | num = self.rounded_number(number, 1000000) 85 | val = "1B" if num=="1000" else num + "M" 86 | return val 87 | elif number >= 1000000000 and number < 1000000000000: 88 | num = self.rounded_number(number, 1000000000) 89 | val = "1T" if num=="1000" else num + "B" 90 | return val 91 | else: 92 | num = self.rounded_number(number, 1000000000000) 93 | return num + "T" 94 | 95 | def rounded_number(self, number, denominator): 96 | """Rounds a number. 97 | 98 | Args: 99 | number (int|float): The number to round. 100 | denominator (int): The denominator. 101 | """ 102 | rounded = str(round(Decimal(number)/Decimal(denominator), 1)) 103 | replace_trailing_zero = re.compile('0$') 104 | no_trailing_zeros = replace_trailing_zero.sub('', rounded) 105 | replace_trailing_period = re.compile('\.$') 106 | final_number = replace_trailing_period.sub('', no_trailing_zeros) 107 | return final_number 108 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/bootstrap-scrollspy.js: -------------------------------------------------------------------------------- 1 | /* ============================================================= 2 | * bootstrap-scrollspy.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#scrollspy 4 | * ============================================================= 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================== */ 19 | 20 | !function ( $ ) { 21 | 22 | "use strict" 23 | 24 | /* SCROLLSPY CLASS DEFINITION 25 | * ========================== */ 26 | 27 | function ScrollSpy( element, options) { 28 | var process = $.proxy(this.process, this) 29 | , $element = $(element).is('body') ? $(window) : $(element) 30 | , href 31 | this.options = $.extend({}, $.fn.scrollspy.defaults, options) 32 | this.$scrollElement = $element.on('scroll.scroll.data-api', process) 33 | this.selector = (this.options.target 34 | || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 35 | || '') + ' .nav li > a' 36 | this.$body = $('body').on('click.scroll.data-api', this.selector, process) 37 | this.refresh() 38 | this.process() 39 | } 40 | 41 | ScrollSpy.prototype = { 42 | 43 | constructor: ScrollSpy 44 | 45 | , refresh: function () { 46 | this.targets = this.$body 47 | .find(this.selector) 48 | .map(function () { 49 | var href = $(this).attr('href') 50 | return /^#\w/.test(href) && $(href).length ? href : null 51 | }) 52 | 53 | this.offsets = $.map(this.targets, function (id) { 54 | return $(id).position().top 55 | }) 56 | } 57 | 58 | , process: function () { 59 | var scrollTop = this.$scrollElement.scrollTop() + this.options.offset 60 | , offsets = this.offsets 61 | , targets = this.targets 62 | , activeTarget = this.activeTarget 63 | , i 64 | 65 | for (i = offsets.length; i--;) { 66 | activeTarget != targets[i] 67 | && scrollTop >= offsets[i] 68 | && (!offsets[i + 1] || scrollTop <= offsets[i + 1]) 69 | && this.activate( targets[i] ) 70 | } 71 | } 72 | 73 | , activate: function (target) { 74 | var active 75 | 76 | this.activeTarget = target 77 | 78 | this.$body 79 | .find(this.selector).parent('.active') 80 | .removeClass('active') 81 | 82 | active = this.$body 83 | .find(this.selector + '[href="' + target + '"]') 84 | .parent('li') 85 | .addClass('active') 86 | 87 | if ( active.parent('.dropdown-menu') ) { 88 | active.closest('li.dropdown').addClass('active') 89 | } 90 | } 91 | 92 | } 93 | 94 | 95 | /* SCROLLSPY PLUGIN DEFINITION 96 | * =========================== */ 97 | 98 | $.fn.scrollspy = function ( option ) { 99 | return this.each(function () { 100 | var $this = $(this) 101 | , data = $this.data('scrollspy') 102 | , options = typeof option == 'object' && option 103 | if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options))) 104 | if (typeof option == 'string') data[option]() 105 | }) 106 | } 107 | 108 | $.fn.scrollspy.Constructor = ScrollSpy 109 | 110 | $.fn.scrollspy.defaults = { 111 | offset: 10 112 | } 113 | 114 | 115 | /* SCROLLSPY DATA-API 116 | * ================== */ 117 | 118 | $(function () { 119 | $('[data-spy="scroll"]').each(function () { 120 | var $spy = $(this) 121 | $spy.scrollspy($spy.data()) 122 | }) 123 | }) 124 | 125 | }( window.jQuery ); -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/tables.less: -------------------------------------------------------------------------------- 1 | // 2 | // Tables.less 3 | // Tables for, you guessed it, tabular data 4 | // ---------------------------------------- 5 | 6 | 7 | // BASE TABLES 8 | // ----------------- 9 | 10 | table { 11 | max-width: 100%; 12 | border-collapse: collapse; 13 | border-spacing: 0; 14 | background-color: @tableBackground; 15 | } 16 | 17 | // BASELINE STYLES 18 | // --------------- 19 | 20 | .table { 21 | width: 100%; 22 | //margin-bottom: @baseLineHeight; 23 | // Cells 24 | th, 25 | td { 26 | padding: 5px; 27 | line-height: @baseLineHeight; 28 | text-align: left; 29 | vertical-align: top; 30 | //border-top: 1px solid @tableBorder; 31 | } 32 | th { 33 | font-weight: bold; 34 | } 35 | // Bottom align for column headings 36 | thead th { 37 | vertical-align: bottom; 38 | } 39 | // Remove top border from thead by default 40 | colgroup + thead tr:first-child th, 41 | colgroup + thead tr:first-child td, 42 | thead:first-child tr:first-child th, 43 | thead:first-child tr:first-child td { 44 | border-top: 0; 45 | } 46 | // Account for multiple tbody instances 47 | tbody + tbody { 48 | border-top: 2px solid @tableBorder; 49 | } 50 | } 51 | 52 | 53 | 54 | // CONDENSED TABLE W/ HALF PADDING 55 | // ------------------------------- 56 | 57 | .table-condensed { 58 | th, 59 | td { 60 | padding: 4px 5px; 61 | } 62 | } 63 | 64 | 65 | // BORDERED VERSION 66 | // ---------------- 67 | 68 | .table-bordered { 69 | border: 1px solid @tableBorder; 70 | border-left: 0; 71 | border-collapse: separate; // Done so we can round those corners! 72 | *border-collapse: collapsed; // IE7 can't round corners anyway 73 | .border-radius(4px); 74 | th, 75 | td { 76 | border-left: 1px solid @tableBorder; 77 | } 78 | // Prevent a double border 79 | thead:first-child tr:first-child th, 80 | tbody:first-child tr:first-child th, 81 | tbody:first-child tr:first-child td { 82 | border-top: 0; 83 | } 84 | // For first th or td in the first row in the first thead or tbody 85 | thead:first-child tr:first-child th:first-child, 86 | tbody:first-child tr:first-child td:first-child { 87 | .border-radius(4px 0 0 0); 88 | } 89 | thead:first-child tr:first-child th:last-child, 90 | tbody:first-child tr:first-child td:last-child { 91 | .border-radius(0 4px 0 0); 92 | } 93 | // For first th or td in the first row in the first thead or tbody 94 | thead:last-child tr:last-child th:first-child, 95 | tbody:last-child tr:last-child td:first-child { 96 | .border-radius(0 0 0 4px); 97 | } 98 | thead:last-child tr:last-child th:last-child, 99 | tbody:last-child tr:last-child td:last-child { 100 | .border-radius(0 0 4px 0); 101 | } 102 | } 103 | 104 | 105 | // ZEBRA-STRIPING 106 | // -------------- 107 | 108 | // Default zebra-stripe styles (alternating gray and transparent backgrounds) 109 | .table-striped { 110 | tbody { 111 | tr:nth-child(odd) td, 112 | tr:nth-child(odd) th { 113 | background-color: @tableBackgroundAccent; 114 | } 115 | } 116 | } 117 | 118 | 119 | // HOVER EFFECT 120 | // ------------ 121 | // Placed here since it has to come after the potential zebra striping 122 | .table { 123 | tbody tr:hover td, 124 | tbody tr:hover th { 125 | //background-color: @tableBackgroundHover; 126 | } 127 | } 128 | 129 | 130 | // TABLE CELL SIZING 131 | // ----------------- 132 | 133 | // Change the columns 134 | table { 135 | .span1 { .tableColumns(1); } 136 | .span2 { .tableColumns(2); } 137 | .span3 { .tableColumns(3); } 138 | .span4 { .tableColumns(4); } 139 | .span5 { .tableColumns(5); } 140 | .span6 { .tableColumns(6); } 141 | .span7 { .tableColumns(7); } 142 | .span8 { .tableColumns(8); } 143 | .span9 { .tableColumns(9); } 144 | .span10 { .tableColumns(10); } 145 | .span11 { .tableColumns(11); } 146 | .span12 { .tableColumns(12); } 147 | .span13 { .tableColumns(13); } 148 | .span14 { .tableColumns(14); } 149 | .span15 { .tableColumns(15); } 150 | .span16 { .tableColumns(16); } 151 | .span17 { .tableColumns(17); } 152 | .span18 { .tableColumns(18); } 153 | .span19 { .tableColumns(19); } 154 | .span20 { .tableColumns(20); } 155 | .span21 { .tableColumns(21); } 156 | .span22 { .tableColumns(22); } 157 | .span23 { .tableColumns(23); } 158 | .span24 { .tableColumns(24); } 159 | } 160 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/bootstrap-collapse.js: -------------------------------------------------------------------------------- 1 | /* ============================================================= 2 | * bootstrap-collapse.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#collapse 4 | * ============================================================= 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================ */ 19 | 20 | !function( $ ){ 21 | 22 | "use strict" 23 | 24 | var Collapse = function ( element, options ) { 25 | this.$element = $(element) 26 | this.options = $.extend({}, $.fn.collapse.defaults, options) 27 | 28 | if (this.options["parent"]) { 29 | this.$parent = $(this.options["parent"]) 30 | } 31 | 32 | this.options.toggle && this.toggle() 33 | } 34 | 35 | Collapse.prototype = { 36 | 37 | constructor: Collapse 38 | 39 | , dimension: function () { 40 | var hasWidth = this.$element.hasClass('width') 41 | return hasWidth ? 'width' : 'height' 42 | } 43 | 44 | , show: function () { 45 | var dimension = this.dimension() 46 | , scroll = $.camelCase(['scroll', dimension].join('-')) 47 | , actives = this.$parent && this.$parent.find('.in') 48 | , hasData 49 | 50 | if (actives && actives.length) { 51 | hasData = actives.data('collapse') 52 | actives.collapse('hide') 53 | hasData || actives.data('collapse', null) 54 | } 55 | 56 | this.$element[dimension](0) 57 | this.transition('addClass', 'show', 'shown') 58 | this.$element[dimension](this.$element[0][scroll]) 59 | 60 | } 61 | 62 | , hide: function () { 63 | var dimension = this.dimension() 64 | this.reset(this.$element[dimension]()) 65 | this.transition('removeClass', 'hide', 'hidden') 66 | this.$element[dimension](0) 67 | } 68 | 69 | , reset: function ( size ) { 70 | var dimension = this.dimension() 71 | 72 | this.$element 73 | .removeClass('collapse') 74 | [dimension](size || 'auto') 75 | [0].offsetWidth 76 | 77 | this.$element[size ? 'addClass' : 'removeClass']('collapse') 78 | 79 | return this 80 | } 81 | 82 | , transition: function ( method, startEvent, completeEvent ) { 83 | var that = this 84 | , complete = function () { 85 | if (startEvent == 'show') that.reset() 86 | that.$element.trigger(completeEvent) 87 | } 88 | 89 | this.$element 90 | .trigger(startEvent) 91 | [method]('in') 92 | 93 | $.support.transition && this.$element.hasClass('collapse') ? 94 | this.$element.one($.support.transition.end, complete) : 95 | complete() 96 | } 97 | 98 | , toggle: function () { 99 | this[this.$element.hasClass('in') ? 'hide' : 'show']() 100 | } 101 | 102 | } 103 | 104 | /* COLLAPSIBLE PLUGIN DEFINITION 105 | * ============================== */ 106 | 107 | $.fn.collapse = function ( option ) { 108 | return this.each(function () { 109 | var $this = $(this) 110 | , data = $this.data('collapse') 111 | , options = typeof option == 'object' && option 112 | if (!data) $this.data('collapse', (data = new Collapse(this, options))) 113 | if (typeof option == 'string') data[option]() 114 | }) 115 | } 116 | 117 | $.fn.collapse.defaults = { 118 | toggle: true 119 | } 120 | 121 | $.fn.collapse.Constructor = Collapse 122 | 123 | 124 | /* COLLAPSIBLE DATA-API 125 | * ==================== */ 126 | 127 | $(function () { 128 | $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) { 129 | var $this = $(this), href 130 | , target = $this.attr('data-target') 131 | || e.preventDefault() 132 | || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 133 | , option = $(target).data('collapse') ? 'toggle' : $this.data() 134 | $(target).collapse(option) 135 | }) 136 | }) 137 | 138 | }( window.jQuery ); -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/type.less: -------------------------------------------------------------------------------- 1 | // Typography.less 2 | // Headings, body text, lists, code, and more for a versatile and durable typography system 3 | // ---------------------------------------------------------------------------------------- 4 | 5 | 6 | // BODY TEXT 7 | // --------- 8 | 9 | p { 10 | margin: 0 0 @baseLineHeight / 2; 11 | font-family: @baseFontFamily; 12 | font-size: @baseFontSize; 13 | line-height: @baseLineHeight; 14 | small { 15 | font-size: @baseFontSize - 2; 16 | color: @grayLight; 17 | } 18 | } 19 | .lead { 20 | margin-bottom: @baseLineHeight; 21 | font-size: 20px; 22 | font-weight: 200; 23 | line-height: @baseLineHeight * 1.5; 24 | } 25 | 26 | // HEADINGS 27 | // -------- 28 | 29 | h1, h2, h3, h4, h5, h6 { 30 | margin: 0; 31 | font-family: @headingsFontFamily; 32 | font-weight: @headingsFontWeight; 33 | color: @headingsColor; 34 | text-rendering: optimizelegibility; // Fix the character spacing for headings 35 | small { 36 | font-weight: normal; 37 | color: @grayLight; 38 | } 39 | } 40 | h1 { 41 | font-size: 30px; 42 | line-height: @baseLineHeight * 2; 43 | small { 44 | font-size: 18px; 45 | } 46 | } 47 | h2 { 48 | font-size: 24px; 49 | line-height: @baseLineHeight * 2; 50 | small { 51 | font-size: 18px; 52 | } 53 | } 54 | h3 { 55 | line-height: @baseLineHeight * 1.5; 56 | font-size: 18px; 57 | small { 58 | font-size: 14px; 59 | } 60 | } 61 | h4, h5, h6 { 62 | line-height: @baseLineHeight; 63 | } 64 | h4 { 65 | font-size: 14px; 66 | small { 67 | font-size: 12px; 68 | } 69 | } 70 | h5 { 71 | font-size: 12px; 72 | } 73 | h6 { 74 | font-size: 11px; 75 | color: @grayLight; 76 | text-transform: uppercase; 77 | } 78 | 79 | // Page header 80 | .page-header { 81 | padding-bottom: @baseLineHeight - 1; 82 | margin: @baseLineHeight 0; 83 | border-bottom: 1px solid @grayLighter; 84 | } 85 | .page-header h1 { 86 | line-height: 1; 87 | } 88 | 89 | 90 | 91 | // LISTS 92 | // ----- 93 | 94 | // Unordered and Ordered lists 95 | ul, ol { 96 | padding: 0; 97 | margin: 0 0 @baseLineHeight / 2 25px; 98 | } 99 | ul ul, 100 | ul ol, 101 | ol ol, 102 | ol ul { 103 | margin-bottom: 0; 104 | } 105 | ul { 106 | list-style: disc; 107 | } 108 | ol { 109 | list-style: decimal; 110 | } 111 | li { 112 | line-height: @baseLineHeight; 113 | } 114 | ul.unstyled, 115 | ol.unstyled { 116 | margin-left: 0; 117 | list-style: none; 118 | } 119 | 120 | // Description Lists 121 | dl { 122 | margin-bottom: @baseLineHeight; 123 | } 124 | dt, 125 | dd { 126 | line-height: @baseLineHeight; 127 | } 128 | dt { 129 | font-weight: bold; 130 | line-height: @baseLineHeight - 1; // fix jank Helvetica Neue font bug 131 | } 132 | dd { 133 | margin-left: @baseLineHeight / 2; 134 | } 135 | // Horizontal layout (like forms) 136 | .dl-horizontal { 137 | dt { 138 | float: left; 139 | clear: left; 140 | width: 120px; 141 | text-align: right; 142 | } 143 | dd { 144 | margin-left: 130px; 145 | } 146 | } 147 | 148 | // MISC 149 | // ---- 150 | 151 | // Horizontal rules 152 | hr { 153 | margin: 2 0; 154 | border: 0; 155 | border-top: @hrWidth solid @hrBorder; 156 | border-bottom: 1px solid @white; 157 | } 158 | 159 | // Emphasis 160 | strong { 161 | font-weight: bold; 162 | } 163 | em { 164 | font-style: italic; 165 | } 166 | .muted { 167 | color: @grayLight; 168 | } 169 | 170 | // Abbreviations and acronyms 171 | abbr[title] { 172 | border-bottom: 1px dotted #ddd; 173 | cursor: help; 174 | } 175 | abbr.initialism { 176 | font-size: 90%; 177 | text-transform: uppercase; 178 | } 179 | 180 | // Blockquotes 181 | blockquote { 182 | padding: 0 0 0 15px; 183 | margin: 0 0 @baseLineHeight; 184 | border-left: 5px solid @grayLighter; 185 | p { 186 | margin-bottom: 0; 187 | #font > .shorthand(16px,300,@baseLineHeight * 1.25); 188 | } 189 | small { 190 | display: block; 191 | line-height: @baseLineHeight; 192 | color: @grayLight; 193 | &:before { 194 | content: '\2014 \00A0'; 195 | } 196 | } 197 | 198 | // Float right with text-align: right 199 | &.pull-right { 200 | float: right; 201 | padding-left: 0; 202 | padding-right: 15px; 203 | border-left: 0; 204 | border-right: 5px solid @grayLighter; 205 | p, 206 | small { 207 | text-align: right; 208 | } 209 | } 210 | } 211 | 212 | // Quotes 213 | q:before, 214 | q:after, 215 | blockquote:before, 216 | blockquote:after { 217 | content: ""; 218 | } 219 | 220 | // Addresses 221 | address { 222 | display: block; 223 | margin-bottom: @baseLineHeight; 224 | line-height: @baseLineHeight; 225 | font-style: normal; 226 | } 227 | 228 | // Misc 229 | small { 230 | font-size: 100%; 231 | } 232 | cite { 233 | font-style: normal; 234 | } 235 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/tests/unit/bootstrap-typeahead.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("bootstrap-typeahead") 4 | 5 | test("should be defined on jquery object", function () { 6 | ok($(document.body).typeahead, 'alert method is defined') 7 | }) 8 | 9 | test("should return element", function () { 10 | ok($(document.body).typeahead()[0] == document.body, 'document.body returned') 11 | }) 12 | 13 | test("should listen to an input", function () { 14 | var $input = $('') 15 | $input.typeahead() 16 | ok($input.data('events').blur, 'has a blur event') 17 | ok($input.data('events').keypress, 'has a keypress event') 18 | ok($input.data('events').keyup, 'has a keyup event') 19 | if ($.browser.webkit || $.browser.msie) { 20 | ok($input.data('events').keydown, 'has a keydown event') 21 | } else { 22 | ok($input.data('events').keydown, 'does not have a keydown event') 23 | } 24 | }) 25 | 26 | test("should create a menu", function () { 27 | var $input = $('') 28 | ok($input.typeahead().data('typeahead').$menu, 'has a menu') 29 | }) 30 | 31 | test("should listen to the menu", function () { 32 | var $input = $('') 33 | , $menu = $input.typeahead().data('typeahead').$menu 34 | 35 | ok($menu.data('events').mouseover, 'has a mouseover(pseudo: mouseenter)') 36 | ok($menu.data('events').click, 'has a click') 37 | }) 38 | 39 | test("should show menu when query entered", function () { 40 | var $input = $('').typeahead({ 41 | source: ['aa', 'ab', 'ac'] 42 | }) 43 | , typeahead = $input.data('typeahead') 44 | 45 | $input.val('a') 46 | typeahead.lookup() 47 | 48 | ok(typeahead.$menu.is(":visible"), 'typeahead is visible') 49 | equals(typeahead.$menu.find('li').length, 3, 'has 3 items in menu') 50 | equals(typeahead.$menu.find('.active').length, 1, 'one item is active') 51 | 52 | typeahead.$menu.remove() 53 | }) 54 | 55 | test("should hide menu when query entered", function () { 56 | stop() 57 | var $input = $('').typeahead({ 58 | source: ['aa', 'ab', 'ac'] 59 | }) 60 | , typeahead = $input.data('typeahead') 61 | 62 | $input.val('a') 63 | typeahead.lookup() 64 | 65 | ok(typeahead.$menu.is(":visible"), 'typeahead is visible') 66 | equals(typeahead.$menu.find('li').length, 3, 'has 3 items in menu') 67 | equals(typeahead.$menu.find('.active').length, 1, 'one item is active') 68 | 69 | $input.blur() 70 | 71 | setTimeout(function () { 72 | ok(!typeahead.$menu.is(":visible"), "typeahead is no longer visible") 73 | start() 74 | }, 200) 75 | 76 | typeahead.$menu.remove() 77 | }) 78 | 79 | test("should set next item when down arrow is pressed", function () { 80 | var $input = $('').typeahead({ 81 | source: ['aa', 'ab', 'ac'] 82 | }) 83 | , typeahead = $input.data('typeahead') 84 | 85 | $input.val('a') 86 | typeahead.lookup() 87 | 88 | ok(typeahead.$menu.is(":visible"), 'typeahead is visible') 89 | equals(typeahead.$menu.find('li').length, 3, 'has 3 items in menu') 90 | equals(typeahead.$menu.find('.active').length, 1, 'one item is active') 91 | ok(typeahead.$menu.find('li').first().hasClass('active'), "first item is active") 92 | 93 | $input.trigger({ 94 | type: 'keypress' 95 | , keyCode: 40 96 | }) 97 | 98 | ok(typeahead.$menu.find('li').first().next().hasClass('active'), "second item is active") 99 | 100 | 101 | $input.trigger({ 102 | type: 'keypress' 103 | , keyCode: 38 104 | }) 105 | 106 | ok(typeahead.$menu.find('li').first().hasClass('active'), "first item is active") 107 | 108 | typeahead.$menu.remove() 109 | }) 110 | 111 | 112 | test("should set input value to selected item", function () { 113 | var $input = $('').typeahead({ 114 | source: ['aa', 'ab', 'ac'] 115 | }) 116 | , typeahead = $input.data('typeahead') 117 | , changed = false 118 | 119 | $input.val('a') 120 | typeahead.lookup() 121 | 122 | $input.change(function() { changed = true }); 123 | 124 | $(typeahead.$menu.find('li')[2]).mouseover().click() 125 | 126 | equals($input.val(), 'ac', 'input value was correctly set') 127 | ok(!typeahead.$menu.is(':visible'), 'the menu was hidden') 128 | ok(changed, 'a change event was fired') 129 | 130 | typeahead.$menu.remove() 131 | }) 132 | }) 133 | -------------------------------------------------------------------------------- /src/www/js/views/base-widget-view.js: -------------------------------------------------------------------------------- 1 | var BaseWidget = Backbone.View.extend({ 2 | 3 | enableLogging : false 4 | 5 | , updateFrequency : 5000 6 | 7 | , Name : "BaseWidget" 8 | 9 | , server : "" 10 | 11 | , events : { 12 | "click .time-period" : "ChangeTimeFrame" 13 | , "click .go" : "Go" 14 | } 15 | 16 | , init : function () { 17 | 18 | var self = this 19 | 20 | $(document).on("ServerChange", function(e, server){ 21 | self.server = server 22 | }) 23 | 24 | this.timer = setInterval( function () { self.UpdateModel(true) }, this.updateFrequency ) 25 | 26 | // set event listners 27 | this.model 28 | .on("error", this.error, this) 29 | .on("change", this.ModelChanged, this) 30 | 31 | } 32 | 33 | , UpdateModel : function ( enableTimer ) { 34 | 35 | clearInterval(this.timer) 36 | 37 | this.startTime = new Date() 38 | 39 | this.model.fetch({ 40 | data : { 41 | // if no from/to are found, provide reasonable defaults of a week ago and now, respectively 42 | from : this.$el.find('[name=from]').val() || new Date(new Date() - 7*24*60*60000).toUTCString() 43 | , to : this.$el.find('[name=to]').val() || new Date().toUTCString() 44 | , server : this.server 45 | } 46 | }) 47 | 48 | this.enableTimer = enableTimer 49 | 50 | } 51 | 52 | , ModelChanged : function(){ 53 | 54 | this.endTime = new Date() 55 | var timeElapsed = (this.endTime - this.startTime); 56 | 57 | if (this.enableLogging) 58 | console.log(this.Name + ": Time Elapsed = " + timeElapsed + " ms") 59 | 60 | this.render() 61 | 62 | if(this.enableTimer) 63 | { 64 | var self = this 65 | this.timer = setInterval( function () { self.UpdateModel(true) }, this.updateFrequency ) 66 | } 67 | } 68 | 69 | , Go : function( el ) { 70 | this.UpdateModel(false) 71 | } 72 | 73 | , ChangeTimeFrame : function ( el ) { 74 | 75 | var selectionType = $(el.target).data("type") 76 | , timeFrame = parseInt( $(el.target).data("time") ) 77 | 78 | // update the dropdown's label 79 | $(el.target) 80 | .closest(".btn-group") 81 | .children() 82 | .first() 83 | .text($(el.target).text()) 84 | 85 | // Custom time frame selected 86 | if ( selectionType == "custom" ) { 87 | $(el.target) 88 | .closest(".btn-group") 89 | .siblings(".date-control") 90 | .css("display","inline") 91 | } 92 | // real time 93 | else if ( selectionType == "realtime" ) { 94 | $(el.target) 95 | .closest(".btn-group") 96 | .siblings(".date-control") 97 | .css("display","none") 98 | 99 | var self = this 100 | this.$el.find('[name=from]').val("") 101 | this.$el.find('[name=to]').val("") 102 | this.timer = setInterval( function () { self.UpdateModel(true) }, this.updateFrequency ) 103 | } 104 | // one of the template time frame selected 105 | // example: last 15mins, last 1 day etc 106 | else { 107 | 108 | $(el.target) 109 | .closest(".btn-group") 110 | .siblings(".date-control") 111 | .css("display","none") 112 | 113 | var endDate = new Date() 114 | , startDate = endDate 115 | 116 | switch(selectionType) { 117 | 118 | case 'minute' : 119 | startDate = new Date(endDate - timeFrame * 60000) 120 | break 121 | 122 | case 'hour' : 123 | startDate = new Date(endDate - timeFrame * 60*60000) 124 | break 125 | 126 | case 'day' : 127 | startDate = new Date(endDate - timeFrame * 24*60*60000) 128 | break 129 | 130 | case 'week' : 131 | startDate = new Date(endDate - timeFrame * 7*24*60*60000) 132 | break 133 | 134 | case 'month' : 135 | startDate = new Date(endDate - timeFrame * 30*24*60*60000) 136 | break 137 | } 138 | 139 | this.$el.find('[name=from]').val(this.ISODateString(startDate)) 140 | this.$el.find('[name=to]').val(this.ISODateString(endDate)) 141 | this.UpdateModel(false) 142 | 143 | } 144 | } 145 | 146 | , ISODateString : function ( d ) { 147 | 148 | function pad ( n ) { 149 | return n < 10 ? '0'+n : n 150 | } 151 | 152 | return d.getFullYear()+'-' 153 | + pad(d.getMonth()+1)+'-' 154 | + pad(d.getDate())+' ' 155 | + pad(d.getHours())+':' 156 | + pad(d.getMinutes())+':' 157 | + pad(d.getSeconds()) 158 | } 159 | 160 | , error: function ( model, error ) { 161 | if (this.enableLogging) 162 | console.log(this.Name + ": Error Occured \n" + error + "\n" + model ) 163 | 164 | } 165 | 166 | }) 167 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/buttons.less: -------------------------------------------------------------------------------- 1 | // BUTTON STYLES 2 | // ------------- 3 | 4 | 5 | // Base styles 6 | // -------------------------------------------------- 7 | 8 | // Core 9 | .btn { 10 | display: inline-block; 11 | .ie7-inline-block(); 12 | padding: 4px 10px 4px; 13 | margin-bottom: 0; // For input.btn 14 | font-size: @baseFontSize; 15 | line-height: @baseLineHeight; 16 | color: @grayDark; 17 | text-align: center; 18 | text-shadow: 0 1px 1px rgba(255,255,255,.75); 19 | vertical-align: middle; 20 | .buttonBackground(@btnBackground, @btnBackgroundHighlight); 21 | border: 1px solid @btnBorder; 22 | border-bottom-color: darken(@btnBorder, 10%); 23 | .border-radius(4px); 24 | @shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); 25 | .box-shadow(@shadow); 26 | cursor: pointer; 27 | 28 | // Give IE7 some love 29 | .ie7-restore-left-whitespace(); 30 | } 31 | 32 | // Hover state 33 | .btn:hover { 34 | color: @grayDark; 35 | text-decoration: none; 36 | background-color: darken(@white, 10%); 37 | background-position: 0 -15px; 38 | 39 | // transition is only when going to hover, otherwise the background 40 | // behind the gradient (there for IE<=9 fallback) gets mismatched 41 | .transition(background-position .1s linear); 42 | } 43 | 44 | // Focus state for keyboard and accessibility 45 | .btn:focus { 46 | .tab-focus(); 47 | } 48 | 49 | // Active state 50 | .btn.active, 51 | .btn:active { 52 | background-image: none; 53 | @shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); 54 | .box-shadow(@shadow); 55 | background-color: darken(@white, 10%); 56 | background-color: darken(@white, 15%) e("\9"); 57 | outline: 0; 58 | } 59 | 60 | // Disabled state 61 | .btn.disabled, 62 | .btn[disabled] { 63 | cursor: default; 64 | background-image: none; 65 | background-color: darken(@white, 10%); 66 | .opacity(65); 67 | .box-shadow(none); 68 | } 69 | 70 | 71 | // Button Sizes 72 | // -------------------------------------------------- 73 | 74 | // Large 75 | .btn-large { 76 | padding: 9px 14px; 77 | font-size: @baseFontSize + 2px; 78 | line-height: normal; 79 | .border-radius(5px); 80 | } 81 | .btn-large [class^="icon-"] { 82 | margin-top: 1px; 83 | } 84 | 85 | // Small 86 | .btn-small { 87 | padding: 5px 9px; 88 | font-size: @baseFontSize - 2px; 89 | line-height: @baseLineHeight - 2px; 90 | } 91 | .btn-small [class^="icon-"] { 92 | margin-top: -1px; 93 | } 94 | 95 | // Mini 96 | .btn-mini { 97 | padding: 2px 6px; 98 | font-size: @baseFontSize - 2px; 99 | line-height: @baseLineHeight - 4px; 100 | } 101 | 102 | 103 | // Alternate buttons 104 | // -------------------------------------------------- 105 | 106 | // Set text color 107 | // ------------------------- 108 | .btn-primary, 109 | .btn-primary:hover, 110 | .btn-warning, 111 | .btn-warning:hover, 112 | .btn-danger, 113 | .btn-danger:hover, 114 | .btn-success, 115 | .btn-success:hover, 116 | .btn-info, 117 | .btn-info:hover, 118 | .btn-inverse, 119 | .btn-inverse:hover { 120 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 121 | color: @white; 122 | } 123 | // Provide *some* extra contrast for those who can get it 124 | .btn-primary.active, 125 | .btn-warning.active, 126 | .btn-danger.active, 127 | .btn-success.active, 128 | .btn-info.active, 129 | .btn-inverse.active { 130 | color: rgba(255,255,255,.75); 131 | } 132 | 133 | // Set the backgrounds 134 | // ------------------------- 135 | .btn-primary { 136 | .buttonBackground(@btnPrimaryBackground, @btnPrimaryBackgroundHighlight); 137 | } 138 | // Warning appears are orange 139 | .btn-warning { 140 | .buttonBackground(@btnWarningBackground, @btnWarningBackgroundHighlight); 141 | } 142 | // Danger and error appear as red 143 | .btn-danger { 144 | .buttonBackground(@btnDangerBackground, @btnDangerBackgroundHighlight); 145 | } 146 | // Success appears as green 147 | .btn-success { 148 | .buttonBackground(@btnSuccessBackground, @btnSuccessBackgroundHighlight); 149 | } 150 | // Info appears as a neutral blue 151 | .btn-info { 152 | .buttonBackground(@btnInfoBackground, @btnInfoBackgroundHighlight); 153 | } 154 | // Inverse appears as dark gray 155 | .btn-inverse { 156 | .buttonBackground(@btnInverseBackground, @btnInverseBackgroundHighlight); 157 | } 158 | 159 | 160 | // Cross-browser Jank 161 | // -------------------------------------------------- 162 | 163 | button.btn, 164 | input[type="submit"].btn { 165 | 166 | // Firefox 3.6 only I believe 167 | &::-moz-focus-inner { 168 | padding: 0; 169 | border: 0; 170 | } 171 | 172 | // IE7 has some default padding on button controls 173 | *padding-top: 2px; 174 | *padding-bottom: 2px; 175 | &.btn-large { 176 | *padding-top: 7px; 177 | *padding-bottom: 7px; 178 | } 179 | &.btn-small { 180 | *padding-top: 3px; 181 | *padding-bottom: 3px; 182 | } 183 | &.btn-mini { 184 | *padding-top: 1px; 185 | *padding-bottom: 1px; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/less/button-groups.less: -------------------------------------------------------------------------------- 1 | // BUTTON GROUPS 2 | // ------------- 3 | 4 | 5 | // Make the div behave like a button 6 | .btn-group { 7 | position: relative; 8 | .clearfix(); // clears the floated buttons 9 | .ie7-restore-left-whitespace(); 10 | } 11 | 12 | // Space out series of button groups 13 | .btn-group + .btn-group { 14 | margin-left: 5px; 15 | } 16 | 17 | // Optional: Group multiple button groups together for a toolbar 18 | .btn-toolbar { 19 | margin-top: @baseLineHeight / 2; 20 | margin-bottom: @baseLineHeight / 2; 21 | .btn-group { 22 | display: inline-block; 23 | .ie7-inline-block(); 24 | } 25 | } 26 | 27 | // Float them, remove border radius, then re-add to first and last elements 28 | .btn-group .btn { 29 | position: relative; 30 | float: left; 31 | margin-left: -1px; 32 | .border-radius(0); 33 | } 34 | // Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match 35 | .btn-group .btn:first-child { 36 | margin-left: 0; 37 | -webkit-border-top-left-radius: 4px; 38 | -moz-border-radius-topleft: 4px; 39 | border-top-left-radius: 4px; 40 | -webkit-border-bottom-left-radius: 4px; 41 | -moz-border-radius-bottomleft: 4px; 42 | border-bottom-left-radius: 4px; 43 | } 44 | .btn-group .btn:last-child, 45 | .btn-group .dropdown-toggle { 46 | -webkit-border-top-right-radius: 4px; 47 | -moz-border-radius-topright: 4px; 48 | border-top-right-radius: 4px; 49 | -webkit-border-bottom-right-radius: 4px; 50 | -moz-border-radius-bottomright: 4px; 51 | border-bottom-right-radius: 4px; 52 | } 53 | // Reset corners for large buttons 54 | .btn-group .btn.large:first-child { 55 | margin-left: 0; 56 | -webkit-border-top-left-radius: 6px; 57 | -moz-border-radius-topleft: 6px; 58 | border-top-left-radius: 6px; 59 | -webkit-border-bottom-left-radius: 6px; 60 | -moz-border-radius-bottomleft: 6px; 61 | border-bottom-left-radius: 6px; 62 | } 63 | .btn-group .btn.large:last-child, 64 | .btn-group .large.dropdown-toggle { 65 | -webkit-border-top-right-radius: 6px; 66 | -moz-border-radius-topright: 6px; 67 | border-top-right-radius: 6px; 68 | -webkit-border-bottom-right-radius: 6px; 69 | -moz-border-radius-bottomright: 6px; 70 | border-bottom-right-radius: 6px; 71 | } 72 | 73 | // On hover/focus/active, bring the proper btn to front 74 | .btn-group .btn:hover, 75 | .btn-group .btn:focus, 76 | .btn-group .btn:active, 77 | .btn-group .btn.active { 78 | z-index: 2; 79 | } 80 | 81 | // On active and open, don't show outline 82 | .btn-group .dropdown-toggle:active, 83 | .btn-group.open .dropdown-toggle { 84 | outline: 0; 85 | } 86 | 87 | 88 | 89 | // Split button dropdowns 90 | // ---------------------- 91 | 92 | // Give the line between buttons some depth 93 | .btn-group .dropdown-toggle { 94 | padding-left: 8px; 95 | padding-right: 8px; 96 | @shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); 97 | .box-shadow(@shadow); 98 | *padding-top: 3px; 99 | *padding-bottom: 3px; 100 | } 101 | .btn-group .btn-mini.dropdown-toggle { 102 | padding-left: 5px; 103 | padding-right: 5px; 104 | *padding-top: 1px; 105 | *padding-bottom: 1px; 106 | } 107 | .btn-group .btn-small.dropdown-toggle { 108 | *padding-top: 4px; 109 | *padding-bottom: 4px; 110 | } 111 | .btn-group .btn-large.dropdown-toggle { 112 | padding-left: 12px; 113 | padding-right: 12px; 114 | } 115 | 116 | .btn-group.open { 117 | // IE7's z-index only goes to the nearest positioned ancestor, which would 118 | // make the menu appear below buttons that appeared later on the page 119 | *z-index: @zindexDropdown; 120 | 121 | // Reposition menu on open and round all corners 122 | .dropdown-menu { 123 | display: block; 124 | margin-top: 1px; 125 | .border-radius(5px); 126 | } 127 | 128 | .dropdown-toggle { 129 | background-image: none; 130 | @shadow: inset 0 1px 6px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); 131 | .box-shadow(@shadow); 132 | } 133 | } 134 | 135 | // Reposition the caret 136 | .btn .caret { 137 | margin-top: 7px; 138 | margin-left: 0; 139 | } 140 | .btn:hover .caret, 141 | .open.btn-group .caret { 142 | .opacity(100); 143 | } 144 | // Carets in other button sizes 145 | .btn-mini .caret { 146 | margin-top: 5px; 147 | } 148 | .btn-small .caret { 149 | margin-top: 6px; 150 | } 151 | .btn-large .caret { 152 | margin-top: 6px; 153 | border-left: 5px solid transparent; 154 | border-right: 5px solid transparent; 155 | border-top: 5px solid @black; 156 | } 157 | 158 | 159 | // Account for other colors 160 | .btn-primary, 161 | .btn-warning, 162 | .btn-danger, 163 | .btn-info, 164 | .btn-success, 165 | .btn-inverse { 166 | .caret { 167 | border-top-color: @white; 168 | border-bottom-color: @white; 169 | .opacity(75); 170 | } 171 | } 172 | 173 | -------------------------------------------------------------------------------- /src/api/controller/BaseController.py: -------------------------------------------------------------------------------- 1 | from dataprovider.dataprovider import RedisLiveDataProvider 2 | import tornado.ioloop 3 | import tornado.web 4 | import dateutil.parser 5 | 6 | 7 | class BaseController(tornado.web.RequestHandler): 8 | 9 | stats_provider = RedisLiveDataProvider.get_provider() 10 | 11 | def datetime_to_list(self, datetime): 12 | """Converts a datetime to a list. 13 | 14 | Args: 15 | datetime (datetime): The datetime to convert. 16 | """ 17 | parsed_date = dateutil.parser.parse(datetime) 18 | # don't return the last two fields, we don't want them. 19 | return tuple(parsed_date.timetuple())[:-2] 20 | 21 | # todo : fix this 22 | def average_data(self, data): 23 | """Averages data. 24 | 25 | TODO: More docstring here, once functionality is understood. 26 | """ 27 | average = [] 28 | 29 | deviation=1024*1024 30 | 31 | start = dateutil.parser.parse(data[0][0]) 32 | end = dateutil.parser.parse(data[-1][0]) 33 | difference = end - start 34 | weeks, days = divmod(difference.days, 7) 35 | minutes, seconds = divmod(difference.seconds, 60) 36 | hours, minutes = divmod(minutes, 60) 37 | 38 | # TODO: These if/elif/else branches chould probably be broken out into 39 | # individual functions to make it easier to follow what's going on. 40 | if difference.days > 0: 41 | current_max = 0 42 | current_current = 0 43 | current_d = 0 44 | 45 | for dt, max_memory, current_memory in data: 46 | d = dateutil.parser.parse(dt) 47 | if d.day != current_d: 48 | current_d = d.day 49 | average.append([dt, max_memory, current_memory]) 50 | current_max = max_memory 51 | current_current = current_memory 52 | else: 53 | if max_memory > current_max or \ 54 | current_memory > current_current: 55 | average.pop() 56 | average.append([dt, max_memory, current_memory]) 57 | current_max=max_memory 58 | current_current=current_memory 59 | elif hours > 0: 60 | current_max = 0 61 | current_current = 0 62 | current = -1 63 | keep_flag = False 64 | 65 | for dt, max_memory, current_memory in data: 66 | d = dateutil.parser.parse(dt) 67 | if d.hour != current: 68 | current = d.hour 69 | average.append([dt, max_memory, current_memory]) 70 | current_max=max_memory 71 | current_current=current_memory 72 | keep_flag=False 73 | elif abs(max_memory - current_max) > deviation or \ 74 | abs(current_memory - current_current) > deviation: 75 | #average.pop() 76 | average.append([dt, max_memory, current_memory]) 77 | current_max = max_memory 78 | current_current = current_memory 79 | keep_flag = True 80 | elif max_memory > current_max or \ 81 | current_memory > current_current: 82 | if keep_flag != True: 83 | average.pop() 84 | average.append([dt, max_memory, current_memory]) 85 | current_max = max_memory 86 | current_current = current_memory 87 | keep_flag = False 88 | else: 89 | current_max = 0 90 | current_current = 0 91 | current_m = -1 92 | keep_flag = False 93 | for dt, max_memory, current_memory in data: 94 | d = dateutil.parser.parse(dt) 95 | if d.minute != current_m: 96 | current_m = d.minute 97 | average.append([dt, max_memory, current_memory]) 98 | current_max = max_memory 99 | current_current = current_memory 100 | keep_flag = False 101 | elif abs(max_memory - current_max) > deviation or \ 102 | abs(current_memory - current_current) > deviation: 103 | #average.pop() 104 | average.append([dt, max_memory, current_memory]) 105 | current_max = max_memory 106 | current_current = current_memory 107 | keep_flag = True 108 | elif max_memory > current_max or \ 109 | current_memory > current_current: 110 | if keep_flag!=True: 111 | average.pop() 112 | average.append([dt,max_memory,current_memory]) 113 | current_max=max_memory 114 | current_current=current_memory 115 | keep_flag=False 116 | 117 | return average 118 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/bootstrap-carousel.js: -------------------------------------------------------------------------------- 1 | /* ========================================================== 2 | * bootstrap-carousel.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#carousel 4 | * ========================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function( $ ){ 22 | 23 | "use strict" 24 | 25 | /* CAROUSEL CLASS DEFINITION 26 | * ========================= */ 27 | 28 | var Carousel = function (element, options) { 29 | this.$element = $(element) 30 | this.options = $.extend({}, $.fn.carousel.defaults, options) 31 | this.options.slide && this.slide(this.options.slide) 32 | this.options.pause == 'hover' && this.$element 33 | .on('mouseenter', $.proxy(this.pause, this)) 34 | .on('mouseleave', $.proxy(this.cycle, this)) 35 | } 36 | 37 | Carousel.prototype = { 38 | 39 | cycle: function () { 40 | this.interval = setInterval($.proxy(this.next, this), this.options.interval) 41 | return this 42 | } 43 | 44 | , to: function (pos) { 45 | var $active = this.$element.find('.active') 46 | , children = $active.parent().children() 47 | , activePos = children.index($active) 48 | , that = this 49 | 50 | if (pos > (children.length - 1) || pos < 0) return 51 | 52 | if (this.sliding) { 53 | return this.$element.one('slid', function () { 54 | that.to(pos) 55 | }) 56 | } 57 | 58 | if (activePos == pos) { 59 | return this.pause().cycle() 60 | } 61 | 62 | return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos])) 63 | } 64 | 65 | , pause: function () { 66 | clearInterval(this.interval) 67 | this.interval = null 68 | return this 69 | } 70 | 71 | , next: function () { 72 | if (this.sliding) return 73 | return this.slide('next') 74 | } 75 | 76 | , prev: function () { 77 | if (this.sliding) return 78 | return this.slide('prev') 79 | } 80 | 81 | , slide: function (type, next) { 82 | var $active = this.$element.find('.active') 83 | , $next = next || $active[type]() 84 | , isCycling = this.interval 85 | , direction = type == 'next' ? 'left' : 'right' 86 | , fallback = type == 'next' ? 'first' : 'last' 87 | , that = this 88 | 89 | this.sliding = true 90 | 91 | isCycling && this.pause() 92 | 93 | $next = $next.length ? $next : this.$element.find('.item')[fallback]() 94 | 95 | if ($next.hasClass('active')) return 96 | 97 | if (!$.support.transition && this.$element.hasClass('slide')) { 98 | this.$element.trigger('slide') 99 | $active.removeClass('active') 100 | $next.addClass('active') 101 | this.sliding = false 102 | this.$element.trigger('slid') 103 | } else { 104 | $next.addClass(type) 105 | $next[0].offsetWidth // force reflow 106 | $active.addClass(direction) 107 | $next.addClass(direction) 108 | this.$element.trigger('slide') 109 | this.$element.one($.support.transition.end, function () { 110 | $next.removeClass([type, direction].join(' ')).addClass('active') 111 | $active.removeClass(['active', direction].join(' ')) 112 | that.sliding = false 113 | setTimeout(function () { that.$element.trigger('slid') }, 0) 114 | }) 115 | } 116 | 117 | isCycling && this.cycle() 118 | 119 | return this 120 | } 121 | 122 | } 123 | 124 | 125 | /* CAROUSEL PLUGIN DEFINITION 126 | * ========================== */ 127 | 128 | $.fn.carousel = function ( option ) { 129 | return this.each(function () { 130 | var $this = $(this) 131 | , data = $this.data('carousel') 132 | , options = typeof option == 'object' && option 133 | if (!data) $this.data('carousel', (data = new Carousel(this, options))) 134 | if (typeof option == 'number') data.to(option) 135 | else if (typeof option == 'string' || (option = options.slide)) data[option]() 136 | else data.cycle() 137 | }) 138 | } 139 | 140 | $.fn.carousel.defaults = { 141 | interval: 5000 142 | , pause: 'hover' 143 | } 144 | 145 | $.fn.carousel.Constructor = Carousel 146 | 147 | 148 | /* CAROUSEL DATA-API 149 | * ================= */ 150 | 151 | $(function () { 152 | $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) { 153 | var $this = $(this), href 154 | , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 155 | , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data()) 156 | $target.carousel(options) 157 | e.preventDefault() 158 | }) 159 | }) 160 | 161 | }( window.jQuery ); -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/tests/vendor/qunit.css: -------------------------------------------------------------------------------- 1 | /** 2 | * QUnit - A JavaScript Unit Testing Framework 3 | * 4 | * http://docs.jquery.com/QUnit 5 | * 6 | * Copyright (c) 2012 John Resig, Jörn Zaefferer 7 | * Dual licensed under the MIT (MIT-LICENSE.txt) 8 | * or GPL (GPL-LICENSE.txt) licenses. 9 | */ 10 | 11 | /** Font Family and Sizes */ 12 | 13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 15 | } 16 | 17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 18 | #qunit-tests { font-size: smaller; } 19 | 20 | 21 | /** Resets */ 22 | 23 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { 24 | margin: 0; 25 | padding: 0; 26 | } 27 | 28 | 29 | /** Header */ 30 | 31 | #qunit-header { 32 | padding: 0.5em 0 0.5em 1em; 33 | 34 | color: #8699a4; 35 | background-color: #0d3349; 36 | 37 | font-size: 1.5em; 38 | line-height: 1em; 39 | font-weight: normal; 40 | 41 | border-radius: 15px 15px 0 0; 42 | -moz-border-radius: 15px 15px 0 0; 43 | -webkit-border-top-right-radius: 15px; 44 | -webkit-border-top-left-radius: 15px; 45 | } 46 | 47 | #qunit-header a { 48 | text-decoration: none; 49 | color: #c2ccd1; 50 | } 51 | 52 | #qunit-header a:hover, 53 | #qunit-header a:focus { 54 | color: #fff; 55 | } 56 | 57 | #qunit-banner { 58 | height: 5px; 59 | } 60 | 61 | #qunit-testrunner-toolbar { 62 | padding: 0.5em 0 0.5em 2em; 63 | color: #5E740B; 64 | background-color: #eee; 65 | } 66 | 67 | #qunit-userAgent { 68 | padding: 0.5em 0 0.5em 2.5em; 69 | background-color: #2b81af; 70 | color: #fff; 71 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 72 | } 73 | 74 | 75 | /** Tests: Pass/Fail */ 76 | 77 | #qunit-tests { 78 | list-style-position: inside; 79 | } 80 | 81 | #qunit-tests li { 82 | padding: 0.4em 0.5em 0.4em 2.5em; 83 | border-bottom: 1px solid #fff; 84 | list-style-position: inside; 85 | } 86 | 87 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 88 | display: none; 89 | } 90 | 91 | #qunit-tests li strong { 92 | cursor: pointer; 93 | } 94 | 95 | #qunit-tests li a { 96 | padding: 0.5em; 97 | color: #c2ccd1; 98 | text-decoration: none; 99 | } 100 | #qunit-tests li a:hover, 101 | #qunit-tests li a:focus { 102 | color: #000; 103 | } 104 | 105 | #qunit-tests ol { 106 | margin-top: 0.5em; 107 | padding: 0.5em; 108 | 109 | background-color: #fff; 110 | 111 | border-radius: 15px; 112 | -moz-border-radius: 15px; 113 | -webkit-border-radius: 15px; 114 | 115 | box-shadow: inset 0px 2px 13px #999; 116 | -moz-box-shadow: inset 0px 2px 13px #999; 117 | -webkit-box-shadow: inset 0px 2px 13px #999; 118 | } 119 | 120 | #qunit-tests table { 121 | border-collapse: collapse; 122 | margin-top: .2em; 123 | } 124 | 125 | #qunit-tests th { 126 | text-align: right; 127 | vertical-align: top; 128 | padding: 0 .5em 0 0; 129 | } 130 | 131 | #qunit-tests td { 132 | vertical-align: top; 133 | } 134 | 135 | #qunit-tests pre { 136 | margin: 0; 137 | white-space: pre-wrap; 138 | word-wrap: break-word; 139 | } 140 | 141 | #qunit-tests del { 142 | background-color: #e0f2be; 143 | color: #374e0c; 144 | text-decoration: none; 145 | } 146 | 147 | #qunit-tests ins { 148 | background-color: #ffcaca; 149 | color: #500; 150 | text-decoration: none; 151 | } 152 | 153 | /*** Test Counts */ 154 | 155 | #qunit-tests b.counts { color: black; } 156 | #qunit-tests b.passed { color: #5E740B; } 157 | #qunit-tests b.failed { color: #710909; } 158 | 159 | #qunit-tests li li { 160 | margin: 0.5em; 161 | padding: 0.4em 0.5em 0.4em 0.5em; 162 | background-color: #fff; 163 | border-bottom: none; 164 | list-style-position: inside; 165 | } 166 | 167 | /*** Passing Styles */ 168 | 169 | #qunit-tests li li.pass { 170 | color: #5E740B; 171 | background-color: #fff; 172 | border-left: 26px solid #C6E746; 173 | } 174 | 175 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 176 | #qunit-tests .pass .test-name { color: #366097; } 177 | 178 | #qunit-tests .pass .test-actual, 179 | #qunit-tests .pass .test-expected { color: #999999; } 180 | 181 | #qunit-banner.qunit-pass { background-color: #C6E746; } 182 | 183 | /*** Failing Styles */ 184 | 185 | #qunit-tests li li.fail { 186 | color: #710909; 187 | background-color: #fff; 188 | border-left: 26px solid #EE5757; 189 | white-space: pre; 190 | } 191 | 192 | #qunit-tests > li:last-child { 193 | border-radius: 0 0 15px 15px; 194 | -moz-border-radius: 0 0 15px 15px; 195 | -webkit-border-bottom-right-radius: 15px; 196 | -webkit-border-bottom-left-radius: 15px; 197 | } 198 | 199 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 200 | #qunit-tests .fail .test-name, 201 | #qunit-tests .fail .module-name { color: #000000; } 202 | 203 | #qunit-tests .fail .test-actual { color: #EE5757; } 204 | #qunit-tests .fail .test-expected { color: green; } 205 | 206 | #qunit-banner.qunit-fail { background-color: #EE5757; } 207 | 208 | 209 | /** Result */ 210 | 211 | #qunit-testresult { 212 | padding: 0.5em 0.5em 0.5em 2.5em; 213 | 214 | color: #2b81af; 215 | background-color: #D2E0E6; 216 | 217 | border-bottom: 1px solid white; 218 | } 219 | 220 | /** Fixture */ 221 | 222 | #qunit-fixture { 223 | position: absolute; 224 | top: -10000px; 225 | left: -10000px; 226 | } 227 | 228 | /** Runoff */ 229 | 230 | #qunit-fixture { 231 | display:none; 232 | } -------------------------------------------------------------------------------- /src/www/js/google/jsapi.css: -------------------------------------------------------------------------------- 1 | 2 | .google-visualization-toolbar{font-size:100%}.google-visualization-toolbar .google-visualization-toolbar-export-igoogle,.google-visualization-toolbar .google-visualization-toolbar-export-data,.google-visualization-toolbar .google-visualization-toolbar-html-code{margin-right:.1em}.google-visualization-toolbar-html-code-explanation{font-weight:bold}.google-visualization-toolbar-ok-button{padding:2px}.google-visualization-toolbar-triangle{position:absolute;right:0;top:0}.google-visualization-toolbar-caption-table{width:100%;padding:0;margin:0;border:0;border-collapse:collapse}.google-visualization-toolbar-small-dialog{width:500px}.google-visualization-toolbar-big-dialog{width:800px}.google-visualization-toolbar-small-dialog,.google-visualization-toolbar-big-dialog{position:absolute;background-color:#c1d9ff;border:1px solid #3a5774;padding:8px}.google-visualization-toolbar-small-dialog-bg,.google-visualization-toolbar-big-dialog-bg{background-color:#ddd;position:absolute;top:0;left:0}.google-visualization-toolbar-small-dialog-title,.google-visualization-toolbar-big-dialog-title{background-color:#e0edfe;color:#000;cursor:pointer;padding:8px;position:relative;font-size:12pt;font-weight:bold;vertical-align:middle}.google-visualization-toolbar-small-dialog-content,.google-visualization-toolbar-big-dialog-content{background-color:#fff;padding:4px;font-weight:normal;overflow:auto}.google-visualization-toolbar-small-dialog-title-close,.google-visualization-toolbar-big-dialog-title-close{background:transparent url(close_box.gif) no-repeat scroll center;height:15px;position:absolute;right:10px;top:8px;width:15px}.google-visualization-toolbar-small-dialog-content iframe,.google-visualization-toolbar-big-dialog-content iframe{width:500px;height:700px;border:1px solid black}.charts-inline-block{position:relative;display:-moz-inline-box;display:inline-block}* html .charts-inline-block,*:first-child+html .charts-inline-block{display:inline}.charts-menu{background:#fff;border-color:#ccc #666 #666 #ccc;border-style:solid;border-width:1px;cursor:default;font:normal 13px Arial,sans-serif;margin:0;outline:none;padding:4px 0;position:absolute;z-index:20000}.charts-menu-button{background:#ddd url(//ssl.gstatic.com/editor/button-bg.png) repeat-x top left;border:0;color:#000;cursor:pointer;list-style:none;margin:2px;outline:none;padding:0;text-decoration:none;vertical-align:middle}.charts-menu-button-outer-box,.charts-menu-button-inner-box{border-style:solid;border-color:#aaa;vertical-align:top}.charts-menu-button-outer-box{margin:0;border-width:1px 0;padding:0}.charts-menu-button-inner-box{margin:0 -1px;border-width:0 1px;padding:3px 4px}* html .charts-menu-button-inner-box{left:-1px}* html .charts-menu-button-rtl .charts-menu-button-outer-box{left:-1px;right:auto}* html .charts-menu-button-rtl .charts-menu-button-inner-box{right:auto}*:first-child+html .charts-menu-button-inner-box{left:-1px}*:first-child+html .charts-menu-button-rtl .charts-menu-button-inner-box{left:1px;right:auto}::root .charts-menu-button{line-height:0}::root .charts-menu-button-outer-box{line-height:0}::root .charts-menu-button-inner-box{line-height:0}::root .charts-menu-button-caption{line-height:normal}::root .charts-menu-button-dropdown{line-height:normal}.charts-menu-button-disabled{background-image:none!important;opacity:.3;-moz-opacity:.3;filter:alpha(opacity=30)}.charts-menu-button-disabled .charts-menu-button-outer-box,.charts-menu-button-disabled .charts-menu-button-inner-box,.charts-menu-button-disabled .charts-menu-button-caption,.charts-menu-button-disabled .charts-menu-button-dropdown{color:#333!important;border-color:#999!important}* html .charts-menu-button-disabled,*:first-child+html .charts-menu-button-disabled{margin:2px 1px!important;padding:0 1px!important}.charts-menu-button-hover .charts-menu-button-outer-box,.charts-menu-button-hover .charts-menu-button-inner-box{border-color:#9cf #69e #69e #7af!important}.charts-menu-button-active,.charts-menu-button-open{background-color:#bbb;background-position:bottom left}.charts-menu-button-focused .charts-menu-button-outer-box,.charts-menu-button-focused .charts-menu-button-inner-box{border-color:orange}.charts-menu-button-caption{padding:0 4px 0 0;vertical-align:top}.charts-menu-button-dropdown{height:15px;width:7px;background:url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -388px 0;vertical-align:top}.charts-menu-button-collapse-right,.charts-menu-button-collapse-right .charts-menu-button-outer-box,.charts-menu-button-collapse-right .charts-menu-button-inner-box{margin-right:0}.charts-menu-button-collapse-left,.charts-menu-button-collapse-left .charts-menu-button-outer-box{margin-left:0}.charts-menu-button-collapse-left .charts-menu-button-inner-box{margin-left:0;border-left:1px solid #fff}.charts-menu-button-collapse-left.charts-menu-button-checked .charts-menu-button-inner-box{border-left:1px solid #ddd}.charts-menuitem{color:#000;font:normal 13px Arial,sans-serif;list-style:none;margin:0;padding:4px 7em 4px 28px;white-space:nowrap}.charts-menuitem.charts-menuitem-rtl{padding-left:7em;padding-right:28px}.charts-menu-nocheckbox .charts-menuitem,.charts-menu-noicon .charts-menuitem{padding-left:12px}.charts-menu-noaccel .charts-menuitem{padding-right:20px}.charts-menuitem-content{color:#000;font:normal 13px Arial,sans-serif}.charts-menuitem-disabled .charts-menuitem-accel,.charts-menuitem-disabled .charts-menuitem-content{color:#ccc!important}.charts-menuitem-disabled .charts-menuitem-icon{opacity:.3;-moz-opacity:.3;filter:alpha(opacity=30)}.charts-menuitem-highlight,.charts-menuitem-hover{background-color:#d6e9f8;border-color:#d6e9f8;border-style:dotted;border-width:1px 0;padding-bottom:3px;padding-top:3px}.charts-menuitem-checkbox,.charts-menuitem-icon{background-repeat:no-repeat;height:16px;left:6px;position:absolute;right:auto;vertical-align:middle;width:16px}.charts-menuitem-rtl .charts-menuitem-checkbox,.charts-menuitem-rtl .charts-menuitem-icon{left:auto;right:6px}.charts-option-selected .charts-menuitem-checkbox,.charts-option-selected .charts-menuitem-icon{background:url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -512px 0}.charts-menuitem-accel{color:#999;direction:ltr;left:auto;padding:0 6px;position:absolute;right:0;text-align:right}.charts-menuitem-rtl .charts-menuitem-accel{left:0;right:auto;text-align:left}.charts-menuitem-mnemonic-hint{text-decoration:underline}.charts-menuitem-mnemonic-separator{color:#999;font-size:12px;padding-left:4px} 3 | 4 | -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/bootstrap-modal.js: -------------------------------------------------------------------------------- 1 | /* ========================================================= 2 | * bootstrap-modal.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#modals 4 | * ========================================================= 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================= */ 19 | 20 | 21 | !function( $ ){ 22 | 23 | "use strict" 24 | 25 | /* MODAL CLASS DEFINITION 26 | * ====================== */ 27 | 28 | var Modal = function ( content, options ) { 29 | this.options = options 30 | this.$element = $(content) 31 | .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this)) 32 | } 33 | 34 | Modal.prototype = { 35 | 36 | constructor: Modal 37 | 38 | , toggle: function () { 39 | return this[!this.isShown ? 'show' : 'hide']() 40 | } 41 | 42 | , show: function () { 43 | var that = this 44 | 45 | if (this.isShown) return 46 | 47 | $('body').addClass('modal-open') 48 | 49 | this.isShown = true 50 | this.$element.trigger('show') 51 | 52 | escape.call(this) 53 | backdrop.call(this, function () { 54 | var transition = $.support.transition && that.$element.hasClass('fade') 55 | 56 | !that.$element.parent().length && that.$element.appendTo(document.body) //don't move modals dom position 57 | 58 | that.$element 59 | .show() 60 | 61 | if (transition) { 62 | that.$element[0].offsetWidth // force reflow 63 | } 64 | 65 | that.$element.addClass('in') 66 | 67 | transition ? 68 | that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) : 69 | that.$element.trigger('shown') 70 | 71 | }) 72 | } 73 | 74 | , hide: function ( e ) { 75 | e && e.preventDefault() 76 | 77 | if (!this.isShown) return 78 | 79 | var that = this 80 | this.isShown = false 81 | 82 | $('body').removeClass('modal-open') 83 | 84 | escape.call(this) 85 | 86 | this.$element 87 | .trigger('hide') 88 | .removeClass('in') 89 | 90 | $.support.transition && this.$element.hasClass('fade') ? 91 | hideWithTransition.call(this) : 92 | hideModal.call(this) 93 | } 94 | 95 | } 96 | 97 | 98 | /* MODAL PRIVATE METHODS 99 | * ===================== */ 100 | 101 | function hideWithTransition() { 102 | var that = this 103 | , timeout = setTimeout(function () { 104 | that.$element.off($.support.transition.end) 105 | hideModal.call(that) 106 | }, 500) 107 | 108 | this.$element.one($.support.transition.end, function () { 109 | clearTimeout(timeout) 110 | hideModal.call(that) 111 | }) 112 | } 113 | 114 | function hideModal( that ) { 115 | this.$element 116 | .hide() 117 | .trigger('hidden') 118 | 119 | backdrop.call(this) 120 | } 121 | 122 | function backdrop( callback ) { 123 | var that = this 124 | , animate = this.$element.hasClass('fade') ? 'fade' : '' 125 | 126 | if (this.isShown && this.options.backdrop) { 127 | var doAnimate = $.support.transition && animate 128 | 129 | this.$backdrop = $('
  2. ' 254 | } 255 | 256 | $.fn.typeahead.Constructor = Typeahead 257 | 258 | 259 | /* TYPEAHEAD DATA-API 260 | * ================== */ 261 | 262 | $(function () { 263 | $('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) { 264 | var $this = $(this) 265 | if ($this.data('typeahead')) return 266 | e.preventDefault() 267 | $this.typeahead($this.data()) 268 | }) 269 | }) 270 | 271 | }( window.jQuery ); -------------------------------------------------------------------------------- /src/www/js/libs/bootstrap/js/bootstrap-tooltip.js: -------------------------------------------------------------------------------- 1 | /* =========================================================== 2 | * bootstrap-tooltip.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#tooltips 4 | * Inspired by the original jQuery.tipsy by Jason Frame 5 | * =========================================================== 6 | * Copyright 2012 Twitter, Inc. 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * ========================================================== */ 20 | 21 | !function( $ ) { 22 | 23 | "use strict" 24 | 25 | /* TOOLTIP PUBLIC CLASS DEFINITION 26 | * =============================== */ 27 | 28 | var Tooltip = function ( element, options ) { 29 | this.init('tooltip', element, options) 30 | } 31 | 32 | Tooltip.prototype = { 33 | 34 | constructor: Tooltip 35 | 36 | , init: function ( type, element, options ) { 37 | var eventIn 38 | , eventOut 39 | 40 | this.type = type 41 | this.$element = $(element) 42 | this.options = this.getOptions(options) 43 | this.enabled = true 44 | 45 | if (this.options.trigger != 'manual') { 46 | eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus' 47 | eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur' 48 | this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this)) 49 | this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this)) 50 | } 51 | 52 | this.options.selector ? 53 | (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : 54 | this.fixTitle() 55 | } 56 | 57 | , getOptions: function ( options ) { 58 | options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data()) 59 | 60 | if (options.delay && typeof options.delay == 'number') { 61 | options.delay = { 62 | show: options.delay 63 | , hide: options.delay 64 | } 65 | } 66 | 67 | return options 68 | } 69 | 70 | , enter: function ( e ) { 71 | var self = $(e.currentTarget)[this.type](this._options).data(this.type) 72 | 73 | if (!self.options.delay || !self.options.delay.show) { 74 | self.show() 75 | } else { 76 | self.hoverState = 'in' 77 | setTimeout(function() { 78 | if (self.hoverState == 'in') { 79 | self.show() 80 | } 81 | }, self.options.delay.show) 82 | } 83 | } 84 | 85 | , leave: function ( e ) { 86 | var self = $(e.currentTarget)[this.type](this._options).data(this.type) 87 | 88 | if (!self.options.delay || !self.options.delay.hide) { 89 | self.hide() 90 | } else { 91 | self.hoverState = 'out' 92 | setTimeout(function() { 93 | if (self.hoverState == 'out') { 94 | self.hide() 95 | } 96 | }, self.options.delay.hide) 97 | } 98 | } 99 | 100 | , show: function () { 101 | var $tip 102 | , inside 103 | , pos 104 | , actualWidth 105 | , actualHeight 106 | , placement 107 | , tp 108 | 109 | if (this.hasContent() && this.enabled) { 110 | $tip = this.tip() 111 | this.setContent() 112 | 113 | if (this.options.animation) { 114 | $tip.addClass('fade') 115 | } 116 | 117 | placement = typeof this.options.placement == 'function' ? 118 | this.options.placement.call(this, $tip[0], this.$element[0]) : 119 | this.options.placement 120 | 121 | inside = /in/.test(placement) 122 | 123 | $tip 124 | .remove() 125 | .css({ top: 0, left: 0, display: 'block' }) 126 | .appendTo(inside ? this.$element : document.body) 127 | 128 | pos = this.getPosition(inside) 129 | 130 | actualWidth = $tip[0].offsetWidth 131 | actualHeight = $tip[0].offsetHeight 132 | 133 | switch (inside ? placement.split(' ')[1] : placement) { 134 | case 'bottom': 135 | tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2} 136 | break 137 | case 'top': 138 | tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2} 139 | break 140 | case 'left': 141 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth} 142 | break 143 | case 'right': 144 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width} 145 | break 146 | } 147 | 148 | $tip 149 | .css(tp) 150 | .addClass(placement) 151 | .addClass('in') 152 | } 153 | } 154 | 155 | , setContent: function () { 156 | var $tip = this.tip() 157 | $tip.find('.tooltip-inner').html(this.getTitle()) 158 | $tip.removeClass('fade in top bottom left right') 159 | } 160 | 161 | , hide: function () { 162 | var that = this 163 | , $tip = this.tip() 164 | 165 | $tip.removeClass('in') 166 | 167 | function removeWithAnimation() { 168 | var timeout = setTimeout(function () { 169 | $tip.off($.support.transition.end).remove() 170 | }, 500) 171 | 172 | $tip.one($.support.transition.end, function () { 173 | clearTimeout(timeout) 174 | $tip.remove() 175 | }) 176 | } 177 | 178 | $.support.transition && this.$tip.hasClass('fade') ? 179 | removeWithAnimation() : 180 | $tip.remove() 181 | } 182 | 183 | , fixTitle: function () { 184 | var $e = this.$element 185 | if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') { 186 | $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title') 187 | } 188 | } 189 | 190 | , hasContent: function () { 191 | return this.getTitle() 192 | } 193 | 194 | , getPosition: function (inside) { 195 | return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), { 196 | width: this.$element[0].offsetWidth 197 | , height: this.$element[0].offsetHeight 198 | }) 199 | } 200 | 201 | , getTitle: function () { 202 | var title 203 | , $e = this.$element 204 | , o = this.options 205 | 206 | title = $e.attr('data-original-title') 207 | || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) 208 | 209 | title = (title || '').toString().replace(/(^\s*|\s*$)/, "") 210 | 211 | return title 212 | } 213 | 214 | , tip: function () { 215 | return this.$tip = this.$tip || $(this.options.template) 216 | } 217 | 218 | , validate: function () { 219 | if (!this.$element[0].parentNode) { 220 | this.hide() 221 | this.$element = null 222 | this.options = null 223 | } 224 | } 225 | 226 | , enable: function () { 227 | this.enabled = true 228 | } 229 | 230 | , disable: function () { 231 | this.enabled = false 232 | } 233 | 234 | , toggleEnabled: function () { 235 | this.enabled = !this.enabled 236 | } 237 | 238 | , toggle: function () { 239 | this[this.tip().hasClass('in') ? 'hide' : 'show']() 240 | } 241 | 242 | } 243 | 244 | 245 | /* TOOLTIP PLUGIN DEFINITION 246 | * ========================= */ 247 | 248 | $.fn.tooltip = function ( option ) { 249 | return this.each(function () { 250 | var $this = $(this) 251 | , data = $this.data('tooltip') 252 | , options = typeof option == 'object' && option 253 | if (!data) $this.data('tooltip', (data = new Tooltip(this, options))) 254 | if (typeof option == 'string') data[option]() 255 | }) 256 | } 257 | 258 | $.fn.tooltip.Constructor = Tooltip 259 | 260 | $.fn.tooltip.defaults = { 261 | animation: true 262 | , delay: 0 263 | , selector: false 264 | , placement: 'top' 265 | , trigger: 'hover' 266 | , title: '' 267 | , template: '
    ' 268 | } 269 | 270 | }( window.jQuery ); --------------------------------------------------------------------------------