├── .gitignore
├── CHANGELOG
├── CONTRIBUTORS
├── COPYING
├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile
├── config.ru
├── config
└── gdash.yaml-sample
├── gdash.gemspec
├── graph_templates
├── dashboards
│ └── README.md
└── node_templates
│ └── collectd
│ ├── cpu-average.graph
│ ├── cpu-max.graph
│ ├── dash.yaml
│ ├── disk-IO.graph
│ ├── disk-usage.graph
│ ├── entropy.graph
│ ├── fork_rate.graph
│ ├── load.graph
│ ├── memory-usage.graph
│ ├── network.eth0.errors.graph
│ ├── network.eth0.trafic.graph
│ ├── network.eth1.errors.graph
│ ├── network.eth1.trafic.graph
│ ├── processes.graph
│ ├── uptime.graph
│ └── users.graph
├── lib
├── gdash.rb
└── gdash
│ ├── dashboard.rb
│ ├── monkey_patches.rb
│ └── sinatra_app.rb
├── public
├── img
│ ├── glyphicons-halflings-white.png
│ └── glyphicons-halflings.png
└── js
│ ├── bootstrap-alert.js
│ ├── bootstrap-alerts.js
│ ├── bootstrap-button.js
│ ├── bootstrap-carousel.js
│ ├── bootstrap-collapse.js
│ ├── bootstrap-dropdown.js
│ ├── bootstrap-modal.js
│ ├── bootstrap-popover.js
│ ├── bootstrap-scrollspy.js
│ ├── bootstrap-tab.js
│ ├── bootstrap-tabs.js
│ ├── bootstrap-tooltip.js
│ ├── bootstrap-transition.js
│ ├── bootstrap-twipsy.js
│ ├── bootstrap-typeahead.js
│ ├── jquery-1.7.2.min.js
│ ├── jquery-ui-1.10.0.custom.min.js
│ ├── jquery-ui-sliderAccess.js
│ ├── jquery-ui-timepicker-addon.css
│ ├── jquery-ui-timepicker-addon.js
│ ├── jquery.cookie.js
│ ├── jquery.tablesorter.min.js
│ └── selectdate.js
├── sample
├── README.md
├── email-full-screen.png
├── email.png
└── email
│ ├── cpu.graph
│ ├── dash.yaml
│ ├── io.graph
│ ├── load.graph
│ └── network.graph
├── tools
└── dashboards-validation.rb
└── views
├── README.md
├── _interval_filter.erb
├── bootstrap
├── accordion.less
├── alerts.less
├── bootstrap.less
├── breadcrumbs.less
├── button-groups.less
├── buttons.less
├── carousel.less
├── close.less
├── code.less
├── component-animations.less
├── dropdowns.less
├── forms.less
├── grid.less
├── hero-unit.less
├── labels-badges.less
├── layouts.less
├── mixins.less
├── modals.less
├── navbar.less
├── navs.less
├── pager.less
├── pagination.less
├── popovers.less
├── progress-bars.less
├── reset.less
├── responsive-1200px-min.less
├── responsive-767px-max.less
├── responsive-768px-979px.less
├── responsive-navbar.less
├── responsive-utilities.less
├── responsive.less
├── scaffolding.less
├── sprites.less
├── tables.less
├── tests
│ ├── css-tests.css
│ ├── css-tests.html
│ ├── forms.html
│ └── navbar.html
├── thumbnails.less
├── tooltip.less
├── type.less
├── utilities.less
├── variables.less
└── wells.less
├── dashboard.erb
├── detailed_dashboard.erb
├── full_size_dashboard.erb
├── graph.erb
├── index.erb
├── layout.erb
├── print_dashboard.erb
└── print_detailed_dashboard.erb
/.gitignore:
--------------------------------------------------------------------------------
1 | config/gdash.yaml
2 | graph_templates
3 |
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | 2012/02/11 - Support time interval views for any graph
2 | 2011/11/12 - Include the uncompiled less files and a js script to compile on demand
3 | making it easier to customize the look and feel of gdash - thanks Joe Miller
4 | 2011/10/18 - Multiple top level categories are supported
5 | 2011/10/16 - The settings that used to be in config.ru is now in a config file
6 | 2011/10/10 - Add a full screen dashboard mode
7 |
--------------------------------------------------------------------------------
/CONTRIBUTORS:
--------------------------------------------------------------------------------
1 | * Nathan Haneysmith - Added support for multiple top level categories
2 | * Elmer Rivera - Made the config.ru configurable
3 | * Jeremy Carroll - Bug fixes to the top level menu
4 | * @nstielau - Use absolute paths in views
5 | * Jason Dixon - Fixed spelling errors in code
6 | * Pierre-Yves Ritschard - Time interval graphs
7 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'http://rubygems.org'
2 | gem 'sinatra'
3 | gem 'redcarpet'
4 | gem 'less'
5 | gem 'therubyracer'
6 | gem 'json'
7 | gem 'graphite_graph', "~>0.0.8"
8 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: http://rubygems.org/
3 | specs:
4 | commonjs (0.2.6)
5 | graphite_graph (0.0.8)
6 | json (1.7.7)
7 | less (2.2.1)
8 | commonjs (~> 0.2.6)
9 | libv8 (3.3.10.4)
10 | rack (1.4.5)
11 | rack-protection (1.2.0)
12 | rack
13 | redcarpet (2.1.1)
14 | sinatra (1.3.3)
15 | rack (~> 1.3, >= 1.3.6)
16 | rack-protection (~> 1.2)
17 | tilt (~> 1.3, >= 1.3.3)
18 | therubyracer (0.10.1)
19 | libv8 (~> 3.3.10)
20 | tilt (1.3.3)
21 |
22 | PLATFORMS
23 | ruby
24 |
25 | DEPENDENCIES
26 | graphite_graph (~> 0.0.8)
27 | json
28 | less
29 | redcarpet
30 | sinatra
31 | therubyracer
32 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rake/clean'
2 | require 'rubygems'
3 | require 'rubygems/package_task'
4 |
5 | spec = eval(File.read('gdash.gemspec'))
6 |
7 | Gem::PackageTask.new(spec) do |pkg|
8 | end
9 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | $: << File.join(File.dirname(__FILE__), "lib")
2 |
3 | require 'bundler/setup'
4 |
5 | require 'gdash'
6 |
7 | set :run, false
8 |
9 | config = YAML.load_file(File.expand_path("../config/gdash.yaml", __FILE__))
10 |
11 | # If you want basic HTTP authentication
12 | # include :username and :password in gdash.yaml
13 | if config[:username] && config[:password]
14 | use Rack::Auth::Basic do |username, password|
15 | username == config[:username] && password == config[:password]
16 | end
17 | end
18 |
19 | run GDash::SinatraApp.new(config[:graphite], config[:templatedir], config[:options])
20 |
--------------------------------------------------------------------------------
/config/gdash.yaml-sample:
--------------------------------------------------------------------------------
1 | :graphite: http://graphite.example.net
2 | :templatedir: /path/to/my/graph/templates
3 | #:username: admin
4 | #:password: secret
5 | :options:
6 | :title: My Dashboard
7 | :prefix: ""
8 | :refresh_rate: 60
9 | :graph_columns: 2
10 | :graph_width: 500
11 | :graph_height: 250
12 | :interval_filters:
13 | - :label: Last Hour
14 | :from: -1hour
15 | :to: now
16 | - :label: Last Day
17 | :from: -1day
18 | - :label: Last Week
19 | :from: -1week
20 | - :label: Last Month
21 | :from: -1month
22 | - :label: Last Year
23 | :from: -1year
24 | :intervals:
25 | - [ "-1hour", "1 hour" ]
26 | - [ "-2hour", "2 hour" ]
27 | - [ "-1day", "1 day" ]
28 | - [ "-1month", "1 month" ]
29 | - [ "-1year", "1 year" ]
30 |
--------------------------------------------------------------------------------
/gdash.gemspec:
--------------------------------------------------------------------------------
1 | spec = Gem::Specification.new do |s|
2 | s.name = 'gdash'
3 | s.version = "0.0.5"
4 | s.author = 'R.I.Pienaar'
5 | s.email = 'rip@devco.net'
6 | s.homepage = 'http://devco.net/'
7 | s.platform = Gem::Platform::RUBY
8 | s.summary = 'Graphite Dashboard'
9 | s.description = "A simple dashboard for creating and displaying Graphite graphs"
10 | # Add your other files here if you make them
11 | s.files = FileList["{README.md,COPYING,CONTRIBUTORS,bin,lib,public,views,sample,Gemfile,Gemfile.lock}/**/*"].to_a
12 | s.require_paths << 'lib'
13 | s.has_rdoc = false
14 | s.add_development_dependency('rake')
15 | s.add_development_dependency('rdoc')
16 | s.add_dependency 'graphite_graph'
17 | s.add_dependency 'sinatra'
18 | s.add_dependency 'redcarpet'
19 | end
20 |
--------------------------------------------------------------------------------
/graph_templates/dashboards/README.md:
--------------------------------------------------------------------------------
1 | Placeholder for dashboards
2 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/cpu-average.graph:
--------------------------------------------------------------------------------
1 | title "CPU Usage: Average for all CPU"
2 | vtitle "percent"
3 | area :stacked
4 | description "The combined CPU usage for node"
5 | hide_legend false
6 |
7 | field :iowait, :color => "red",
8 | :alias => "IO Wait",
9 | :data => "averageSeries(node.cpu.*.wait)"
10 |
11 | field :system, :color => "orange",
12 | :alias => "System",
13 | :data => "averageSeries(node.cpu.*.system)"
14 |
15 | field :user, :color => "yellow",
16 | :alias => "User",
17 | :data => "averageSeries(node.cpu.*.user)"
18 |
19 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/cpu-max.graph:
--------------------------------------------------------------------------------
1 | title "CPU Usage: Value for the most used CPU"
2 | vtitle "percent"
3 | area :stacked
4 | description "The combined CPU usage for node"
5 | hide_legend false
6 |
7 | field :iowait, :color => "red",
8 | :alias => "IO Wait",
9 | :data => "maxSeries(node.cpu.*.wait)"
10 |
11 | field :system, :color => "orange",
12 | :alias => "System",
13 | :data => "maxSeries(node.cpu.*.system)"
14 |
15 | field :user, :color => "yellow",
16 | :alias => "User",
17 | :data => "maxSeries(node.cpu.*.user)"
18 |
19 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/dash.yaml:
--------------------------------------------------------------------------------
1 | :name: node example
2 | :description: The cpu, load, etc. usage for node
3 |
4 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/disk-IO.graph:
--------------------------------------------------------------------------------
1 | title "Disk Usage (in percent)"
2 | area :none
3 | description "Disk usage (in percent)"
4 | hide_legend false
5 | linewidth 3
6 |
7 | field :opt, :color => "red",
8 | :alias => "/opt",
9 | :data => "asPercent(node.df.opt.used,sumSeries(node.df.opt.used, node.df.opt.free))"
10 |
11 | field :var, :color => "orange",
12 | :alias => "/var",
13 | :data => "asPercent(node.df.var.used,sumSeries(node.df.var.used, node.df.var.free))"
14 |
15 | field :tmp, :color => "yellow",
16 | :alias => "/tmp",
17 | :data => "asPercent(node.df.tmp.used,sumSeries(node.df.tmp.used, node.df.tmp.free))"
18 |
19 | field :home, :color => "blue",
20 | :alias => "/home",
21 | :data => "asPercent(node.df.home.used,sumSeries(node.df.home.used, node.df.home.free))"
22 |
23 | field :root, :color => "green",
24 | :alias => "/root",
25 | :data => "asPercent(node.df.root.used,sumSeries(node.df.root.used, node.df.root.free))"
26 |
27 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/disk-usage.graph:
--------------------------------------------------------------------------------
1 | title "Disk Usage (in percent)"
2 | area :none
3 | description "Disk usage (in percent)"
4 | hide_legend false
5 | linewidth 3
6 |
7 | field :opt, :color => "red",
8 | :alias => "/opt",
9 | :data => "asPercent(node.df.opt.used,sumSeries(node.df.opt.used, l52eatsrv-cs1.df.opt.free))"
10 |
11 | field :var, :color => "orange",
12 | :alias => "/var",
13 | :data => "asPercent(node.df.var.used,sumSeries(node.df.var.used, l52eatsrv-cs1.df.var.free))"
14 |
15 | field :tmp, :color => "yellow",
16 | :alias => "/tmp",
17 | :data => "asPercent(node.df.tmp.used,sumSeries(node.df.tmp.used, l52eatsrv-cs1.df.tmp.free))"
18 |
19 | field :home, :color => "blue",
20 | :alias => "/home",
21 | :data => "asPercent(node.df.home.used,sumSeries(node.df.home.used, l52eatsrv-cs1.df.home.free))"
22 |
23 | field :root, :color => "green",
24 | :alias => "/root",
25 | :data => "asPercent(node.df.root.used,sumSeries(node.df.root.used, l52eatsrv-cs1.df.root.free))"
26 |
27 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/entropy.graph:
--------------------------------------------------------------------------------
1 | title "Entropy"
2 | area :all
3 | hide_legend false
4 |
5 | field :entropy, :alias => "Entropy",
6 | :data => "node.entropy.entropy.entropy"
7 |
8 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/fork_rate.graph:
--------------------------------------------------------------------------------
1 | title "Fork rate"
2 | area :all
3 | hide_legend false
4 |
5 | field :fork, :alias => "Fork rate",
6 | :data => "node.processes.fork_rate"
7 |
8 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/load.graph:
--------------------------------------------------------------------------------
1 | title "Load"
2 | area :none
3 | description "The combined CPU usage for node"
4 | hide_legend false
5 |
6 | field :short, :color => "red",
7 | :alias => "Short term (1 minute)",
8 | :data => "node.load.shortterm"
9 |
10 | field :mid, :color => "orange",
11 | :alias => "Mid term (5 minutes)",
12 | :data => "node.load.midterm"
13 |
14 | field :long, :color => "yellow",
15 | :alias => "Long term (15 minutes)",
16 | :data => "node.load.longterm"
17 |
18 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/memory-usage.graph:
--------------------------------------------------------------------------------
1 | title "RAM Usage (in percent)"
2 | area :stacked
3 | description "RAM usage (in percent)"
4 | hide_legend false
5 |
6 | field :use, :color => "red",
7 | :alias => "used by application",
8 | :data => "asPercent(diffSeries(node.memory.used,node.memory.cached,node.memory.buffered),sumSeries(node.memory.used, node.memory.free))"
9 | /
10 |
11 | field :buffer, :color => "orange",
12 | :alias => "buffer",
13 | :data => "asPercent(node.memory.buffered,sumSeries(node.memory.used, node.memory.free))"
14 | /
15 |
16 | field :cache, :color => "yellow",
17 | :alias => "cache",
18 | :data => "asPercent(node.memory.cached,sumSeries(node.memory.used, node.memory.free)"
19 | /
20 |
21 | field :free, :color => "green",
22 | :alias => "free",
23 | :data => "asPercent(node.memory.free,sumSeries(node.memory.used, node.memory.free))"
24 | /
25 |
26 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/network.eth0.errors.graph:
--------------------------------------------------------------------------------
1 | title "Errors on eth0"
2 | area :first
3 | hide_legend false
4 |
5 | field :receiving, :color => "green",
6 | :alias => "Receiving",
7 | :data => "node.interface.errors.eth0.rx"
8 |
9 | field :transmitting, :color => "blue",
10 | :alias => "Transmiting",
11 | :data => "node.interface.errors.eth0.tx"
12 |
13 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/network.eth0.trafic.graph:
--------------------------------------------------------------------------------
1 | title "Traffic on eth0"
2 | area :first
3 | hide_legend false
4 |
5 | field :receiving, :color => "green",
6 | :alias => "Receiving",
7 | :data => "node.interface.packets.eth0.rx"
8 |
9 | field :transmitting, :color => "blue",
10 | :alias => "Transmiting",
11 | :data => "node.interface.packets.eth0.tx"
12 |
13 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/network.eth1.errors.graph:
--------------------------------------------------------------------------------
1 | title "Errors on eth1"
2 | area :first
3 | hide_legend false
4 |
5 | field :receiving, :color => "green",
6 | :alias => "Receiving",
7 | :data => "node.interface.errors.eth1.rx"
8 |
9 | field :transmitting, :color => "blue",
10 | :alias => "Transmiting",
11 | :data => "node.interface.errors.eth1.tx"
12 |
13 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/network.eth1.trafic.graph:
--------------------------------------------------------------------------------
1 | title "Traffic on eth1"
2 | area :first
3 | hide_legend false
4 |
5 | field :receiving, :color => "green",
6 | :alias => "Receiving",
7 | :data => "node.interface.packets.eth1.rx"
8 |
9 | field :transmitting, :color => "blue",
10 | :alias => "Transmiting",
11 | :data => "node.interface.packets.eth1.tx"
12 |
13 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/processes.graph:
--------------------------------------------------------------------------------
1 | title "Processes"
2 | vtitle "Number of processes"
3 | area :stacked
4 | description "Number of processes for node"
5 | hide_legend false
6 |
7 | field :paging, :color => "purple",
8 | :alias => "paging",
9 | :data => "node.processes.ps_state.paging"
10 |
11 | field :zombie, :color => "white",
12 | :alias => "zombies",
13 | :data => "node.processes.ps_state.zombie"
14 |
15 | field :uninterruptible, :color => "orange",
16 | :alias => "uninteruptible",
17 | :data => "node.processes.ps_state.blocked"
18 |
19 | field :dead, :color => "red",
20 | :alias => "dead",
21 | :data => "node.processes.ps_state.stopped"
22 |
23 | field :sleeping, :color => "blue",
24 | :alias => "sleeping",
25 | :data => "node.processes.ps_state.sleeping"
26 |
27 | field :runnable, :color => "green",
28 | :alias => "runnable",
29 | :data => "node.processes.ps_state.running"
30 |
31 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/uptime.graph:
--------------------------------------------------------------------------------
1 | title "Uptime"
2 | area :all
3 | hide_legend false
4 |
5 | field :uptime, :alias => "Uptime",
6 | :data => "scale(node.uptime,0.000011574)"
7 |
--------------------------------------------------------------------------------
/graph_templates/node_templates/collectd/users.graph:
--------------------------------------------------------------------------------
1 | title "User connected"
2 | area :all
3 | hide_legend false
4 |
5 | field :users, :alias => "Users",
6 | :data => "node.users.users.users"
7 |
--------------------------------------------------------------------------------
/lib/gdash.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra'
3 | require 'yaml'
4 | require 'erb'
5 | require 'redcarpet'
6 | require 'less'
7 |
8 | class GDash
9 | require 'gdash/dashboard'
10 | require 'gdash/monkey_patches'
11 | require 'gdash/sinatra_app'
12 | require 'graphite_graph'
13 |
14 | attr_reader :graphite_base, :graphite_render, :graph_templates, :category, :dash_templates, :height, :width, :from, :until
15 |
16 | def initialize(graphite_base, render_url, graph_templates, category, options={})
17 | @graphite_base = graphite_base
18 | @graphite_render = [@graphite_base, "/render/"].join
19 | @graph_templates = graph_templates
20 | @category = category
21 | @dash_templates = File.join(graph_templates, category)
22 | @height = options.delete(:height)
23 | @width = options.delete(:width)
24 | @from = options.delete(:from)
25 | @until = options.delete(:until)
26 |
27 | raise "Dashboard templates directory #{@dash_templates} does not exist" unless File.directory?(@dash_templates)
28 | end
29 |
30 | def dashboard(name, options={})
31 | options[:width] ||= @width
32 | options[:height] ||= @height
33 | options[:from] ||= @from
34 | options[:until] ||= @until
35 |
36 |
37 | Dashboard.new(name, graph_templates, category, options, graphite_render)
38 | end
39 |
40 | def list
41 | dashboards.map {|dash| dash[:link]}
42 | end
43 |
44 | def dashboards
45 | dashboards = []
46 |
47 | Dir.entries(dash_templates).each do |dash|
48 | begin
49 | yaml_file = File.join(dash_templates, dash, "dash.yaml")
50 | if File.exist?(yaml_file)
51 | dashboards << YAML.load_file(yaml_file).merge({:category => category, :link => dash})
52 | end
53 | rescue Exception => e
54 | p e
55 | end
56 | end
57 |
58 | dashboards.sort_by{|d| d[:name].to_s}
59 | end
60 | end
61 |
--------------------------------------------------------------------------------
/lib/gdash/dashboard.rb:
--------------------------------------------------------------------------------
1 | class GDash
2 | class Dashboard
3 | attr_accessor :properties
4 |
5 | def initialize(short_name, graph_templates, category, options={}, graphite_render="")
6 |
7 | @properties = {:graph_width => nil,
8 | :graph_height => nil,
9 | :graph_from => nil,
10 | :graph_until => nil}
11 |
12 | @properties[:short_name] = short_name
13 | @properties[:graph_templates] = graph_templates
14 | @properties[:category] = category
15 | @properties[:directory] = File.join(graph_templates, category, short_name)
16 | @properties[:graphite_render] = graphite_render
17 |
18 | raise "Cannot find dashboard directory #{directory}" unless File.directory?(directory)
19 |
20 | @properties[:yaml] = File.join(directory, "dash.yaml")
21 |
22 | raise "Cannot find YAML file #{yaml}" unless File.exists?(yaml)
23 |
24 |
25 | @properties.merge!(YAML.load_file(yaml))
26 |
27 | if @properties[:include_properties] == nil || @properties[:include_properties].empty?
28 | property_includes = []
29 | elsif @properties[:include_properties].is_a? Array
30 | property_includes = @properties[:include_properties]
31 | elsif @properties[:include_properties].is_a? String
32 | property_includes = [@properties[:include_properties]]
33 | else
34 | raise "Invalid value for include_properties in #{File.join(directory, 'dash.yaml')}"
35 | end
36 |
37 | property_includes << options[:include_properties] if options[:include_properties]
38 |
39 | for property_file in property_includes
40 | yaml_file = File.join(graph_templates, property_file)
41 | if File.exist?(yaml_file)
42 | @properties.rmerge!(YAML.load_file(yaml_file))
43 | end
44 | end
45 |
46 | # Properties defined in dashboard config file are overridden when given on initialization
47 | @properties.rmerge!(options)
48 | @properties[:graph_width] = options.delete(:width) || graph_width
49 | @properties[:graph_height] = options.delete(:height) || graph_height
50 | @properties[:graph_from] = options.delete(:from) || graph_from
51 | @properties[:graph_until] = options.delete(:until) || graph_until
52 |
53 | if @properties[:include_graphs] == nil || @properties[:include_graphs].empty?
54 | graph_includes = []
55 | elsif @properties[:include_graphs].is_a? Array
56 | graph_includes = @properties[:include_graphs]
57 | elsif @properties[:include_graphs].is_a? String
58 | graph_includes = [@properties[:include_graphs]]
59 | else
60 | raise "Invalid value for include in #{File.join(directory, 'dash.yaml')}"
61 | end
62 |
63 | @directories = graph_includes.map { |d|
64 | File.join(graph_templates, d)
65 | }
66 | @directories << directory
67 |
68 | #Graphite defined in gdash.yaml is overwritten if set in dash.yaml
69 | if !(@properties[:graphite] == nil || @properties[:graphite].empty?)
70 | @properties[:graphite_render] = @properties[:graphite]+"/render"
71 | end
72 | end
73 |
74 | def list_graphs
75 | graphs = {}
76 | @directories.each { |directory|
77 | current_graphs = Dir.entries(directory).select {|f| f.match(/\.graph$/)}
78 | current_graphs.each { |graph_filename|
79 | graph_name = File.basename(graph_filename, ".graph")
80 | graphs[graph_name] = File.join(directory, graph_filename)
81 | }
82 | }
83 | graphs
84 | end
85 |
86 | def graphs(options={})
87 | options[:width] ||= graph_width
88 | options[:height] ||= graph_height
89 | options[:from] ||= graph_from
90 | options[:until] ||= graph_until
91 |
92 | overrides = options.reject { |k,v| v.nil? }
93 | overrides = overrides.merge!(@properties[:graph_properties]) if @properties[:graph_properties]
94 | overrides = overrides.merge!(options[:placeholders]) if options[:placeholders]
95 |
96 | graphs = list_graphs
97 |
98 | graphs.keys.sort.map do |graph_name|
99 | {:name => graph_name,
100 | :graphite => GraphiteGraph.new(graphs[graph_name], overrides)}
101 | end
102 | end
103 |
104 | def graph_by_name(name, options={})
105 | options[:width] ||= graph_width
106 | options[:height] ||= graph_height
107 | options[:from] ||= graph_from
108 | options[:until] ||= graph_until
109 |
110 | overrides = options.reject { |k,v| v.nil? }
111 | overrides = overrides.merge!(@properties[:graph_properties]) if @properties[:graph_properties]
112 | overrides = overrides.merge!(options[:placeholders]) if options[:placeholders]
113 |
114 | graphs = list_graphs
115 |
116 | {:name => name, :graphite => GraphiteGraph.new(graphs[name], overrides)}
117 | end
118 |
119 | def method_missing(method, *args)
120 | if properties.include?(method)
121 | properties[method]
122 | else
123 | super
124 | end
125 | end
126 | end
127 | end
128 |
--------------------------------------------------------------------------------
/lib/gdash/monkey_patches.rb:
--------------------------------------------------------------------------------
1 | class GraphiteGraph
2 | attr_accessor :properties, :file
3 | end
4 |
5 | class Hash
6 | def rmerge!(other_hash)
7 | merge!(other_hash) do |key, oldval, newval|
8 | oldval.class == self.class ? oldval.rmerge!(newval) : newval
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/public/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ripienaar/gdash/0f334f006b39c6f72b5e496fd8ed0f203aa4495f/public/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/public/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ripienaar/gdash/0f334f006b39c6f72b5e496fd8ed0f203aa4495f/public/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/public/js/bootstrap-alert.js:
--------------------------------------------------------------------------------
1 | /* ==========================================================
2 | * bootstrap-alert.js v2.0.4
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"; // jshint ;_;
24 |
25 |
26 | /* ALERT CLASS DEFINITION
27 | * ====================== */
28 |
29 | var dismiss = '[data-dismiss="alert"]'
30 | , Alert = function (el) {
31 | $(el).on('click', dismiss, this.close)
32 | }
33 |
34 | Alert.prototype.close = function (e) {
35 | var $this = $(this)
36 | , selector = $this.attr('data-target')
37 | , $parent
38 |
39 | if (!selector) {
40 | selector = $this.attr('href')
41 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
42 | }
43 |
44 | $parent = $(selector)
45 |
46 | e && e.preventDefault()
47 |
48 | $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
49 |
50 | $parent.trigger(e = $.Event('close'))
51 |
52 | if (e.isDefaultPrevented()) return
53 |
54 | $parent.removeClass('in')
55 |
56 | function removeElement() {
57 | $parent
58 | .trigger('closed')
59 | .remove()
60 | }
61 |
62 | $.support.transition && $parent.hasClass('fade') ?
63 | $parent.on($.support.transition.end, removeElement) :
64 | removeElement()
65 | }
66 |
67 |
68 | /* ALERT PLUGIN DEFINITION
69 | * ======================= */
70 |
71 | $.fn.alert = function (option) {
72 | return this.each(function () {
73 | var $this = $(this)
74 | , data = $this.data('alert')
75 | if (!data) $this.data('alert', (data = new Alert(this)))
76 | if (typeof option == 'string') data[option].call($this)
77 | })
78 | }
79 |
80 | $.fn.alert.Constructor = Alert
81 |
82 |
83 | /* ALERT DATA-API
84 | * ============== */
85 |
86 | $(function () {
87 | $('body').on('click.alert.data-api', dismiss, Alert.prototype.close)
88 | })
89 |
90 | }(window.jQuery);
--------------------------------------------------------------------------------
/public/js/bootstrap-alerts.js:
--------------------------------------------------------------------------------
1 | /* ==========================================================
2 | * bootstrap-alerts.js v1.3.0
3 | * http://twitter.github.com/bootstrap/javascript.html#alerts
4 | * ==========================================================
5 | * Copyright 2011 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 | /* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
24 | * ======================================================= */
25 |
26 | var transitionEnd
27 |
28 | $(document).ready(function () {
29 |
30 | $.support.transition = (function () {
31 | var thisBody = document.body || document.documentElement
32 | , thisStyle = thisBody.style
33 | , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
34 | return support
35 | })()
36 |
37 | // set CSS transition event type
38 | if ( $.support.transition ) {
39 | transitionEnd = "TransitionEnd"
40 | if ( $.browser.webkit ) {
41 | transitionEnd = "webkitTransitionEnd"
42 | } else if ( $.browser.mozilla ) {
43 | transitionEnd = "transitionend"
44 | } else if ( $.browser.opera ) {
45 | transitionEnd = "oTransitionEnd"
46 | }
47 | }
48 |
49 | })
50 |
51 | /* ALERT CLASS DEFINITION
52 | * ====================== */
53 |
54 | var Alert = function ( content, selector ) {
55 | this.$element = $(content)
56 | .delegate(selector || '.close', 'click', this.close)
57 | }
58 |
59 | Alert.prototype = {
60 |
61 | close: function (e) {
62 | var $element = $(this).parent('.alert-message')
63 |
64 | e && e.preventDefault()
65 | $element.removeClass('in')
66 |
67 | function removeElement () {
68 | $element.remove()
69 | }
70 |
71 | $.support.transition && $element.hasClass('fade') ?
72 | $element.bind(transitionEnd, removeElement) :
73 | removeElement()
74 | }
75 |
76 | }
77 |
78 |
79 | /* ALERT PLUGIN DEFINITION
80 | * ======================= */
81 |
82 | $.fn.alert = function ( options ) {
83 |
84 | if ( options === true ) {
85 | return this.data('alert')
86 | }
87 |
88 | return this.each(function () {
89 | var $this = $(this)
90 |
91 | if ( typeof options == 'string' ) {
92 | return $this.data('alert')[options]()
93 | }
94 |
95 | $(this).data('alert', new Alert( this ))
96 |
97 | })
98 | }
99 |
100 | $(document).ready(function () {
101 | new Alert($('body'), '.alert-message[data-alert] .close')
102 | })
103 |
104 | }( window.jQuery || window.ender );
--------------------------------------------------------------------------------
/public/js/bootstrap-button.js:
--------------------------------------------------------------------------------
1 | /* ============================================================
2 | * bootstrap-button.js v2.0.4
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 |
21 | !function ($) {
22 |
23 | "use strict"; // jshint ;_;
24 |
25 |
26 | /* BUTTON PUBLIC CLASS DEFINITION
27 | * ============================== */
28 |
29 | var Button = function (element, options) {
30 | this.$element = $(element)
31 | this.options = $.extend({}, $.fn.button.defaults, options)
32 | }
33 |
34 | Button.prototype.setState = function (state) {
35 | var d = 'disabled'
36 | , $el = this.$element
37 | , data = $el.data()
38 | , val = $el.is('input') ? 'val' : 'html'
39 |
40 | state = state + 'Text'
41 | data.resetText || $el.data('resetText', $el[val]())
42 |
43 | $el[val](data[state] || this.options[state])
44 |
45 | // push to event loop to allow forms to submit
46 | setTimeout(function () {
47 | state == 'loadingText' ?
48 | $el.addClass(d).attr(d, d) :
49 | $el.removeClass(d).removeAttr(d)
50 | }, 0)
51 | }
52 |
53 | Button.prototype.toggle = function () {
54 | var $parent = this.$element.parent('[data-toggle="buttons-radio"]')
55 |
56 | $parent && $parent
57 | .find('.active')
58 | .removeClass('active')
59 |
60 | this.$element.toggleClass('active')
61 | }
62 |
63 |
64 | /* BUTTON PLUGIN DEFINITION
65 | * ======================== */
66 |
67 | $.fn.button = function (option) {
68 | return this.each(function () {
69 | var $this = $(this)
70 | , data = $this.data('button')
71 | , options = typeof option == 'object' && option
72 | if (!data) $this.data('button', (data = new Button(this, options)))
73 | if (option == 'toggle') data.toggle()
74 | else if (option) data.setState(option)
75 | })
76 | }
77 |
78 | $.fn.button.defaults = {
79 | loadingText: 'loading...'
80 | }
81 |
82 | $.fn.button.Constructor = Button
83 |
84 |
85 | /* BUTTON DATA-API
86 | * =============== */
87 |
88 | $(function () {
89 | $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) {
90 | var $btn = $(e.target)
91 | if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
92 | $btn.button('toggle')
93 | })
94 | })
95 |
96 | }(window.jQuery);
--------------------------------------------------------------------------------
/public/js/bootstrap-carousel.js:
--------------------------------------------------------------------------------
1 | /* ==========================================================
2 | * bootstrap-carousel.js v2.0.4
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"; // jshint ;_;
24 |
25 |
26 | /* CAROUSEL CLASS DEFINITION
27 | * ========================= */
28 |
29 | var Carousel = function (element, options) {
30 | this.$element = $(element)
31 | this.options = options
32 | this.options.slide && this.slide(this.options.slide)
33 | this.options.pause == 'hover' && this.$element
34 | .on('mouseenter', $.proxy(this.pause, this))
35 | .on('mouseleave', $.proxy(this.cycle, this))
36 | }
37 |
38 | Carousel.prototype = {
39 |
40 | cycle: function (e) {
41 | if (!e) this.paused = false
42 | this.options.interval
43 | && !this.paused
44 | && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
45 | return this
46 | }
47 |
48 | , to: function (pos) {
49 | var $active = this.$element.find('.active')
50 | , children = $active.parent().children()
51 | , activePos = children.index($active)
52 | , that = this
53 |
54 | if (pos > (children.length - 1) || pos < 0) return
55 |
56 | if (this.sliding) {
57 | return this.$element.one('slid', function () {
58 | that.to(pos)
59 | })
60 | }
61 |
62 | if (activePos == pos) {
63 | return this.pause().cycle()
64 | }
65 |
66 | return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos]))
67 | }
68 |
69 | , pause: function (e) {
70 | if (!e) this.paused = true
71 | clearInterval(this.interval)
72 | this.interval = null
73 | return this
74 | }
75 |
76 | , next: function () {
77 | if (this.sliding) return
78 | return this.slide('next')
79 | }
80 |
81 | , prev: function () {
82 | if (this.sliding) return
83 | return this.slide('prev')
84 | }
85 |
86 | , slide: function (type, next) {
87 | var $active = this.$element.find('.active')
88 | , $next = next || $active[type]()
89 | , isCycling = this.interval
90 | , direction = type == 'next' ? 'left' : 'right'
91 | , fallback = type == 'next' ? 'first' : 'last'
92 | , that = this
93 | , e = $.Event('slide')
94 |
95 | this.sliding = true
96 |
97 | isCycling && this.pause()
98 |
99 | $next = $next.length ? $next : this.$element.find('.item')[fallback]()
100 |
101 | if ($next.hasClass('active')) return
102 |
103 | if ($.support.transition && this.$element.hasClass('slide')) {
104 | this.$element.trigger(e)
105 | if (e.isDefaultPrevented()) return
106 | $next.addClass(type)
107 | $next[0].offsetWidth // force reflow
108 | $active.addClass(direction)
109 | $next.addClass(direction)
110 | this.$element.one($.support.transition.end, function () {
111 | $next.removeClass([type, direction].join(' ')).addClass('active')
112 | $active.removeClass(['active', direction].join(' '))
113 | that.sliding = false
114 | setTimeout(function () { that.$element.trigger('slid') }, 0)
115 | })
116 | } else {
117 | this.$element.trigger(e)
118 | if (e.isDefaultPrevented()) return
119 | $active.removeClass('active')
120 | $next.addClass('active')
121 | this.sliding = false
122 | this.$element.trigger('slid')
123 | }
124 |
125 | isCycling && this.cycle()
126 |
127 | return this
128 | }
129 |
130 | }
131 |
132 |
133 | /* CAROUSEL PLUGIN DEFINITION
134 | * ========================== */
135 |
136 | $.fn.carousel = function (option) {
137 | return this.each(function () {
138 | var $this = $(this)
139 | , data = $this.data('carousel')
140 | , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
141 | if (!data) $this.data('carousel', (data = new Carousel(this, options)))
142 | if (typeof option == 'number') data.to(option)
143 | else if (typeof option == 'string' || (option = options.slide)) data[option]()
144 | else if (options.interval) data.cycle()
145 | })
146 | }
147 |
148 | $.fn.carousel.defaults = {
149 | interval: 5000
150 | , pause: 'hover'
151 | }
152 |
153 | $.fn.carousel.Constructor = Carousel
154 |
155 |
156 | /* CAROUSEL DATA-API
157 | * ================= */
158 |
159 | $(function () {
160 | $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) {
161 | var $this = $(this), href
162 | , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
163 | , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data())
164 | $target.carousel(options)
165 | e.preventDefault()
166 | })
167 | })
168 |
169 | }(window.jQuery);
--------------------------------------------------------------------------------
/public/js/bootstrap-collapse.js:
--------------------------------------------------------------------------------
1 | /* =============================================================
2 | * bootstrap-collapse.js v2.0.4
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 |
21 | !function ($) {
22 |
23 | "use strict"; // jshint ;_;
24 |
25 |
26 | /* COLLAPSE PUBLIC CLASS DEFINITION
27 | * ================================ */
28 |
29 | var Collapse = function (element, options) {
30 | this.$element = $(element)
31 | this.options = $.extend({}, $.fn.collapse.defaults, options)
32 |
33 | if (this.options.parent) {
34 | this.$parent = $(this.options.parent)
35 | }
36 |
37 | this.options.toggle && this.toggle()
38 | }
39 |
40 | Collapse.prototype = {
41 |
42 | constructor: Collapse
43 |
44 | , dimension: function () {
45 | var hasWidth = this.$element.hasClass('width')
46 | return hasWidth ? 'width' : 'height'
47 | }
48 |
49 | , show: function () {
50 | var dimension
51 | , scroll
52 | , actives
53 | , hasData
54 |
55 | if (this.transitioning) return
56 |
57 | dimension = this.dimension()
58 | scroll = $.camelCase(['scroll', dimension].join('-'))
59 | actives = this.$parent && this.$parent.find('> .accordion-group > .in')
60 |
61 | if (actives && actives.length) {
62 | hasData = actives.data('collapse')
63 | if (hasData && hasData.transitioning) return
64 | actives.collapse('hide')
65 | hasData || actives.data('collapse', null)
66 | }
67 |
68 | this.$element[dimension](0)
69 | this.transition('addClass', $.Event('show'), 'shown')
70 | this.$element[dimension](this.$element[0][scroll])
71 | }
72 |
73 | , hide: function () {
74 | var dimension
75 | if (this.transitioning) return
76 | dimension = this.dimension()
77 | this.reset(this.$element[dimension]())
78 | this.transition('removeClass', $.Event('hide'), 'hidden')
79 | this.$element[dimension](0)
80 | }
81 |
82 | , reset: function (size) {
83 | var dimension = this.dimension()
84 |
85 | this.$element
86 | .removeClass('collapse')
87 | [dimension](size || 'auto')
88 | [0].offsetWidth
89 |
90 | this.$element[size !== null ? 'addClass' : 'removeClass']('collapse')
91 |
92 | return this
93 | }
94 |
95 | , transition: function (method, startEvent, completeEvent) {
96 | var that = this
97 | , complete = function () {
98 | if (startEvent.type == 'show') that.reset()
99 | that.transitioning = 0
100 | that.$element.trigger(completeEvent)
101 | }
102 |
103 | this.$element.trigger(startEvent)
104 |
105 | if (startEvent.isDefaultPrevented()) return
106 |
107 | this.transitioning = 1
108 |
109 | this.$element[method]('in')
110 |
111 | $.support.transition && this.$element.hasClass('collapse') ?
112 | this.$element.one($.support.transition.end, complete) :
113 | complete()
114 | }
115 |
116 | , toggle: function () {
117 | this[this.$element.hasClass('in') ? 'hide' : 'show']()
118 | }
119 |
120 | }
121 |
122 |
123 | /* COLLAPSIBLE PLUGIN DEFINITION
124 | * ============================== */
125 |
126 | $.fn.collapse = function (option) {
127 | return this.each(function () {
128 | var $this = $(this)
129 | , data = $this.data('collapse')
130 | , options = typeof option == 'object' && option
131 | if (!data) $this.data('collapse', (data = new Collapse(this, options)))
132 | if (typeof option == 'string') data[option]()
133 | })
134 | }
135 |
136 | $.fn.collapse.defaults = {
137 | toggle: true
138 | }
139 |
140 | $.fn.collapse.Constructor = Collapse
141 |
142 |
143 | /* COLLAPSIBLE DATA-API
144 | * ==================== */
145 |
146 | $(function () {
147 | $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
148 | var $this = $(this), href
149 | , target = $this.attr('data-target')
150 | || e.preventDefault()
151 | || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
152 | , option = $(target).data('collapse') ? 'toggle' : $this.data()
153 | $(target).collapse(option)
154 | })
155 | })
156 |
157 | }(window.jQuery);
--------------------------------------------------------------------------------
/public/js/bootstrap-dropdown.js:
--------------------------------------------------------------------------------
1 | /* ============================================================
2 | * bootstrap-dropdown.js v2.0.4
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"; // jshint ;_;
24 |
25 |
26 | /* DROPDOWN CLASS DEFINITION
27 | * ========================= */
28 |
29 | var toggle = '[data-toggle="dropdown"]'
30 | , Dropdown = function (element) {
31 | var $el = $(element).on('click.dropdown.data-api', this.toggle)
32 | $('html').on('click.dropdown.data-api', function () {
33 | $el.parent().removeClass('open')
34 | })
35 | }
36 |
37 | Dropdown.prototype = {
38 |
39 | constructor: Dropdown
40 |
41 | , toggle: function (e) {
42 | var $this = $(this)
43 | , $parent
44 | , selector
45 | , isActive
46 |
47 | if ($this.is('.disabled, :disabled')) return
48 |
49 | selector = $this.attr('data-target')
50 |
51 | if (!selector) {
52 | selector = $this.attr('href')
53 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
54 | }
55 |
56 | $parent = $(selector)
57 | $parent.length || ($parent = $this.parent())
58 |
59 | isActive = $parent.hasClass('open')
60 |
61 | clearMenus()
62 |
63 | if (!isActive) $parent.toggleClass('open')
64 |
65 | return false
66 | }
67 |
68 | }
69 |
70 | function clearMenus() {
71 | $(toggle).parent().removeClass('open')
72 | }
73 |
74 |
75 | /* DROPDOWN PLUGIN DEFINITION
76 | * ========================== */
77 |
78 | $.fn.dropdown = function (option) {
79 | return this.each(function () {
80 | var $this = $(this)
81 | , data = $this.data('dropdown')
82 | if (!data) $this.data('dropdown', (data = new Dropdown(this)))
83 | if (typeof option == 'string') data[option].call($this)
84 | })
85 | }
86 |
87 | $.fn.dropdown.Constructor = Dropdown
88 |
89 |
90 | /* APPLY TO STANDARD DROPDOWN ELEMENTS
91 | * =================================== */
92 |
93 | $(function () {
94 | $('html').on('click.dropdown.data-api', clearMenus)
95 | $('body')
96 | .on('click.dropdown', '.dropdown form', function (e) { e.stopPropagation() })
97 | .on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
98 | })
99 |
100 | }(window.jQuery);
--------------------------------------------------------------------------------
/public/js/bootstrap-modal.js:
--------------------------------------------------------------------------------
1 | /* =========================================================
2 | * bootstrap-modal.js v2.0.4
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"; // jshint ;_;
24 |
25 |
26 | /* MODAL CLASS DEFINITION
27 | * ====================== */
28 |
29 | var Modal = function (content, options) {
30 | this.options = options
31 | this.$element = $(content)
32 | .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
33 | }
34 |
35 | Modal.prototype = {
36 |
37 | constructor: Modal
38 |
39 | , toggle: function () {
40 | return this[!this.isShown ? 'show' : 'hide']()
41 | }
42 |
43 | , show: function () {
44 | var that = this
45 | , e = $.Event('show')
46 |
47 | this.$element.trigger(e)
48 |
49 | if (this.isShown || e.isDefaultPrevented()) return
50 |
51 | $('body').addClass('modal-open')
52 |
53 | this.isShown = true
54 |
55 | escape.call(this)
56 | backdrop.call(this, function () {
57 | var transition = $.support.transition && that.$element.hasClass('fade')
58 |
59 | if (!that.$element.parent().length) {
60 | that.$element.appendTo(document.body) //don't move modals dom position
61 | }
62 |
63 | that.$element
64 | .show()
65 |
66 | if (transition) {
67 | that.$element[0].offsetWidth // force reflow
68 | }
69 |
70 | that.$element.addClass('in')
71 |
72 | transition ?
73 | that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
74 | that.$element.trigger('shown')
75 |
76 | })
77 | }
78 |
79 | , hide: function (e) {
80 | e && e.preventDefault()
81 |
82 | var that = this
83 |
84 | e = $.Event('hide')
85 |
86 | this.$element.trigger(e)
87 |
88 | if (!this.isShown || e.isDefaultPrevented()) return
89 |
90 | this.isShown = false
91 |
92 | $('body').removeClass('modal-open')
93 |
94 | escape.call(this)
95 |
96 | this.$element.removeClass('in')
97 |
98 | $.support.transition && this.$element.hasClass('fade') ?
99 | hideWithTransition.call(this) :
100 | hideModal.call(this)
101 | }
102 |
103 | }
104 |
105 |
106 | /* MODAL PRIVATE METHODS
107 | * ===================== */
108 |
109 | function hideWithTransition() {
110 | var that = this
111 | , timeout = setTimeout(function () {
112 | that.$element.off($.support.transition.end)
113 | hideModal.call(that)
114 | }, 500)
115 |
116 | this.$element.one($.support.transition.end, function () {
117 | clearTimeout(timeout)
118 | hideModal.call(that)
119 | })
120 | }
121 |
122 | function hideModal(that) {
123 | this.$element
124 | .hide()
125 | .trigger('hidden')
126 |
127 | backdrop.call(this)
128 | }
129 |
130 | function backdrop(callback) {
131 | var that = this
132 | , animate = this.$element.hasClass('fade') ? 'fade' : ''
133 |
134 | if (this.isShown && this.options.backdrop) {
135 | var doAnimate = $.support.transition && animate
136 |
137 | this.$backdrop = $('
')
138 | .appendTo(document.body)
139 |
140 | if (this.options.backdrop != 'static') {
141 | this.$backdrop.click($.proxy(this.hide, this))
142 | }
143 |
144 | if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
145 |
146 | this.$backdrop.addClass('in')
147 |
148 | doAnimate ?
149 | this.$backdrop.one($.support.transition.end, callback) :
150 | callback()
151 |
152 | } else if (!this.isShown && this.$backdrop) {
153 | this.$backdrop.removeClass('in')
154 |
155 | $.support.transition && this.$element.hasClass('fade')?
156 | this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
157 | removeBackdrop.call(this)
158 |
159 | } else if (callback) {
160 | callback()
161 | }
162 | }
163 |
164 | function removeBackdrop() {
165 | this.$backdrop.remove()
166 | this.$backdrop = null
167 | }
168 |
169 | function escape() {
170 | var that = this
171 | if (this.isShown && this.options.keyboard) {
172 | $(document).on('keyup.dismiss.modal', function ( e ) {
173 | e.which == 27 && that.hide()
174 | })
175 | } else if (!this.isShown) {
176 | $(document).off('keyup.dismiss.modal')
177 | }
178 | }
179 |
180 |
181 | /* MODAL PLUGIN DEFINITION
182 | * ======================= */
183 |
184 | $.fn.modal = function (option) {
185 | return this.each(function () {
186 | var $this = $(this)
187 | , data = $this.data('modal')
188 | , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
189 | if (!data) $this.data('modal', (data = new Modal(this, options)))
190 | if (typeof option == 'string') data[option]()
191 | else if (options.show) data.show()
192 | })
193 | }
194 |
195 | $.fn.modal.defaults = {
196 | backdrop: true
197 | , keyboard: true
198 | , show: true
199 | }
200 |
201 | $.fn.modal.Constructor = Modal
202 |
203 |
204 | /* MODAL DATA-API
205 | * ============== */
206 |
207 | $(function () {
208 | $('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
209 | var $this = $(this), href
210 | , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
211 | , option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data())
212 |
213 | e.preventDefault()
214 | $target.modal(option)
215 | })
216 | })
217 |
218 | }(window.jQuery);
--------------------------------------------------------------------------------
/public/js/bootstrap-popover.js:
--------------------------------------------------------------------------------
1 | /* ===========================================================
2 | * bootstrap-popover.js v2.0.4
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"; // jshint ;_;
24 |
25 |
26 | /* POPOVER PUBLIC CLASS DEFINITION
27 | * =============================== */
28 |
29 | var Popover = function ( element, options ) {
30 | this.init('popover', element, options)
31 | }
32 |
33 |
34 | /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
35 | ========================================== */
36 |
37 | Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
38 |
39 | constructor: Popover
40 |
41 | , setContent: function () {
42 | var $tip = this.tip()
43 | , title = this.getTitle()
44 | , content = this.getContent()
45 |
46 | $tip.find('.popover-title')[this.isHTML(title) ? 'html' : 'text'](title)
47 | $tip.find('.popover-content > *')[this.isHTML(content) ? 'html' : 'text'](content)
48 |
49 | $tip.removeClass('fade top bottom left right in')
50 | }
51 |
52 | , hasContent: function () {
53 | return this.getTitle() || this.getContent()
54 | }
55 |
56 | , getContent: function () {
57 | var content
58 | , $e = this.$element
59 | , o = this.options
60 |
61 | content = $e.attr('data-content')
62 | || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
63 |
64 | return content
65 | }
66 |
67 | , tip: function () {
68 | if (!this.$tip) {
69 | this.$tip = $(this.options.template)
70 | }
71 | return this.$tip
72 | }
73 |
74 | })
75 |
76 |
77 | /* POPOVER PLUGIN DEFINITION
78 | * ======================= */
79 |
80 | $.fn.popover = function (option) {
81 | return this.each(function () {
82 | var $this = $(this)
83 | , data = $this.data('popover')
84 | , options = typeof option == 'object' && option
85 | if (!data) $this.data('popover', (data = new Popover(this, options)))
86 | if (typeof option == 'string') data[option]()
87 | })
88 | }
89 |
90 | $.fn.popover.Constructor = Popover
91 |
92 | $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
93 | placement: 'right'
94 | , content: ''
95 | , template: ''
96 | })
97 |
98 | }(window.jQuery);
--------------------------------------------------------------------------------
/public/js/bootstrap-scrollspy.js:
--------------------------------------------------------------------------------
1 | /* =============================================================
2 | * bootstrap-scrollspy.js v2.0.4
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 |
21 | !function ($) {
22 |
23 | "use strict"; // jshint ;_;
24 |
25 |
26 | /* SCROLLSPY CLASS DEFINITION
27 | * ========================== */
28 |
29 | function ScrollSpy( element, options) {
30 | var process = $.proxy(this.process, this)
31 | , $element = $(element).is('body') ? $(window) : $(element)
32 | , href
33 | this.options = $.extend({}, $.fn.scrollspy.defaults, options)
34 | this.$scrollElement = $element.on('scroll.scroll.data-api', process)
35 | this.selector = (this.options.target
36 | || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
37 | || '') + ' .nav li > a'
38 | this.$body = $('body')
39 | this.refresh()
40 | this.process()
41 | }
42 |
43 | ScrollSpy.prototype = {
44 |
45 | constructor: ScrollSpy
46 |
47 | , refresh: function () {
48 | var self = this
49 | , $targets
50 |
51 | this.offsets = $([])
52 | this.targets = $([])
53 |
54 | $targets = this.$body
55 | .find(this.selector)
56 | .map(function () {
57 | var $el = $(this)
58 | , href = $el.data('target') || $el.attr('href')
59 | , $href = /^#\w/.test(href) && $(href)
60 | return ( $href
61 | && href.length
62 | && [[ $href.position().top, href ]] ) || null
63 | })
64 | .sort(function (a, b) { return a[0] - b[0] })
65 | .each(function () {
66 | self.offsets.push(this[0])
67 | self.targets.push(this[1])
68 | })
69 | }
70 |
71 | , process: function () {
72 | var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
73 | , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
74 | , maxScroll = scrollHeight - this.$scrollElement.height()
75 | , offsets = this.offsets
76 | , targets = this.targets
77 | , activeTarget = this.activeTarget
78 | , i
79 |
80 | if (scrollTop >= maxScroll) {
81 | return activeTarget != (i = targets.last()[0])
82 | && this.activate ( i )
83 | }
84 |
85 | for (i = offsets.length; i--;) {
86 | activeTarget != targets[i]
87 | && scrollTop >= offsets[i]
88 | && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
89 | && this.activate( targets[i] )
90 | }
91 | }
92 |
93 | , activate: function (target) {
94 | var active
95 | , selector
96 |
97 | this.activeTarget = target
98 |
99 | $(this.selector)
100 | .parent('.active')
101 | .removeClass('active')
102 |
103 | selector = this.selector
104 | + '[data-target="' + target + '"],'
105 | + this.selector + '[href="' + target + '"]'
106 |
107 | active = $(selector)
108 | .parent('li')
109 | .addClass('active')
110 |
111 | if (active.parent('.dropdown-menu')) {
112 | active = active.closest('li.dropdown').addClass('active')
113 | }
114 |
115 | active.trigger('activate')
116 | }
117 |
118 | }
119 |
120 |
121 | /* SCROLLSPY PLUGIN DEFINITION
122 | * =========================== */
123 |
124 | $.fn.scrollspy = function ( option ) {
125 | return this.each(function () {
126 | var $this = $(this)
127 | , data = $this.data('scrollspy')
128 | , options = typeof option == 'object' && option
129 | if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
130 | if (typeof option == 'string') data[option]()
131 | })
132 | }
133 |
134 | $.fn.scrollspy.Constructor = ScrollSpy
135 |
136 | $.fn.scrollspy.defaults = {
137 | offset: 10
138 | }
139 |
140 |
141 | /* SCROLLSPY DATA-API
142 | * ================== */
143 |
144 | $(function () {
145 | $('[data-spy="scroll"]').each(function () {
146 | var $spy = $(this)
147 | $spy.scrollspy($spy.data())
148 | })
149 | })
150 |
151 | }(window.jQuery);
--------------------------------------------------------------------------------
/public/js/bootstrap-tab.js:
--------------------------------------------------------------------------------
1 | /* ========================================================
2 | * bootstrap-tab.js v2.0.4
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"; // jshint ;_;
24 |
25 |
26 | /* TAB CLASS DEFINITION
27 | * ==================== */
28 |
29 | var Tab = function ( element ) {
30 | this.element = $(element)
31 | }
32 |
33 | Tab.prototype = {
34 |
35 | constructor: Tab
36 |
37 | , show: function () {
38 | var $this = this.element
39 | , $ul = $this.closest('ul:not(.dropdown-menu)')
40 | , selector = $this.attr('data-target')
41 | , previous
42 | , $target
43 | , e
44 |
45 | if (!selector) {
46 | selector = $this.attr('href')
47 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
48 | }
49 |
50 | if ( $this.parent('li').hasClass('active') ) return
51 |
52 | previous = $ul.find('.active a').last()[0]
53 |
54 | e = $.Event('show', {
55 | relatedTarget: previous
56 | })
57 |
58 | $this.trigger(e)
59 |
60 | if (e.isDefaultPrevented()) return
61 |
62 | $target = $(selector)
63 |
64 | this.activate($this.parent('li'), $ul)
65 | this.activate($target, $target.parent(), function () {
66 | $this.trigger({
67 | type: 'shown'
68 | , relatedTarget: previous
69 | })
70 | })
71 | }
72 |
73 | , activate: function ( element, container, callback) {
74 | var $active = container.find('> .active')
75 | , transition = callback
76 | && $.support.transition
77 | && $active.hasClass('fade')
78 |
79 | function next() {
80 | $active
81 | .removeClass('active')
82 | .find('> .dropdown-menu > .active')
83 | .removeClass('active')
84 |
85 | element.addClass('active')
86 |
87 | if (transition) {
88 | element[0].offsetWidth // reflow for transition
89 | element.addClass('in')
90 | } else {
91 | element.removeClass('fade')
92 | }
93 |
94 | if ( element.parent('.dropdown-menu') ) {
95 | element.closest('li.dropdown').addClass('active')
96 | }
97 |
98 | callback && callback()
99 | }
100 |
101 | transition ?
102 | $active.one($.support.transition.end, next) :
103 | next()
104 |
105 | $active.removeClass('in')
106 | }
107 | }
108 |
109 |
110 | /* TAB PLUGIN DEFINITION
111 | * ===================== */
112 |
113 | $.fn.tab = function ( option ) {
114 | return this.each(function () {
115 | var $this = $(this)
116 | , data = $this.data('tab')
117 | if (!data) $this.data('tab', (data = new Tab(this)))
118 | if (typeof option == 'string') data[option]()
119 | })
120 | }
121 |
122 | $.fn.tab.Constructor = Tab
123 |
124 |
125 | /* TAB DATA-API
126 | * ============ */
127 |
128 | $(function () {
129 | $('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
130 | e.preventDefault()
131 | $(this).tab('show')
132 | })
133 | })
134 |
135 | }(window.jQuery);
--------------------------------------------------------------------------------
/public/js/bootstrap-tabs.js:
--------------------------------------------------------------------------------
1 | /* ========================================================
2 | * bootstrap-tabs.js v1.3.0
3 | * http://twitter.github.com/bootstrap/javascript.html#tabs
4 | * ========================================================
5 | * Copyright 2011 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 | function activate ( element, container ) {
24 | container.find('.active').removeClass('active')
25 | element.addClass('active')
26 | }
27 |
28 | function tab( e ) {
29 | var $this = $(this)
30 | , href = $this.attr('href')
31 | , $ul = $this.closest('ul')
32 | , $controlled
33 |
34 | if (/^#\w+/.test(href)) {
35 | e.preventDefault()
36 |
37 | if ($this.hasClass('active')) {
38 | return
39 | }
40 |
41 | $href = $(href)
42 |
43 | activate($this.parent('li'), $ul)
44 | activate($href, $href.parent())
45 | }
46 | }
47 |
48 |
49 | /* TABS/PILLS PLUGIN DEFINITION
50 | * ============================ */
51 |
52 | $.fn.tabs = $.fn.pills = function ( selector ) {
53 | return this.each(function () {
54 | $(this).delegate(selector || '.tabs li > a, .pills > li > a', 'click', tab)
55 | })
56 | }
57 |
58 | $(document).ready(function () {
59 | $('body').tabs('ul[data-tabs] li > a, ul[data-pills] > li > a')
60 | })
61 |
62 | }( window.jQuery || window.ender );
--------------------------------------------------------------------------------
/public/js/bootstrap-tooltip.js:
--------------------------------------------------------------------------------
1 | /* ===========================================================
2 | * bootstrap-tooltip.js v2.0.4
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 |
22 | !function ($) {
23 |
24 | "use strict"; // jshint ;_;
25 |
26 |
27 | /* TOOLTIP PUBLIC CLASS DEFINITION
28 | * =============================== */
29 |
30 | var Tooltip = function (element, options) {
31 | this.init('tooltip', element, options)
32 | }
33 |
34 | Tooltip.prototype = {
35 |
36 | constructor: Tooltip
37 |
38 | , init: function (type, element, options) {
39 | var eventIn
40 | , eventOut
41 |
42 | this.type = type
43 | this.$element = $(element)
44 | this.options = this.getOptions(options)
45 | this.enabled = true
46 |
47 | if (this.options.trigger != 'manual') {
48 | eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
49 | eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
50 | this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))
51 | this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
52 | }
53 |
54 | this.options.selector ?
55 | (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
56 | this.fixTitle()
57 | }
58 |
59 | , getOptions: function (options) {
60 | options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
61 |
62 | if (options.delay && typeof options.delay == 'number') {
63 | options.delay = {
64 | show: options.delay
65 | , hide: options.delay
66 | }
67 | }
68 |
69 | return options
70 | }
71 |
72 | , enter: function (e) {
73 | var self = $(e.currentTarget)[this.type](this._options).data(this.type)
74 |
75 | if (!self.options.delay || !self.options.delay.show) return self.show()
76 |
77 | clearTimeout(this.timeout)
78 | self.hoverState = 'in'
79 | this.timeout = setTimeout(function() {
80 | if (self.hoverState == 'in') self.show()
81 | }, self.options.delay.show)
82 | }
83 |
84 | , leave: function (e) {
85 | var self = $(e.currentTarget)[this.type](this._options).data(this.type)
86 |
87 | if (this.timeout) clearTimeout(this.timeout)
88 | if (!self.options.delay || !self.options.delay.hide) return self.hide()
89 |
90 | self.hoverState = 'out'
91 | this.timeout = setTimeout(function() {
92 | if (self.hoverState == 'out') self.hide()
93 | }, self.options.delay.hide)
94 | }
95 |
96 | , show: function () {
97 | var $tip
98 | , inside
99 | , pos
100 | , actualWidth
101 | , actualHeight
102 | , placement
103 | , tp
104 |
105 | if (this.hasContent() && this.enabled) {
106 | $tip = this.tip()
107 | this.setContent()
108 |
109 | if (this.options.animation) {
110 | $tip.addClass('fade')
111 | }
112 |
113 | placement = typeof this.options.placement == 'function' ?
114 | this.options.placement.call(this, $tip[0], this.$element[0]) :
115 | this.options.placement
116 |
117 | inside = /in/.test(placement)
118 |
119 | $tip
120 | .remove()
121 | .css({ top: 0, left: 0, display: 'block' })
122 | .appendTo(inside ? this.$element : document.body)
123 |
124 | pos = this.getPosition(inside)
125 |
126 | actualWidth = $tip[0].offsetWidth
127 | actualHeight = $tip[0].offsetHeight
128 |
129 | switch (inside ? placement.split(' ')[1] : placement) {
130 | case 'bottom':
131 | tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
132 | break
133 | case 'top':
134 | tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
135 | break
136 | case 'left':
137 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
138 | break
139 | case 'right':
140 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
141 | break
142 | }
143 |
144 | $tip
145 | .css(tp)
146 | .addClass(placement)
147 | .addClass('in')
148 | }
149 | }
150 |
151 | , isHTML: function(text) {
152 | // html string detection logic adapted from jQuery
153 | return typeof text != 'string'
154 | || ( text.charAt(0) === "<"
155 | && text.charAt( text.length - 1 ) === ">"
156 | && text.length >= 3
157 | ) || /^(?:[^<]*<[\w\W]+>[^>]*$)/.exec(text)
158 | }
159 |
160 | , setContent: function () {
161 | var $tip = this.tip()
162 | , title = this.getTitle()
163 |
164 | $tip.find('.tooltip-inner')[this.isHTML(title) ? 'html' : 'text'](title)
165 | $tip.removeClass('fade in top bottom left right')
166 | }
167 |
168 | , hide: function () {
169 | var that = this
170 | , $tip = this.tip()
171 |
172 | $tip.removeClass('in')
173 |
174 | function removeWithAnimation() {
175 | var timeout = setTimeout(function () {
176 | $tip.off($.support.transition.end).remove()
177 | }, 500)
178 |
179 | $tip.one($.support.transition.end, function () {
180 | clearTimeout(timeout)
181 | $tip.remove()
182 | })
183 | }
184 |
185 | $.support.transition && this.$tip.hasClass('fade') ?
186 | removeWithAnimation() :
187 | $tip.remove()
188 | }
189 |
190 | , fixTitle: function () {
191 | var $e = this.$element
192 | if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
193 | $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
194 | }
195 | }
196 |
197 | , hasContent: function () {
198 | return this.getTitle()
199 | }
200 |
201 | , getPosition: function (inside) {
202 | return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
203 | width: this.$element[0].offsetWidth
204 | , height: this.$element[0].offsetHeight
205 | })
206 | }
207 |
208 | , getTitle: function () {
209 | var title
210 | , $e = this.$element
211 | , o = this.options
212 |
213 | title = $e.attr('data-original-title')
214 | || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
215 |
216 | return title
217 | }
218 |
219 | , tip: function () {
220 | return this.$tip = this.$tip || $(this.options.template)
221 | }
222 |
223 | , validate: function () {
224 | if (!this.$element[0].parentNode) {
225 | this.hide()
226 | this.$element = null
227 | this.options = null
228 | }
229 | }
230 |
231 | , enable: function () {
232 | this.enabled = true
233 | }
234 |
235 | , disable: function () {
236 | this.enabled = false
237 | }
238 |
239 | , toggleEnabled: function () {
240 | this.enabled = !this.enabled
241 | }
242 |
243 | , toggle: function () {
244 | this[this.tip().hasClass('in') ? 'hide' : 'show']()
245 | }
246 |
247 | }
248 |
249 |
250 | /* TOOLTIP PLUGIN DEFINITION
251 | * ========================= */
252 |
253 | $.fn.tooltip = function ( option ) {
254 | return this.each(function () {
255 | var $this = $(this)
256 | , data = $this.data('tooltip')
257 | , options = typeof option == 'object' && option
258 | if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
259 | if (typeof option == 'string') data[option]()
260 | })
261 | }
262 |
263 | $.fn.tooltip.Constructor = Tooltip
264 |
265 | $.fn.tooltip.defaults = {
266 | animation: true
267 | , placement: 'top'
268 | , selector: false
269 | , template: ''
270 | , trigger: 'hover'
271 | , title: ''
272 | , delay: 0
273 | }
274 |
275 | }(window.jQuery);
276 |
--------------------------------------------------------------------------------
/public/js/bootstrap-transition.js:
--------------------------------------------------------------------------------
1 | /* ===================================================
2 | * bootstrap-transition.js v2.0.4
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 |
21 | !function ($) {
22 |
23 | $(function () {
24 |
25 | "use strict"; // jshint ;_;
26 |
27 |
28 | /* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
29 | * ======================================================= */
30 |
31 | $.support.transition = (function () {
32 |
33 | var transitionEnd = (function () {
34 |
35 | var el = document.createElement('bootstrap')
36 | , transEndEventNames = {
37 | 'WebkitTransition' : 'webkitTransitionEnd'
38 | , 'MozTransition' : 'transitionend'
39 | , 'OTransition' : 'oTransitionEnd'
40 | , 'msTransition' : 'MSTransitionEnd'
41 | , 'transition' : 'transitionend'
42 | }
43 | , name
44 |
45 | for (name in transEndEventNames){
46 | if (el.style[name] !== undefined) {
47 | return transEndEventNames[name]
48 | }
49 | }
50 |
51 | }())
52 |
53 | return transitionEnd && {
54 | end: transitionEnd
55 | }
56 |
57 | })()
58 |
59 | })
60 |
61 | }(window.jQuery);
--------------------------------------------------------------------------------
/public/js/bootstrap-twipsy.js:
--------------------------------------------------------------------------------
1 | /* ==========================================================
2 | * bootstrap-twipsy.js v1.3.0
3 | * http://twitter.github.com/bootstrap/javascript.html#twipsy
4 | * Adapted from the original jQuery.tipsy by Jason Frame
5 | * ==========================================================
6 | * Copyright 2011 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 |
22 | !function( $ ) {
23 |
24 | /* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
25 | * ======================================================= */
26 |
27 | var transitionEnd
28 |
29 | $(document).ready(function () {
30 |
31 | $.support.transition = (function () {
32 | var thisBody = document.body || document.documentElement
33 | , thisStyle = thisBody.style
34 | , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
35 | return support
36 | })()
37 |
38 | // set CSS transition event type
39 | if ( $.support.transition ) {
40 | transitionEnd = "TransitionEnd"
41 | if ( $.browser.webkit ) {
42 | transitionEnd = "webkitTransitionEnd"
43 | } else if ( $.browser.mozilla ) {
44 | transitionEnd = "transitionend"
45 | } else if ( $.browser.opera ) {
46 | transitionEnd = "oTransitionEnd"
47 | }
48 | }
49 |
50 | })
51 |
52 |
53 | /* TWIPSY PUBLIC CLASS DEFINITION
54 | * ============================== */
55 |
56 | var Twipsy = function ( element, options ) {
57 | this.$element = $(element)
58 | this.options = options
59 | this.enabled = true
60 | this.fixTitle()
61 | }
62 |
63 | Twipsy.prototype = {
64 |
65 | show: function() {
66 | var pos
67 | , actualWidth
68 | , actualHeight
69 | , placement
70 | , $tip
71 | , tp
72 |
73 | if (this.getTitle() && this.enabled) {
74 | $tip = this.tip()
75 | this.setContent()
76 |
77 | if (this.options.animate) {
78 | $tip.addClass('fade')
79 | }
80 |
81 | $tip
82 | .remove()
83 | .css({ top: 0, left: 0, display: 'block' })
84 | .prependTo(document.body)
85 |
86 | pos = $.extend({}, this.$element.offset(), {
87 | width: this.$element[0].offsetWidth
88 | , height: this.$element[0].offsetHeight
89 | })
90 |
91 | actualWidth = $tip[0].offsetWidth
92 | actualHeight = $tip[0].offsetHeight
93 | placement = _.maybeCall(this.options.placement, this.$element[0])
94 |
95 | switch (placement) {
96 | case 'below':
97 | tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}
98 | break
99 | case 'above':
100 | tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}
101 | break
102 | case 'left':
103 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset}
104 | break
105 | case 'right':
106 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset}
107 | break
108 | }
109 |
110 | $tip
111 | .css(tp)
112 | .addClass(placement)
113 | .addClass('in')
114 | }
115 | }
116 |
117 | , setContent: function () {
118 | var $tip = this.tip()
119 | $tip.find('.twipsy-inner')[this.options.html ? 'html' : 'text'](this.getTitle())
120 | $tip[0].className = 'twipsy'
121 | }
122 |
123 | , hide: function() {
124 | var that = this
125 | , $tip = this.tip()
126 |
127 | $tip.removeClass('in')
128 |
129 | function removeElement () {
130 | $tip.remove()
131 | }
132 |
133 | $.support.transition && this.$tip.hasClass('fade') ?
134 | $tip.bind(transitionEnd, removeElement) :
135 | removeElement()
136 | }
137 |
138 | , fixTitle: function() {
139 | var $e = this.$element
140 | if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
141 | $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
142 | }
143 | }
144 |
145 | , getTitle: function() {
146 | var title
147 | , $e = this.$element
148 | , o = this.options
149 |
150 | this.fixTitle()
151 |
152 | if (typeof o.title == 'string') {
153 | title = $e.attr(o.title == 'title' ? 'data-original-title' : o.title)
154 | } else if (typeof o.title == 'function') {
155 | title = o.title.call($e[0])
156 | }
157 |
158 | title = ('' + title).replace(/(^\s*|\s*$)/, "")
159 |
160 | return title || o.fallback
161 | }
162 |
163 | , tip: function() {
164 | if (!this.$tip) {
165 | this.$tip = $('
').html('
')
166 | }
167 | return this.$tip
168 | }
169 |
170 | , validate: function() {
171 | if (!this.$element[0].parentNode) {
172 | this.hide()
173 | this.$element = null
174 | this.options = null
175 | }
176 | }
177 |
178 | , enable: function() {
179 | this.enabled = true
180 | }
181 |
182 | , disable: function() {
183 | this.enabled = false
184 | }
185 |
186 | , toggleEnabled: function() {
187 | this.enabled = !this.enabled
188 | }
189 |
190 | }
191 |
192 |
193 | /* TWIPSY PRIVATE METHODS
194 | * ====================== */
195 |
196 | var _ = {
197 |
198 | maybeCall: function ( thing, ctx ) {
199 | return (typeof thing == 'function') ? (thing.call(ctx)) : thing
200 | }
201 |
202 | }
203 |
204 |
205 | /* TWIPSY PLUGIN DEFINITION
206 | * ======================== */
207 |
208 | $.fn.twipsy = function (options) {
209 | $.fn.twipsy.initWith.call(this, options, Twipsy, 'twipsy')
210 | return this
211 | }
212 |
213 | $.fn.twipsy.initWith = function (options, Constructor, name) {
214 | var twipsy
215 | , binder
216 | , eventIn
217 | , eventOut
218 |
219 | if (options === true) {
220 | return this.data(name)
221 | } else if (typeof options == 'string') {
222 | twipsy = this.data(name)
223 | if (twipsy) {
224 | twipsy[options]()
225 | }
226 | return this
227 | }
228 |
229 | options = $.extend({}, $.fn[name].defaults, options)
230 |
231 | function get(ele) {
232 | var twipsy = $.data(ele, name)
233 |
234 | if (!twipsy) {
235 | twipsy = new Constructor(ele, $.fn.twipsy.elementOptions(ele, options))
236 | $.data(ele, name, twipsy)
237 | }
238 |
239 | return twipsy
240 | }
241 |
242 | function enter() {
243 | var twipsy = get(this)
244 | twipsy.hoverState = 'in'
245 |
246 | if (options.delayIn == 0) {
247 | twipsy.show()
248 | } else {
249 | twipsy.fixTitle()
250 | setTimeout(function() {
251 | if (twipsy.hoverState == 'in') {
252 | twipsy.show()
253 | }
254 | }, options.delayIn)
255 | }
256 | }
257 |
258 | function leave() {
259 | var twipsy = get(this)
260 | twipsy.hoverState = 'out'
261 | if (options.delayOut == 0) {
262 | twipsy.hide()
263 | } else {
264 | setTimeout(function() {
265 | if (twipsy.hoverState == 'out') {
266 | twipsy.hide()
267 | }
268 | }, options.delayOut)
269 | }
270 | }
271 |
272 | if (!options.live) {
273 | this.each(function() {
274 | get(this)
275 | })
276 | }
277 |
278 | if (options.trigger != 'manual') {
279 | binder = options.live ? 'live' : 'bind'
280 | eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus'
281 | eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur'
282 | this[binder](eventIn, enter)[binder](eventOut, leave)
283 | }
284 |
285 | return this
286 | }
287 |
288 | $.fn.twipsy.Twipsy = Twipsy
289 |
290 | $.fn.twipsy.defaults = {
291 | animate: true
292 | , delayIn: 0
293 | , delayOut: 0
294 | , fallback: ''
295 | , placement: 'above'
296 | , html: false
297 | , live: false
298 | , offset: 0
299 | , title: 'title'
300 | , trigger: 'hover'
301 | }
302 |
303 | $.fn.twipsy.elementOptions = function(ele, options) {
304 | return $.metadata ? $.extend({}, options, $(ele).metadata()) : options
305 | }
306 |
307 | }( window.jQuery || window.ender );
--------------------------------------------------------------------------------
/public/js/bootstrap-typeahead.js:
--------------------------------------------------------------------------------
1 | /* =============================================================
2 | * bootstrap-typeahead.js v2.0.4
3 | * http://twitter.github.com/bootstrap/javascript.html#typeahead
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"; // jshint ;_;
24 |
25 |
26 | /* TYPEAHEAD PUBLIC CLASS DEFINITION
27 | * ================================= */
28 |
29 | var Typeahead = function (element, options) {
30 | this.$element = $(element)
31 | this.options = $.extend({}, $.fn.typeahead.defaults, options)
32 | this.matcher = this.options.matcher || this.matcher
33 | this.sorter = this.options.sorter || this.sorter
34 | this.highlighter = this.options.highlighter || this.highlighter
35 | this.updater = this.options.updater || this.updater
36 | this.$menu = $(this.options.menu).appendTo('body')
37 | this.source = this.options.source
38 | this.shown = false
39 | this.listen()
40 | }
41 |
42 | Typeahead.prototype = {
43 |
44 | constructor: Typeahead
45 |
46 | , select: function () {
47 | var val = this.$menu.find('.active').attr('data-value')
48 | this.$element
49 | .val(this.updater(val))
50 | .change()
51 | return this.hide()
52 | }
53 |
54 | , updater: function (item) {
55 | return item
56 | }
57 |
58 | , show: function () {
59 | var pos = $.extend({}, this.$element.offset(), {
60 | height: this.$element[0].offsetHeight
61 | })
62 |
63 | this.$menu.css({
64 | top: pos.top + pos.height
65 | , left: pos.left
66 | })
67 |
68 | this.$menu.show()
69 | this.shown = true
70 | return this
71 | }
72 |
73 | , hide: function () {
74 | this.$menu.hide()
75 | this.shown = false
76 | return this
77 | }
78 |
79 | , lookup: function (event) {
80 | var that = this
81 | , items
82 | , q
83 |
84 | this.query = this.$element.val()
85 |
86 | if (!this.query) {
87 | return this.shown ? this.hide() : this
88 | }
89 |
90 | items = $.grep(this.source, function (item) {
91 | return that.matcher(item)
92 | })
93 |
94 | items = this.sorter(items)
95 |
96 | if (!items.length) {
97 | return this.shown ? this.hide() : this
98 | }
99 |
100 | return this.render(items.slice(0, this.options.items)).show()
101 | }
102 |
103 | , matcher: function (item) {
104 | return ~item.toLowerCase().indexOf(this.query.toLowerCase())
105 | }
106 |
107 | , sorter: function (items) {
108 | var beginswith = []
109 | , caseSensitive = []
110 | , caseInsensitive = []
111 | , item
112 |
113 | while (item = items.shift()) {
114 | if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
115 | else if (~item.indexOf(this.query)) caseSensitive.push(item)
116 | else caseInsensitive.push(item)
117 | }
118 |
119 | return beginswith.concat(caseSensitive, caseInsensitive)
120 | }
121 |
122 | , highlighter: function (item) {
123 | var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
124 | return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
125 | return '' + match + ' '
126 | })
127 | }
128 |
129 | , render: function (items) {
130 | var that = this
131 |
132 | items = $(items).map(function (i, item) {
133 | i = $(that.options.item).attr('data-value', item)
134 | i.find('a').html(that.highlighter(item))
135 | return i[0]
136 | })
137 |
138 | items.first().addClass('active')
139 | this.$menu.html(items)
140 | return this
141 | }
142 |
143 | , next: function (event) {
144 | var active = this.$menu.find('.active').removeClass('active')
145 | , next = active.next()
146 |
147 | if (!next.length) {
148 | next = $(this.$menu.find('li')[0])
149 | }
150 |
151 | next.addClass('active')
152 | }
153 |
154 | , prev: function (event) {
155 | var active = this.$menu.find('.active').removeClass('active')
156 | , prev = active.prev()
157 |
158 | if (!prev.length) {
159 | prev = this.$menu.find('li').last()
160 | }
161 |
162 | prev.addClass('active')
163 | }
164 |
165 | , listen: function () {
166 | this.$element
167 | .on('blur', $.proxy(this.blur, this))
168 | .on('keypress', $.proxy(this.keypress, this))
169 | .on('keyup', $.proxy(this.keyup, this))
170 |
171 | if ($.browser.webkit || $.browser.msie) {
172 | this.$element.on('keydown', $.proxy(this.keypress, this))
173 | }
174 |
175 | this.$menu
176 | .on('click', $.proxy(this.click, this))
177 | .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
178 | }
179 |
180 | , keyup: function (e) {
181 | switch(e.keyCode) {
182 | case 40: // down arrow
183 | case 38: // up arrow
184 | break
185 |
186 | case 9: // tab
187 | case 13: // enter
188 | if (!this.shown) return
189 | this.select()
190 | break
191 |
192 | case 27: // escape
193 | if (!this.shown) return
194 | this.hide()
195 | break
196 |
197 | default:
198 | this.lookup()
199 | }
200 |
201 | e.stopPropagation()
202 | e.preventDefault()
203 | }
204 |
205 | , keypress: function (e) {
206 | if (!this.shown) return
207 |
208 | switch(e.keyCode) {
209 | case 9: // tab
210 | case 13: // enter
211 | case 27: // escape
212 | e.preventDefault()
213 | break
214 |
215 | case 38: // up arrow
216 | //if (e.type != 'keydown') break
217 | e.preventDefault()
218 | this.prev()
219 | break
220 |
221 | case 40: // down arrow
222 | //if (e.type != 'keydown') break
223 | e.preventDefault()
224 | this.next()
225 | break
226 | }
227 |
228 | e.stopPropagation()
229 | }
230 |
231 | , blur: function (e) {
232 | var that = this
233 | setTimeout(function () { that.hide() }, 150)
234 | }
235 |
236 | , click: function (e) {
237 | e.stopPropagation()
238 | e.preventDefault()
239 | this.select()
240 | this.$element.focus()
241 | }
242 |
243 | , mouseenter: function (e) {
244 | this.$menu.find('.active').removeClass('active')
245 | $(e.currentTarget).addClass('active')
246 | }
247 |
248 | }
249 |
250 |
251 | /* TYPEAHEAD PLUGIN DEFINITION
252 | * =========================== */
253 |
254 | $.fn.typeahead = function (option) {
255 | return this.each(function () {
256 | var $this = $(this)
257 | , data = $this.data('typeahead')
258 | , options = typeof option == 'object' && option
259 | if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
260 | if (typeof option == 'string') data[option]()
261 | })
262 | }
263 |
264 | $.fn.typeahead.defaults = {
265 | source: []
266 | , items: 8
267 | , menu: ''
268 | , item: ' '
269 | }
270 |
271 | $.fn.typeahead.Constructor = Typeahead
272 |
273 |
274 | /* TYPEAHEAD DATA-API
275 | * ================== */
276 |
277 | $(function () {
278 | $('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
279 | var $this = $(this)
280 | if ($this.data('typeahead')) return
281 | e.preventDefault()
282 | $this.typeahead($this.data())
283 | })
284 | })
285 |
286 | }(window.jQuery);
--------------------------------------------------------------------------------
/public/js/jquery-ui-sliderAccess.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery UI Slider Access
3 | * By: Trent Richardson [http://trentrichardson.com]
4 | * Version 0.3
5 | * Last Modified: 10/20/2012
6 | *
7 | * Copyright 2011 Trent Richardson
8 | * Dual licensed under the MIT and GPL licenses.
9 | * http://trentrichardson.com/Impromptu/GPL-LICENSE.txt
10 | * http://trentrichardson.com/Impromptu/MIT-LICENSE.txt
11 | *
12 | */
13 | (function($){
14 |
15 | $.fn.extend({
16 | sliderAccess: function(options){
17 | options = options || {};
18 | options.touchonly = options.touchonly !== undefined? options.touchonly : true; // by default only show it if touch device
19 |
20 | if(options.touchonly === true && !("ontouchend" in document))
21 | return $(this);
22 |
23 | return $(this).each(function(i,obj){
24 | var $t = $(this),
25 | o = $.extend({},{
26 | where: 'after',
27 | step: $t.slider('option','step'),
28 | upIcon: 'ui-icon-plus',
29 | downIcon: 'ui-icon-minus',
30 | text: false,
31 | upText: '+',
32 | downText: '-',
33 | buttonset: true,
34 | buttonsetTag: 'span',
35 | isRTL: false
36 | }, options),
37 | $buttons = $('<'+ o.buttonsetTag +' class="ui-slider-access">'+
38 | ''+ o.downText +' '+
39 | ''+ o.upText +' '+
40 | ''+ o.buttonsetTag +'>');
41 |
42 | $buttons.children('button').each(function(j, jobj){
43 | var $jt = $(this);
44 | $jt.button({
45 | text: o.text,
46 | icons: { primary: $jt.data('icon') }
47 | })
48 | .click(function(e){
49 | var step = $jt.data('step'),
50 | curr = $t.slider('value'),
51 | newval = curr += step*1,
52 | minval = $t.slider('option','min'),
53 | maxval = $t.slider('option','max'),
54 | slidee = $t.slider("option", "slide") || function(){},
55 | stope = $t.slider("option", "stop") || function(){};
56 |
57 | e.preventDefault();
58 |
59 | if(newval < minval || newval > maxval)
60 | return;
61 |
62 | $t.slider('value', newval);
63 |
64 | slidee.call($t, null, { value: newval });
65 | stope.call($t, null, { value: newval });
66 | });
67 | });
68 |
69 | // before or after
70 | $t[o.where]($buttons);
71 |
72 | if(o.buttonset){
73 | $buttons.removeClass('ui-corner-right').removeClass('ui-corner-left').buttonset();
74 | $buttons.eq(0).addClass('ui-corner-left');
75 | $buttons.eq(1).addClass('ui-corner-right');
76 | }
77 |
78 | // adjust the width so we don't break the original layout
79 | var bOuterWidth = $buttons.css({
80 | marginLeft: ((o.where == 'after' && !o.isRTL) || (o.where == 'before' && o.isRTL)? 10:0),
81 | marginRight: ((o.where == 'before' && !o.isRTL) || (o.where == 'after' && o.isRTL)? 10:0)
82 | }).outerWidth(true) + 5;
83 | var tOuterWidth = $t.outerWidth(true);
84 | $t.css('display','inline-block').width(tOuterWidth-bOuterWidth);
85 | });
86 | }
87 | });
88 |
89 | })(jQuery);
--------------------------------------------------------------------------------
/public/js/jquery-ui-timepicker-addon.css:
--------------------------------------------------------------------------------
1 | .ui-timepicker-div .ui-widget-header { margin-bottom: 8px; }
2 | .ui-timepicker-div dl { text-align: left; }
3 | .ui-timepicker-div dl dt { height: 25px; margin-bottom: -25px; }
4 | .ui-timepicker-div dl dd { margin: 0 10px 10px 65px; }
5 | .ui-timepicker-div td { font-size: 90%; }
6 | .ui-tpicker-grid-label { background: none; border: none; margin: 0; padding: 0; }
7 |
8 | .ui-timepicker-rtl{ direction: rtl; }
9 | .ui-timepicker-rtl dl { text-align: right; }
10 | .ui-timepicker-rtl dl dd { margin: 0 65px 10px 10px; }
--------------------------------------------------------------------------------
/public/js/jquery.cookie.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery Cookie Plugin v1.3.1
3 | * https://github.com/carhartl/jquery-cookie
4 | *
5 | * Copyright 2013 Klaus Hartl
6 | * Released under the MIT license
7 | */
8 | (function (factory) {
9 | if (typeof define === 'function' && define.amd && define.amd.jQuery) {
10 | // AMD. Register as anonymous module.
11 | define(['jquery'], factory);
12 | } else {
13 | // Browser globals.
14 | factory(jQuery);
15 | }
16 | }(function ($) {
17 |
18 | var pluses = /\+/g;
19 |
20 | function raw(s) {
21 | return s;
22 | }
23 |
24 | function decoded(s) {
25 | return decodeURIComponent(s.replace(pluses, ' '));
26 | }
27 |
28 | function converted(s) {
29 | if (s.indexOf('"') === 0) {
30 | // This is a quoted cookie as according to RFC2068, unescape
31 | s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
32 | }
33 | try {
34 | return config.json ? JSON.parse(s) : s;
35 | } catch(er) {}
36 | }
37 |
38 | var config = $.cookie = function (key, value, options) {
39 |
40 | // write
41 | if (value !== undefined) {
42 | options = $.extend({}, config.defaults, options);
43 |
44 | if (typeof options.expires === 'number') {
45 | var days = options.expires, t = options.expires = new Date();
46 | t.setDate(t.getDate() + days);
47 | }
48 |
49 | value = config.json ? JSON.stringify(value) : String(value);
50 |
51 | return (document.cookie = [
52 | encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value),
53 | options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
54 | options.path ? '; path=' + options.path : '',
55 | options.domain ? '; domain=' + options.domain : '',
56 | options.secure ? '; secure' : ''
57 | ].join(''));
58 | }
59 |
60 | // read
61 | var decode = config.raw ? raw : decoded;
62 | var cookies = document.cookie.split('; ');
63 | var result = key ? undefined : {};
64 | for (var i = 0, l = cookies.length; i < l; i++) {
65 | var parts = cookies[i].split('=');
66 | var name = decode(parts.shift());
67 | var cookie = decode(parts.join('='));
68 |
69 | if (key && key === name) {
70 | result = converted(cookie);
71 | break;
72 | }
73 |
74 | if (!key) {
75 | result[name] = converted(cookie);
76 | }
77 | }
78 |
79 | return result;
80 | };
81 |
82 | config.defaults = {};
83 |
84 | $.removeCookie = function (key, options) {
85 | if ($.cookie(key) !== undefined) {
86 | $.cookie(key, '', $.extend(options, { expires: -1 }));
87 | return true;
88 | }
89 | return false;
90 | };
91 |
92 | }));
93 |
--------------------------------------------------------------------------------
/public/js/selectdate.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | if($('#dt_from').val() != 'from')
3 | $('#toggleDateTimePicker').parent().addClass('active');
4 | else
5 | $('#dateTimePicker').hide();
6 | });
7 |
8 | $(function() {
9 | $('#toggleDateTimePicker').click(function() {
10 | $('#dateTimePicker').toggle(400);
11 | });
12 | });
13 |
14 | $(function() {
15 | var startDateTextBox = $('#dt_from');
16 | var endDateTextBox = $('#dt_to');
17 | startDateTextBox.datetimepicker({
18 | dateFormat: 'yy-mm-dd',
19 | onClose: function(dateText, inst) {
20 | setDestinationDate(startDateTextBox, endDateTextBox, dateText);
21 | }
22 | });
23 | endDateTextBox.datetimepicker({
24 | dateFormat: 'yy-mm-dd',
25 | onClose: function(dateText, inst) {
26 | setDestinationDate(endDateTextBox, startDateTextBox, dateText);
27 | }
28 | });
29 | });
30 |
31 | function getURLParameter(name) {
32 | return decodeURIComponent(
33 | (RegExp(name + '=' + '(.+?)(&|$)').exec(location.search)||[,""])[1]
34 | );
35 | }
36 |
37 | function setDestinationDate(srcDateBox, destDateBox, dateText)
38 | {
39 | if (destDateBox.val() == destDateBox.prop("defaultValue"))
40 | destDateBox.val(dateText);
41 | else {
42 | var startDate = $('#dt_from').datetimepicker('getDate');
43 | var endDate = $('#dt_to').datetimepicker('getDate');
44 | if (startDate > endDate)
45 | destDateBox.datetimepicker('setDate', srcDateBox.datetimepicker('getDate'));
46 | }
47 | }
48 |
49 | function formatSelectedDate(date) {
50 | return "" + zeroPad(date.getMonth() + 1) + "/" +
51 | zeroPad(date.getDate()) + "/"
52 | + date.getFullYear() + " "
53 | + zeroPad(date.getHours()) + ":" +
54 | zeroPad(date.getMinutes());
55 | }
56 |
57 | function selectDt() {
58 | dt_from = $('#dt_from').datetimepicker('getDate');
59 | dt_to = $('#dt_to').datetimepicker('getDate');
60 | window.location = buildGraphiteDateUrl(dt_from, dt_to);
61 | return true;
62 | }
63 |
64 | function buildGraphiteDateUrl(dt_from, dt_to)
65 | {
66 | from = buildGraphiteDateString(dt_from);
67 | to = buildGraphiteDateString(dt_to);
68 | params = "time/" + from + "/" + to;
69 | newurl = document.URL.replace(/#/g, '');
70 | regex = /((time|\?*&from=|\?*&until=).+)/g;
71 | if (newurl.match(regex)) {
72 | newurl = newurl.replace(regex,"");
73 | }
74 | return newurl + params;
75 | }
76 |
77 | function buildGraphiteDateString(date)
78 | {
79 | val = zeroPad(date.getHours()) + "%3A" + zeroPad(date.getMinutes()) + "_" + date.getFullYear() + zeroPad(date.getMonth()+1) + zeroPad(date.getDate());
80 | return val;
81 | }
82 |
83 | function zeroPad(num) {
84 | num = "" + num
85 | if (num.length == 1) {
86 | num = "0" + num
87 | }
88 | return num
89 | }
90 |
--------------------------------------------------------------------------------
/sample/README.md:
--------------------------------------------------------------------------------
1 | A sample dashboard, place these files in _graph_templates/dashboards/email_ and
2 | you will see a new dashboard in the UI
3 |
4 | 2 sample images are included, one of the normal dashboard and one of the full
5 | screen view
6 |
--------------------------------------------------------------------------------
/sample/email-full-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ripienaar/gdash/0f334f006b39c6f72b5e496fd8ed0f203aa4495f/sample/email-full-screen.png
--------------------------------------------------------------------------------
/sample/email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ripienaar/gdash/0f334f006b39c6f72b5e496fd8ed0f203aa4495f/sample/email.png
--------------------------------------------------------------------------------
/sample/email/cpu.graph:
--------------------------------------------------------------------------------
1 | title "Combined CPU Usage"
2 | vtitle "percent"
3 | area :stacked
4 | description "The combined CPU usage for all Exim Anti Spam servers"
5 |
6 | field :iowait, :scale => 0.001,
7 | :color => "red",
8 | :alias => "IO Wait",
9 | :data => "sumSeries(derivative(mw*.munin.cpu.iowait))"
10 |
11 | field :system, :scale => 0.001,
12 | :color => "orange",
13 | :alias => "System",
14 | :data => "sumSeries(derivative(mw*.munin.cpu.system))"
15 |
16 | field :user, :scale => 0.001,
17 | :color => "yellow",
18 | :alias => "User",
19 | :data => "sumSeries(derivative(mw*.munin.cpu.user))"
20 |
--------------------------------------------------------------------------------
/sample/email/dash.yaml:
--------------------------------------------------------------------------------
1 | :name: Email Metrics
2 | :description: Hourly metrics for the email system
3 |
--------------------------------------------------------------------------------
/sample/email/io.graph:
--------------------------------------------------------------------------------
1 | title "Combined IO"
2 | area :first
3 | vtitle "blocks"
4 |
5 | field :down, :color => "blue",
6 | :alias => "Writes",
7 | :data => "sumSeries(derivative(mw*.munin.iostat.dev*_write))"
8 |
9 | field :up, :color => "green",
10 | :alias => "Reads",
11 | :data => "sumSeries(derivative(mw*.munin.iostat.dev*_read))"
12 |
13 |
--------------------------------------------------------------------------------
/sample/email/load.graph:
--------------------------------------------------------------------------------
1 | title "Combined Load"
2 | area :all
3 |
4 | field :up, :color => "green",
5 | :alias => "Up",
6 | :data => "sumSeries(mw*.munin.load.load)"
7 |
--------------------------------------------------------------------------------
/sample/email/network.graph:
--------------------------------------------------------------------------------
1 | title "Combined Network Usage"
2 | vtitle "bps"
3 | area :first
4 |
5 | field :up, :color => "green",
6 | :alias => "Up",
7 | :data => "sumSeries(nonNegativeDerivative(mw*.munin.if_*.up))"
8 |
9 | field :down, :color => "blue",
10 | :alias => "Down",
11 | :data => "sumSeries(nonNegativeDerivative(mw*.munin.if_*.down))"
12 |
--------------------------------------------------------------------------------
/tools/dashboards-validation.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'optparse'
4 | require 'gdash'
5 | require 'gdash/dashboard'
6 |
7 | options = {}
8 |
9 | opt_parser = OptionParser.new do |opt|
10 | opt.banner = "Usage: " + opt.program_name + " [OPTIONS]"
11 |
12 | opt.on("-p", "--path PATH", String, "dashboard path") do |path|
13 | options[:path] = path
14 | end
15 |
16 | opt.on("-h","--help","help") do
17 | puts opt_parser
18 | end
19 | end
20 |
21 | begin
22 | opt_parser.parse!
23 | if options[:path].nil?
24 | puts "Missing options path."
25 | puts opt_parser
26 | abort()
27 | end
28 | rescue OptionParser::InvalidOption, OptionParser::MissingArgument
29 | puts $!.to_s
30 | puts opt_parser
31 | abort()
32 | end
33 |
34 | unless File.directory? options[:path]
35 | puts "Path #{options[:path]} does not exists."
36 | puts opt_parser
37 | abort()
38 | end
39 |
40 | errors = []
41 |
42 | Dir.foreach(options[:path]).each do |dashboard|
43 | next if dashboard == '.' or dashboard == '..'
44 | begin
45 | graph_directory = File.join(options[:path], dashboard)
46 |
47 | Dir.foreach(graph_directory).each do |category|
48 | next if category == '.' or category == '..'
49 | category_dir = File.join(graph_directory, category)
50 | category_desc = File.join(category_dir, 'dash.yaml')
51 |
52 | if !File.exists?(category_desc)
53 | errors << "The dashboard description file (" + category_desc + ") is missing."
54 | end
55 |
56 | graphs = Dir.entries(category_dir).select{|f| f.match(/\.graph$/)}
57 | for graph in graphs
58 | begin
59 | full_path = File.join(category_dir, graph)
60 | GraphiteGraph.new(full_path)
61 | rescue Exception => e
62 | errors << "Can't parse the graph file " + full_path + ":"
63 | errors << "\t " + e.message
64 | end
65 | end
66 | end
67 | rescue Exception => e
68 | p e
69 | end
70 | end
71 |
72 | if !errors.empty?
73 | abort(errors.join("\n"))
74 | end
75 |
--------------------------------------------------------------------------------
/views/README.md:
--------------------------------------------------------------------------------
1 | ../README.md
--------------------------------------------------------------------------------
/views/_interval_filter.erb:
--------------------------------------------------------------------------------
1 |
2 | <% @interval_filters.each do |options| %>
3 | ><%= link_to_interval(options) %>
4 | <% end %>
5 | Select Date
6 |
7 |
12 |
--------------------------------------------------------------------------------
/views/bootstrap/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 | // General toggle styles
25 | .accordion-toggle {
26 | cursor: pointer;
27 | }
28 |
29 | // Inner needs the styles because you can't animate properly with any styles on the element
30 | .accordion-inner {
31 | padding: 9px 15px;
32 | border-top: 1px solid #e5e5e5;
33 | }
34 |
--------------------------------------------------------------------------------
/views/bootstrap/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 |
--------------------------------------------------------------------------------
/views/bootstrap/bootstrap.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v2.0.4
3 | *
4 | * Copyright 2012 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world @twitter by @mdo and @fat.
9 | */
10 |
11 | // CSS Reset
12 | @import "reset.less";
13 |
14 | // Core variables and mixins
15 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc
16 | @import "mixins.less";
17 |
18 | // Grid system and page structure
19 | @import "scaffolding.less";
20 | @import "grid.less";
21 | @import "layouts.less";
22 |
23 | // Base CSS
24 | @import "type.less";
25 | @import "code.less";
26 | @import "forms.less";
27 | @import "tables.less";
28 |
29 | // Components: common
30 | @import "sprites.less";
31 | @import "dropdowns.less";
32 | @import "wells.less";
33 | @import "component-animations.less";
34 | @import "close.less";
35 |
36 | // Components: Buttons & Alerts
37 | @import "buttons.less";
38 | @import "button-groups.less";
39 | @import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less
40 |
41 | // Components: Nav
42 | @import "navs.less";
43 | @import "navbar.less";
44 | @import "breadcrumbs.less";
45 | @import "pagination.less";
46 | @import "pager.less";
47 |
48 | // Components: Popovers
49 | @import "modals.less";
50 | @import "tooltip.less";
51 | @import "popovers.less";
52 |
53 | // Components: Misc
54 | @import "thumbnails.less";
55 | @import "labels-badges.less";
56 | @import "progress-bars.less";
57 | @import "accordion.less";
58 | @import "carousel.less";
59 | @import "hero-unit.less";
60 |
61 | // Utility classes
62 | @import "utilities.less"; // Has to be last to override when necessary
63 |
64 | @import "responsive.less";
65 |
--------------------------------------------------------------------------------
/views/bootstrap/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 |
--------------------------------------------------------------------------------
/views/bootstrap/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 | // Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it
45 | .btn-group > .btn:last-child,
46 | .btn-group > .dropdown-toggle {
47 | -webkit-border-top-right-radius: 4px;
48 | -moz-border-radius-topright: 4px;
49 | border-top-right-radius: 4px;
50 | -webkit-border-bottom-right-radius: 4px;
51 | -moz-border-radius-bottomright: 4px;
52 | border-bottom-right-radius: 4px;
53 | }
54 | // Reset corners for large buttons
55 | .btn-group > .btn.large:first-child {
56 | margin-left: 0;
57 | -webkit-border-top-left-radius: 6px;
58 | -moz-border-radius-topleft: 6px;
59 | border-top-left-radius: 6px;
60 | -webkit-border-bottom-left-radius: 6px;
61 | -moz-border-radius-bottomleft: 6px;
62 | border-bottom-left-radius: 6px;
63 | }
64 | .btn-group > .btn.large:last-child,
65 | .btn-group > .large.dropdown-toggle {
66 | -webkit-border-top-right-radius: 6px;
67 | -moz-border-radius-topright: 6px;
68 | border-top-right-radius: 6px;
69 | -webkit-border-bottom-right-radius: 6px;
70 | -moz-border-radius-bottomright: 6px;
71 | border-bottom-right-radius: 6px;
72 | }
73 |
74 | // On hover/focus/active, bring the proper btn to front
75 | .btn-group > .btn:hover,
76 | .btn-group > .btn:focus,
77 | .btn-group > .btn:active,
78 | .btn-group > .btn.active {
79 | z-index: 2;
80 | }
81 |
82 | // On active and open, don't show outline
83 | .btn-group .dropdown-toggle:active,
84 | .btn-group.open .dropdown-toggle {
85 | outline: 0;
86 | }
87 |
88 |
89 |
90 | // Split button dropdowns
91 | // ----------------------
92 |
93 | // Give the line between buttons some depth
94 | .btn-group > .dropdown-toggle {
95 | padding-left: 8px;
96 | padding-right: 8px;
97 | .box-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)");
98 | *padding-top: 4px;
99 | *padding-bottom: 4px;
100 | }
101 | .btn-group > .btn-mini.dropdown-toggle {
102 | padding-left: 5px;
103 | padding-right: 5px;
104 | }
105 | .btn-group > .btn-small.dropdown-toggle {
106 | *padding-top: 4px;
107 | *padding-bottom: 4px;
108 | }
109 | .btn-group > .btn-large.dropdown-toggle {
110 | padding-left: 12px;
111 | padding-right: 12px;
112 | }
113 |
114 | .btn-group.open {
115 |
116 | // The clickable button for toggling the menu
117 | // Remove the gradient and set the same inset shadow as the :active state
118 | .dropdown-toggle {
119 | background-image: none;
120 | .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)");
121 | }
122 |
123 | // Keep the hover's background when dropdown is open
124 | .btn.dropdown-toggle {
125 | background-color: @btnBackgroundHighlight;
126 | }
127 | .btn-primary.dropdown-toggle {
128 | background-color: @btnPrimaryBackgroundHighlight;
129 | }
130 | .btn-warning.dropdown-toggle {
131 | background-color: @btnWarningBackgroundHighlight;
132 | }
133 | .btn-danger.dropdown-toggle {
134 | background-color: @btnDangerBackgroundHighlight;
135 | }
136 | .btn-success.dropdown-toggle {
137 | background-color: @btnSuccessBackgroundHighlight;
138 | }
139 | .btn-info.dropdown-toggle {
140 | background-color: @btnInfoBackgroundHighlight;
141 | }
142 | .btn-inverse.dropdown-toggle {
143 | background-color: @btnInverseBackgroundHighlight;
144 | }
145 | }
146 |
147 |
148 | // Reposition the caret
149 | .btn .caret {
150 | margin-top: 7px;
151 | margin-left: 0;
152 | }
153 | .btn:hover .caret,
154 | .open.btn-group .caret {
155 | .opacity(100);
156 | }
157 | // Carets in other button sizes
158 | .btn-mini .caret {
159 | margin-top: 5px;
160 | }
161 | .btn-small .caret {
162 | margin-top: 6px;
163 | }
164 | .btn-large .caret {
165 | margin-top: 6px;
166 | border-left-width: 5px;
167 | border-right-width: 5px;
168 | border-top-width: 5px;
169 | }
170 | // Upside down carets for .dropup
171 | .dropup .btn-large .caret {
172 | border-bottom: 5px solid @black;
173 | border-top: 0;
174 | }
175 |
176 |
177 |
178 | // Account for other colors
179 | .btn-primary,
180 | .btn-warning,
181 | .btn-danger,
182 | .btn-info,
183 | .btn-success,
184 | .btn-inverse {
185 | .caret {
186 | border-top-color: @white;
187 | border-bottom-color: @white;
188 | .opacity(75);
189 | }
190 | }
191 |
192 |
--------------------------------------------------------------------------------
/views/bootstrap/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 | *line-height: 20px;
17 | color: @grayDark;
18 | text-align: center;
19 | text-shadow: 0 1px 1px rgba(255,255,255,.75);
20 | vertical-align: middle;
21 | cursor: pointer;
22 | .buttonBackground(@btnBackground, @btnBackgroundHighlight);
23 | border: 1px solid @btnBorder;
24 | *border: 0; // Remove the border to prevent IE7's black border on input:focus
25 | border-bottom-color: darken(@btnBorder, 10%);
26 | .border-radius(4px);
27 | .ie7-restore-left-whitespace(); // Give IE7 some love
28 | .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)");
29 | }
30 |
31 | // Hover state
32 | .btn:hover {
33 | color: @grayDark;
34 | text-decoration: none;
35 | background-color: darken(@white, 10%);
36 | *background-color: darken(@white, 15%); /* Buttons in IE7 don't get borders, so darken on hover */
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-color: darken(@white, 10%);
53 | background-color: darken(@white, 15%) e("\9");
54 | background-image: none;
55 | outline: 0;
56 | .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)");
57 | }
58 |
59 | // Disabled state
60 | .btn.disabled,
61 | .btn[disabled] {
62 | cursor: default;
63 | background-color: darken(@white, 10%);
64 | background-image: none;
65 | .opacity(65);
66 | .box-shadow(none);
67 | }
68 |
69 |
70 | // Button Sizes
71 | // --------------------------------------------------
72 |
73 | // Large
74 | .btn-large {
75 | padding: 9px 14px;
76 | font-size: @baseFontSize + 2px;
77 | line-height: normal;
78 | .border-radius(5px);
79 | }
80 | .btn-large [class^="icon-"] {
81 | margin-top: 1px;
82 | }
83 |
84 | // Small
85 | .btn-small {
86 | padding: 5px 9px;
87 | font-size: @baseFontSize - 2px;
88 | line-height: @baseLineHeight - 2px;
89 | }
90 | .btn-small [class^="icon-"] {
91 | margin-top: -1px;
92 | }
93 |
94 | // Mini
95 | .btn-mini {
96 | padding: 2px 6px;
97 | font-size: @baseFontSize - 2px;
98 | line-height: @baseLineHeight - 4px;
99 | }
100 |
101 |
102 | // Alternate buttons
103 | // --------------------------------------------------
104 |
105 | // Set text color
106 | // -------------------------
107 | .btn-primary,
108 | .btn-primary:hover,
109 | .btn-warning,
110 | .btn-warning:hover,
111 | .btn-danger,
112 | .btn-danger:hover,
113 | .btn-success,
114 | .btn-success:hover,
115 | .btn-info,
116 | .btn-info:hover,
117 | .btn-inverse,
118 | .btn-inverse:hover {
119 | color: @white;
120 | text-shadow: 0 -1px 0 rgba(0,0,0,.25);
121 | }
122 | // Provide *some* extra contrast for those who can get it
123 | .btn-primary.active,
124 | .btn-warning.active,
125 | .btn-danger.active,
126 | .btn-success.active,
127 | .btn-info.active,
128 | .btn-inverse.active {
129 | color: rgba(255,255,255,.75);
130 | }
131 |
132 | // Set the backgrounds
133 | // -------------------------
134 | .btn {
135 | // reset here as of 2.0.3 due to Recess property order
136 | border-color: #ccc;
137 | border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);
138 | }
139 | .btn-primary {
140 | .buttonBackground(@btnPrimaryBackground, @btnPrimaryBackgroundHighlight);
141 | }
142 | // Warning appears are orange
143 | .btn-warning {
144 | .buttonBackground(@btnWarningBackground, @btnWarningBackgroundHighlight);
145 | }
146 | // Danger and error appear as red
147 | .btn-danger {
148 | .buttonBackground(@btnDangerBackground, @btnDangerBackgroundHighlight);
149 | }
150 | // Success appears as green
151 | .btn-success {
152 | .buttonBackground(@btnSuccessBackground, @btnSuccessBackgroundHighlight);
153 | }
154 | // Info appears as a neutral blue
155 | .btn-info {
156 | .buttonBackground(@btnInfoBackground, @btnInfoBackgroundHighlight);
157 | }
158 | // Inverse appears as dark gray
159 | .btn-inverse {
160 | .buttonBackground(@btnInverseBackground, @btnInverseBackgroundHighlight);
161 | }
162 |
163 |
164 | // Cross-browser Jank
165 | // --------------------------------------------------
166 |
167 | button.btn,
168 | input[type="submit"].btn {
169 |
170 | // Firefox 3.6 only I believe
171 | &::-moz-focus-inner {
172 | padding: 0;
173 | border: 0;
174 | }
175 |
176 | // IE7 has some default padding on button controls
177 | *padding-top: 2px;
178 | *padding-bottom: 2px;
179 | &.btn-large {
180 | *padding-top: 7px;
181 | *padding-bottom: 7px;
182 | }
183 | &.btn-small {
184 | *padding-top: 3px;
185 | *padding-bottom: 3px;
186 | }
187 | &.btn-mini {
188 | *padding-top: 1px;
189 | *padding-bottom: 1px;
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/views/bootstrap/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 |
--------------------------------------------------------------------------------
/views/bootstrap/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 | cursor: pointer;
16 | .opacity(40);
17 | }
18 | }
19 |
20 | // Additional properties for button version
21 | // iOS requires the button element instead of an anchor tag.
22 | // If you want the anchor version, it requires `href="#"`.
23 | button.close {
24 | padding: 0;
25 | cursor: pointer;
26 | background: transparent;
27 | border: 0;
28 | -webkit-appearance: none;
29 | }
--------------------------------------------------------------------------------
/views/bootstrap/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 | word-break: break-all;
31 | word-wrap: break-word;
32 | white-space: pre;
33 | white-space: pre-wrap;
34 | background-color: #f5f5f5;
35 | border: 1px solid #ccc; // fallback for IE7-8
36 | border: 1px solid rgba(0,0,0,.15);
37 | .border-radius(4px);
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 | }
--------------------------------------------------------------------------------
/views/bootstrap/component-animations.less:
--------------------------------------------------------------------------------
1 | // COMPONENT ANIMATIONS
2 | // --------------------
3 |
4 | .fade {
5 | opacity: 0;
6 | .transition(opacity .15s linear);
7 | &.in {
8 | opacity: 1;
9 | }
10 | }
11 |
12 | .collapse {
13 | position: relative;
14 | height: 0;
15 | overflow: hidden;
16 | .transition(height .35s ease);
17 | &.in {
18 | height: auto;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/views/bootstrap/dropdowns.less:
--------------------------------------------------------------------------------
1 | // DROPDOWN MENUS
2 | // --------------
3 |
4 | // Use the .menu class on any element within the topbar or ul.tabs and you'll get some superfancy dropdowns
5 | .dropup,
6 | .dropdown {
7 | position: relative;
8 | }
9 | .dropdown-toggle {
10 | // The caret makes the toggle a bit too tall in IE7
11 | *margin-bottom: -3px;
12 | }
13 | .dropdown-toggle:active,
14 | .open .dropdown-toggle {
15 | outline: 0;
16 | }
17 |
18 | // Dropdown arrow/caret
19 | // --------------------
20 | .caret {
21 | display: inline-block;
22 | width: 0;
23 | height: 0;
24 | vertical-align: top;
25 | border-top: 4px solid @black;
26 | border-right: 4px solid transparent;
27 | border-left: 4px solid transparent;
28 | content: "";
29 | .opacity(30);
30 | }
31 |
32 | // Place the caret
33 | .dropdown .caret {
34 | margin-top: 8px;
35 | margin-left: 2px;
36 | }
37 | .dropdown:hover .caret,
38 | .open .caret {
39 | .opacity(100);
40 | }
41 |
42 | // The dropdown menu (ul)
43 | // ----------------------
44 | .dropdown-menu {
45 | position: absolute;
46 | top: 100%;
47 | left: 0;
48 | z-index: @zindexDropdown;
49 | display: none; // none by default, but block on "open" of the menu
50 | float: left;
51 | min-width: 160px;
52 | padding: 4px 0;
53 | margin: 1px 0 0; // override default ul
54 | list-style: none;
55 | background-color: @dropdownBackground;
56 | border: 1px solid #ccc;
57 | border: 1px solid rgba(0,0,0,.2);
58 | *border-right-width: 2px;
59 | *border-bottom-width: 2px;
60 | .border-radius(5px);
61 | .box-shadow(0 5px 10px rgba(0,0,0,.2));
62 | -webkit-background-clip: padding-box;
63 | -moz-background-clip: padding;
64 | background-clip: padding-box;
65 |
66 | // Aligns the dropdown menu to right
67 | &.pull-right {
68 | right: 0;
69 | left: auto;
70 | }
71 |
72 | // Dividers (basically an hr) within the dropdown
73 | .divider {
74 | .nav-divider(@dropdownDividerTop, @dropdownDividerBottom);
75 | }
76 |
77 | // Links within the dropdown menu
78 | a {
79 | display: block;
80 | padding: 3px 15px;
81 | clear: both;
82 | font-weight: normal;
83 | line-height: @baseLineHeight;
84 | color: @dropdownLinkColor;
85 | white-space: nowrap;
86 | }
87 | }
88 |
89 | // Hover state
90 | // -----------
91 | .dropdown-menu li > a:hover,
92 | .dropdown-menu .active > a,
93 | .dropdown-menu .active > a:hover {
94 | color: @dropdownLinkColorHover;
95 | text-decoration: none;
96 | background-color: @dropdownLinkBackgroundHover;
97 | }
98 |
99 | // Open state for the dropdown
100 | // ---------------------------
101 | .open {
102 | // IE7's z-index only goes to the nearest positioned ancestor, which would
103 | // make the menu appear below buttons that appeared later on the page
104 | *z-index: @zindexDropdown;
105 |
106 | & > .dropdown-menu {
107 | display: block;
108 | }
109 | }
110 |
111 | // Right aligned dropdowns
112 | // ---------------------------
113 | .pull-right > .dropdown-menu {
114 | right: 0;
115 | left: auto;
116 | }
117 |
118 | // Allow for dropdowns to go bottom up (aka, dropup-menu)
119 | // ------------------------------------------------------
120 | // Just add .dropup after the standard .dropdown class and you're set, bro.
121 | // TODO: abstract this so that the navbar fixed styles are not placed here?
122 | .dropup,
123 | .navbar-fixed-bottom .dropdown {
124 | // Reverse the caret
125 | .caret {
126 | border-top: 0;
127 | border-bottom: 4px solid @black;
128 | content: "\2191";
129 | }
130 | // Different positioning for bottom up menu
131 | .dropdown-menu {
132 | top: auto;
133 | bottom: 100%;
134 | margin-bottom: 1px;
135 | }
136 | }
137 |
138 | // Typeahead
139 | // ---------
140 | .typeahead {
141 | margin-top: 2px; // give it some space to breathe
142 | .border-radius(4px);
143 | }
144 |
--------------------------------------------------------------------------------
/views/bootstrap/grid.less:
--------------------------------------------------------------------------------
1 | // Fixed (940px)
2 | #grid > .core(@gridColumnWidth, @gridGutterWidth);
3 |
4 | // Fluid (940px)
5 | #grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth);
--------------------------------------------------------------------------------
/views/bootstrap/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 |
--------------------------------------------------------------------------------
/views/bootstrap/labels-badges.less:
--------------------------------------------------------------------------------
1 | // LABELS & BADGES
2 | // ---------------
3 |
4 | // Base classes
5 | .label,
6 | .badge {
7 | font-size: @baseFontSize * .846;
8 | font-weight: bold;
9 | line-height: 14px; // ensure proper line-height if floated
10 | color: @white;
11 | vertical-align: baseline;
12 | white-space: nowrap;
13 | text-shadow: 0 -1px 0 rgba(0,0,0,.25);
14 | background-color: @grayLight;
15 | }
16 | // Set unique padding and border-radii
17 | .label {
18 | padding: 1px 4px 2px;
19 | .border-radius(3px);
20 | }
21 | .badge {
22 | padding: 1px 9px 2px;
23 | .border-radius(9px);
24 | }
25 |
26 | // Hover state, but only for links
27 | a {
28 | &.label:hover,
29 | &.badge:hover {
30 | color: @white;
31 | text-decoration: none;
32 | cursor: pointer;
33 | }
34 | }
35 |
36 | // Colors
37 | // Only give background-color difference to links (and to simplify, we don't qualifty with `a` but [href] attribute)
38 | .label,
39 | .badge {
40 | // Important (red)
41 | &-important { background-color: @errorText; }
42 | &-important[href] { background-color: darken(@errorText, 10%); }
43 | // Warnings (orange)
44 | &-warning { background-color: @orange; }
45 | &-warning[href] { background-color: darken(@orange, 10%); }
46 | // Success (green)
47 | &-success { background-color: @successText; }
48 | &-success[href] { background-color: darken(@successText, 10%); }
49 | // Info (turquoise)
50 | &-info { background-color: @infoText; }
51 | &-info[href] { background-color: darken(@infoText, 10%); }
52 | // Inverse (black)
53 | &-inverse { background-color: @grayDark; }
54 | &-inverse[href] { background-color: darken(@grayDark, 10%); }
55 | }
56 |
--------------------------------------------------------------------------------
/views/bootstrap/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-right: @gridGutterWidth;
15 | padding-left: @gridGutterWidth;
16 | .clearfix();
17 | }
--------------------------------------------------------------------------------
/views/bootstrap/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 resides)
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 |
--------------------------------------------------------------------------------
/views/bootstrap/navs.less:
--------------------------------------------------------------------------------
1 | // NAVIGATIONS
2 | // -----------
3 |
4 |
5 |
6 | // BASE CLASS
7 | // ----------
8 |
9 | .nav {
10 | margin-left: 0;
11 | margin-bottom: @baseLineHeight;
12 | list-style: none;
13 | }
14 |
15 | // Make links block level
16 | .nav > li > a {
17 | display: block;
18 | }
19 | .nav > li > a:hover {
20 | text-decoration: none;
21 | background-color: @grayLighter;
22 | }
23 |
24 | // Redeclare pull classes because of specifity
25 | .nav > .pull-right {
26 | float: right;
27 | }
28 |
29 | // Nav headers (for dropdowns and lists)
30 | .nav .nav-header {
31 | display: block;
32 | padding: 3px 15px;
33 | font-size: 11px;
34 | font-weight: bold;
35 | line-height: @baseLineHeight;
36 | color: @grayLight;
37 | text-shadow: 0 1px 0 rgba(255,255,255,.5);
38 | text-transform: uppercase;
39 | }
40 | // Space them out when they follow another list item (link)
41 | .nav li + .nav-header {
42 | margin-top: 9px;
43 | }
44 |
45 |
46 | // NAV LIST
47 | // --------
48 |
49 | .nav-list {
50 | padding-left: 15px;
51 | padding-right: 15px;
52 | margin-bottom: 0;
53 | }
54 | .nav-list > li > a,
55 | .nav-list .nav-header {
56 | margin-left: -15px;
57 | margin-right: -15px;
58 | text-shadow: 0 1px 0 rgba(255,255,255,.5);
59 | }
60 | .nav-list > li > a {
61 | padding: 3px 15px;
62 | }
63 | .nav-list > .active > a,
64 | .nav-list > .active > a:hover {
65 | color: @white;
66 | text-shadow: 0 -1px 0 rgba(0,0,0,.2);
67 | background-color: @linkColor;
68 | }
69 | .nav-list [class^="icon-"] {
70 | margin-right: 2px;
71 | }
72 | // Dividers (basically an hr) within the dropdown
73 | .nav-list .divider {
74 | .nav-divider();
75 | }
76 |
77 |
78 |
79 | // TABS AND PILLS
80 | // -------------
81 |
82 | // Common styles
83 | .nav-tabs,
84 | .nav-pills {
85 | .clearfix();
86 | }
87 | .nav-tabs > li,
88 | .nav-pills > li {
89 | float: left;
90 | }
91 | .nav-tabs > li > a,
92 | .nav-pills > li > a {
93 | padding-right: 12px;
94 | padding-left: 12px;
95 | margin-right: 2px;
96 | line-height: 14px; // keeps the overall height an even number
97 | }
98 |
99 | // TABS
100 | // ----
101 |
102 | // Give the tabs something to sit on
103 | .nav-tabs {
104 | border-bottom: 1px solid #ddd;
105 | }
106 | // Make the list-items overlay the bottom border
107 | .nav-tabs > li {
108 | margin-bottom: -1px;
109 | }
110 | // Actual tabs (as links)
111 | .nav-tabs > li > a {
112 | padding-top: 8px;
113 | padding-bottom: 8px;
114 | line-height: @baseLineHeight;
115 | border: 1px solid transparent;
116 | .border-radius(4px 4px 0 0);
117 | &:hover {
118 | border-color: @grayLighter @grayLighter #ddd;
119 | }
120 | }
121 | // Active state, and it's :hover to override normal :hover
122 | .nav-tabs > .active > a,
123 | .nav-tabs > .active > a:hover {
124 | color: @gray;
125 | background-color: @white;
126 | border: 1px solid #ddd;
127 | border-bottom-color: transparent;
128 | cursor: default;
129 | }
130 |
131 |
132 | // PILLS
133 | // -----
134 |
135 | // Links rendered as pills
136 | .nav-pills > li > a {
137 | padding-top: 8px;
138 | padding-bottom: 8px;
139 | margin-top: 2px;
140 | margin-bottom: 2px;
141 | .border-radius(5px);
142 | }
143 |
144 | // Active state
145 | .nav-pills > .active > a,
146 | .nav-pills > .active > a:hover {
147 | color: @white;
148 | background-color: @linkColor;
149 | }
150 |
151 |
152 |
153 | // STACKED NAV
154 | // -----------
155 |
156 | // Stacked tabs and pills
157 | .nav-stacked > li {
158 | float: none;
159 | }
160 | .nav-stacked > li > a {
161 | margin-right: 0; // no need for the gap between nav items
162 | }
163 |
164 | // Tabs
165 | .nav-tabs.nav-stacked {
166 | border-bottom: 0;
167 | }
168 | .nav-tabs.nav-stacked > li > a {
169 | border: 1px solid #ddd;
170 | .border-radius(0);
171 | }
172 | .nav-tabs.nav-stacked > li:first-child > a {
173 | .border-radius(4px 4px 0 0);
174 | }
175 | .nav-tabs.nav-stacked > li:last-child > a {
176 | .border-radius(0 0 4px 4px);
177 | }
178 | .nav-tabs.nav-stacked > li > a:hover {
179 | border-color: #ddd;
180 | z-index: 2;
181 | }
182 |
183 | // Pills
184 | .nav-pills.nav-stacked > li > a {
185 | margin-bottom: 3px;
186 | }
187 | .nav-pills.nav-stacked > li:last-child > a {
188 | margin-bottom: 1px; // decrease margin to match sizing of stacked tabs
189 | }
190 |
191 |
192 |
193 | // DROPDOWNS
194 | // ---------
195 |
196 | .nav-tabs .dropdown-menu {
197 | .border-radius(0 0 5px 5px); // remove the top rounded corners here since there is a hard edge above the menu
198 | }
199 | .nav-pills .dropdown-menu {
200 | .border-radius(4px); // make rounded corners match the pills
201 | }
202 |
203 | // Default dropdown links
204 | // -------------------------
205 | // Make carets use linkColor to start
206 | .nav-tabs .dropdown-toggle .caret,
207 | .nav-pills .dropdown-toggle .caret {
208 | border-top-color: @linkColor;
209 | border-bottom-color: @linkColor;
210 | margin-top: 6px;
211 | }
212 | .nav-tabs .dropdown-toggle:hover .caret,
213 | .nav-pills .dropdown-toggle:hover .caret {
214 | border-top-color: @linkColorHover;
215 | border-bottom-color: @linkColorHover;
216 | }
217 |
218 | // Active dropdown links
219 | // -------------------------
220 | .nav-tabs .active .dropdown-toggle .caret,
221 | .nav-pills .active .dropdown-toggle .caret {
222 | border-top-color: @grayDark;
223 | border-bottom-color: @grayDark;
224 | }
225 |
226 | // Active:hover dropdown links
227 | // -------------------------
228 | .nav > .dropdown.active > a:hover {
229 | color: @black;
230 | cursor: pointer;
231 | }
232 |
233 | // Open dropdowns
234 | // -------------------------
235 | .nav-tabs .open .dropdown-toggle,
236 | .nav-pills .open .dropdown-toggle,
237 | .nav > li.dropdown.open.active > a:hover {
238 | color: @white;
239 | background-color: @grayLight;
240 | border-color: @grayLight;
241 | }
242 | .nav li.dropdown.open .caret,
243 | .nav li.dropdown.open.active .caret,
244 | .nav li.dropdown.open a:hover .caret {
245 | border-top-color: @white;
246 | border-bottom-color: @white;
247 | .opacity(100);
248 | }
249 |
250 | // Dropdowns in stacked tabs
251 | .tabs-stacked .open > a:hover {
252 | border-color: @grayLight;
253 | }
254 |
255 |
256 |
257 | // TABBABLE
258 | // --------
259 |
260 |
261 | // COMMON STYLES
262 | // -------------
263 |
264 | // Clear any floats
265 | .tabbable {
266 | .clearfix();
267 | }
268 | .tab-content {
269 | overflow: auto; // prevent content from running below tabs
270 | }
271 |
272 | // Remove border on bottom, left, right
273 | .tabs-below > .nav-tabs,
274 | .tabs-right > .nav-tabs,
275 | .tabs-left > .nav-tabs {
276 | border-bottom: 0;
277 | }
278 |
279 | // Show/hide tabbable areas
280 | .tab-content > .tab-pane,
281 | .pill-content > .pill-pane {
282 | display: none;
283 | }
284 | .tab-content > .active,
285 | .pill-content > .active {
286 | display: block;
287 | }
288 |
289 |
290 | // BOTTOM
291 | // ------
292 |
293 | .tabs-below > .nav-tabs {
294 | border-top: 1px solid #ddd;
295 | }
296 | .tabs-below > .nav-tabs > li {
297 | margin-top: -1px;
298 | margin-bottom: 0;
299 | }
300 | .tabs-below > .nav-tabs > li > a {
301 | .border-radius(0 0 4px 4px);
302 | &:hover {
303 | border-bottom-color: transparent;
304 | border-top-color: #ddd;
305 | }
306 | }
307 | .tabs-below > .nav-tabs > .active > a,
308 | .tabs-below > .nav-tabs > .active > a:hover {
309 | border-color: transparent #ddd #ddd #ddd;
310 | }
311 |
312 | // LEFT & RIGHT
313 | // ------------
314 |
315 | // Common styles
316 | .tabs-left > .nav-tabs > li,
317 | .tabs-right > .nav-tabs > li {
318 | float: none;
319 | }
320 | .tabs-left > .nav-tabs > li > a,
321 | .tabs-right > .nav-tabs > li > a {
322 | min-width: 74px;
323 | margin-right: 0;
324 | margin-bottom: 3px;
325 | }
326 |
327 | // Tabs on the left
328 | .tabs-left > .nav-tabs {
329 | float: left;
330 | margin-right: 19px;
331 | border-right: 1px solid #ddd;
332 | }
333 | .tabs-left > .nav-tabs > li > a {
334 | margin-right: -1px;
335 | .border-radius(4px 0 0 4px);
336 | }
337 | .tabs-left > .nav-tabs > li > a:hover {
338 | border-color: @grayLighter #ddd @grayLighter @grayLighter;
339 | }
340 | .tabs-left > .nav-tabs .active > a,
341 | .tabs-left > .nav-tabs .active > a:hover {
342 | border-color: #ddd transparent #ddd #ddd;
343 | *border-right-color: @white;
344 | }
345 |
346 | // Tabs on the right
347 | .tabs-right > .nav-tabs {
348 | float: right;
349 | margin-left: 19px;
350 | border-left: 1px solid #ddd;
351 | }
352 | .tabs-right > .nav-tabs > li > a {
353 | margin-left: -1px;
354 | .border-radius(0 4px 4px 0);
355 | }
356 | .tabs-right > .nav-tabs > li > a:hover {
357 | border-color: @grayLighter @grayLighter @grayLighter #ddd;
358 | }
359 | .tabs-right > .nav-tabs .active > a,
360 | .tabs-right > .nav-tabs .active > a:hover {
361 | border-color: #ddd #ddd #ddd transparent;
362 | *border-left-color: @white;
363 | }
364 |
--------------------------------------------------------------------------------
/views/bootstrap/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 | }
--------------------------------------------------------------------------------
/views/bootstrap/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 |
--------------------------------------------------------------------------------
/views/bootstrap/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 |
--------------------------------------------------------------------------------
/views/bootstrap/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: 40px 0; }
11 | to { background-position: 0 0; }
12 | }
13 |
14 | // Firefox
15 | @-moz-keyframes progress-bar-stripes {
16 | from { background-position: 40px 0; }
17 | to { background-position: 0 0; }
18 | }
19 |
20 | // IE9
21 | @-ms-keyframes progress-bar-stripes {
22 | from { background-position: 40px 0; }
23 | to { background-position: 0 0; }
24 | }
25 |
26 | // Opera
27 | @-o-keyframes progress-bar-stripes {
28 | from { background-position: 0 0; }
29 | to { background-position: 40px 0; }
30 | }
31 |
32 | // Spec
33 | @keyframes progress-bar-stripes {
34 | from { background-position: 40px 0; }
35 | to { background-position: 0 0; }
36 | }
37 |
38 |
39 |
40 | // THE BARS
41 | // --------
42 |
43 | // Outer container
44 | .progress {
45 | overflow: hidden;
46 | height: 18px;
47 | margin-bottom: 18px;
48 | #gradient > .vertical(#f5f5f5, #f9f9f9);
49 | .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));
50 | .border-radius(4px);
51 | }
52 |
53 | // Bar of progress
54 | .progress .bar {
55 | width: 0%;
56 | height: 18px;
57 | color: @white;
58 | font-size: 12px;
59 | text-align: center;
60 | text-shadow: 0 -1px 0 rgba(0,0,0,.25);
61 | #gradient > .vertical(#149bdf, #0480be);
62 | .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));
63 | .box-sizing(border-box);
64 | .transition(width .6s ease);
65 | }
66 |
67 | // Striped bars
68 | .progress-striped .bar {
69 | #gradient > .striped(#149bdf);
70 | .background-size(40px 40px);
71 | }
72 |
73 | // Call animation for the active one
74 | .progress.active .bar {
75 | -webkit-animation: progress-bar-stripes 2s linear infinite;
76 | -moz-animation: progress-bar-stripes 2s linear infinite;
77 | -ms-animation: progress-bar-stripes 2s linear infinite;
78 | -o-animation: progress-bar-stripes 2s linear infinite;
79 | animation: progress-bar-stripes 2s linear infinite;
80 | }
81 |
82 |
83 |
84 | // COLORS
85 | // ------
86 |
87 | // Danger (red)
88 | .progress-danger .bar {
89 | #gradient > .vertical(#ee5f5b, #c43c35);
90 | }
91 | .progress-danger.progress-striped .bar {
92 | #gradient > .striped(#ee5f5b);
93 | }
94 |
95 | // Success (green)
96 | .progress-success .bar {
97 | #gradient > .vertical(#62c462, #57a957);
98 | }
99 | .progress-success.progress-striped .bar {
100 | #gradient > .striped(#62c462);
101 | }
102 |
103 | // Info (teal)
104 | .progress-info .bar {
105 | #gradient > .vertical(#5bc0de, #339bb9);
106 | }
107 | .progress-info.progress-striped .bar {
108 | #gradient > .striped(#5bc0de);
109 | }
110 |
111 | // Warning (orange)
112 | .progress-warning .bar {
113 | #gradient > .vertical(lighten(@orange, 15%), @orange);
114 | }
115 | .progress-warning.progress-striped .bar {
116 | #gradient > .striped(lighten(@orange, 15%));
117 | }
118 |
--------------------------------------------------------------------------------
/views/bootstrap/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 | max-width: 100%; // Make images inherently responsive
79 | vertical-align: middle;
80 | border: 0;
81 | -ms-interpolation-mode: bicubic;
82 | }
83 |
84 | // Prevent max-width from affecting Google Maps
85 | #map_canvas img {
86 | max-width: none;
87 | }
88 |
89 | // Forms
90 | // -------------------------
91 |
92 | // Font size in all browsers, margin changes, misc consistency
93 | button,
94 | input,
95 | select,
96 | textarea {
97 | margin: 0;
98 | font-size: 100%;
99 | vertical-align: middle;
100 | }
101 | button,
102 | input {
103 | *overflow: visible; // Inner spacing ie IE6/7
104 | line-height: normal; // FF3/4 have !important on line-height in UA stylesheet
105 | }
106 | button::-moz-focus-inner,
107 | input::-moz-focus-inner { // Inner padding and border oddities in FF3/4
108 | padding: 0;
109 | border: 0;
110 | }
111 | button,
112 | input[type="button"],
113 | input[type="reset"],
114 | input[type="submit"] {
115 | cursor: pointer; // Cursors on all buttons applied consistently
116 | -webkit-appearance: button; // Style clickable inputs in iOS
117 | }
118 | input[type="search"] { // Appearance in Safari/Chrome
119 | -webkit-box-sizing: content-box;
120 | -moz-box-sizing: content-box;
121 | box-sizing: content-box;
122 | -webkit-appearance: textfield;
123 | }
124 | input[type="search"]::-webkit-search-decoration,
125 | input[type="search"]::-webkit-search-cancel-button {
126 | -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5
127 | }
128 | textarea {
129 | overflow: auto; // Remove vertical scrollbar in IE6-9
130 | vertical-align: top; // Readability and alignment cross-browser
131 | }
132 |
--------------------------------------------------------------------------------
/views/bootstrap/responsive-1200px-min.less:
--------------------------------------------------------------------------------
1 | // LARGE DESKTOP & UP
2 | // ------------------
3 |
4 | @media (min-width: 1200px) {
5 |
6 | // Fixed grid
7 | #grid > .core(70px, 30px);
8 |
9 | // Fluid grid
10 | #grid > .fluid(5.982905983%, 2.564102564%);
11 |
12 | // Input grid
13 | #grid > .input(70px, 30px);
14 |
15 | // Thumbnails
16 | .thumbnails {
17 | margin-left: -30px;
18 | }
19 | .thumbnails > li {
20 | margin-left: 30px;
21 | }
22 | .row-fluid .thumbnails {
23 | margin-left: 0;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/views/bootstrap/responsive-767px-max.less:
--------------------------------------------------------------------------------
1 | // UP TO LANDSCAPE PHONE
2 | // ---------------------
3 |
4 | @media (max-width: 480px) {
5 |
6 | // Smooth out the collapsing/expanding nav
7 | .nav-collapse {
8 | -webkit-transform: translate3d(0, 0, 0); // activate the GPU
9 | }
10 |
11 | // Block level the page header small tag for readability
12 | .page-header h1 small {
13 | display: block;
14 | line-height: @baseLineHeight;
15 | }
16 |
17 | // Update checkboxes for iOS
18 | input[type="checkbox"],
19 | input[type="radio"] {
20 | border: 1px solid #ccc;
21 | }
22 |
23 | // Remove the horizontal form styles
24 | .form-horizontal .control-group > label {
25 | float: none;
26 | width: auto;
27 | padding-top: 0;
28 | text-align: left;
29 | }
30 | // Move over all input controls and content
31 | .form-horizontal .controls {
32 | margin-left: 0;
33 | }
34 | // Move the options list down to align with labels
35 | .form-horizontal .control-list {
36 | padding-top: 0; // has to be padding because margin collaspes
37 | }
38 | // Move over buttons in .form-actions to align with .controls
39 | .form-horizontal .form-actions {
40 | padding-left: 10px;
41 | padding-right: 10px;
42 | }
43 |
44 | // Modals
45 | .modal {
46 | position: absolute;
47 | top: 10px;
48 | left: 10px;
49 | right: 10px;
50 | width: auto;
51 | margin: 0;
52 | &.fade.in { top: auto; }
53 | }
54 | .modal-header .close {
55 | padding: 10px;
56 | margin: -10px;
57 | }
58 |
59 | // Carousel
60 | .carousel-caption {
61 | position: static;
62 | }
63 |
64 | }
65 |
66 |
67 |
68 | // LANDSCAPE PHONE TO SMALL DESKTOP & PORTRAIT TABLET
69 | // --------------------------------------------------
70 |
71 | @media (max-width: 767px) {
72 |
73 | // Padding to set content in a bit
74 | body {
75 | padding-left: 20px;
76 | padding-right: 20px;
77 | }
78 | // Negative indent the now static "fixed" navbar
79 | .navbar-fixed-top,
80 | .navbar-fixed-bottom {
81 | margin-left: -20px;
82 | margin-right: -20px;
83 | }
84 | // Remove padding on container given explicit padding set on body
85 | .container-fluid {
86 | padding: 0;
87 | }
88 |
89 | // TYPOGRAPHY
90 | // ----------
91 | // Reset horizontal dl
92 | .dl-horizontal {
93 | dt {
94 | float: none;
95 | clear: none;
96 | width: auto;
97 | text-align: left;
98 | }
99 | dd {
100 | margin-left: 0;
101 | }
102 | }
103 |
104 | // GRID & CONTAINERS
105 | // -----------------
106 | // Remove width from containers
107 | .container {
108 | width: auto;
109 | }
110 | // Fluid rows
111 | .row-fluid {
112 | width: 100%;
113 | }
114 | // Undo negative margin on rows and thumbnails
115 | .row,
116 | .thumbnails {
117 | margin-left: 0;
118 | }
119 | // Make all grid-sized elements block level again
120 | [class*="span"],
121 | .row-fluid [class*="span"] {
122 | float: none;
123 | display: block;
124 | width: auto;
125 | margin-left: 0;
126 | }
127 |
128 | // FORM FIELDS
129 | // -----------
130 | // Make span* classes full width
131 | .input-large,
132 | .input-xlarge,
133 | .input-xxlarge,
134 | input[class*="span"],
135 | select[class*="span"],
136 | textarea[class*="span"],
137 | .uneditable-input {
138 | .input-block-level();
139 | }
140 | // But don't let it screw up prepend/append inputs
141 | .input-prepend input,
142 | .input-append input,
143 | .input-prepend input[class*="span"],
144 | .input-append input[class*="span"] {
145 | display: inline-block; // redeclare so they don't wrap to new lines
146 | width: auto;
147 | }
148 |
149 | }
150 |
--------------------------------------------------------------------------------
/views/bootstrap/responsive-768px-979px.less:
--------------------------------------------------------------------------------
1 | // PORTRAIT TABLET TO DEFAULT DESKTOP
2 | // ----------------------------------
3 |
4 | @media (min-width: 768px) and (max-width: 979px) {
5 |
6 | // Fixed grid
7 | #grid > .core(42px, 20px);
8 |
9 | // Fluid grid
10 | #grid > .fluid(5.801104972%, 2.762430939%);
11 |
12 | // Input grid
13 | #grid > .input(42px, 20px);
14 |
15 | // No need to reset .thumbnails here since it's the same @gridGutterWidth
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/views/bootstrap/responsive-navbar.less:
--------------------------------------------------------------------------------
1 | // TABLETS AND BELOW
2 | // -----------------
3 | @media (max-width: 979px) {
4 |
5 | // UNFIX THE TOPBAR
6 | // ----------------
7 | // Remove any padding from the body
8 | body {
9 | padding-top: 0;
10 | }
11 | // Unfix the navbar
12 | .navbar-fixed-top,
13 | .navbar-fixed-bottom {
14 | position: static;
15 | }
16 | .navbar-fixed-top {
17 | margin-bottom: @baseLineHeight;
18 | }
19 | .navbar-fixed-bottom {
20 | margin-top: @baseLineHeight;
21 | }
22 | .navbar-fixed-top .navbar-inner,
23 | .navbar-fixed-bottom .navbar-inner {
24 | padding: 5px;
25 | }
26 | .navbar .container {
27 | width: auto;
28 | padding: 0;
29 | }
30 | // Account for brand name
31 | .navbar .brand {
32 | padding-left: 10px;
33 | padding-right: 10px;
34 | margin: 0 0 0 -5px;
35 | }
36 |
37 | // COLLAPSIBLE NAVBAR
38 | // ------------------
39 | // Nav collapse clears brand
40 | .nav-collapse {
41 | clear: both;
42 | }
43 | // Block-level the nav
44 | .nav-collapse .nav {
45 | float: none;
46 | margin: 0 0 (@baseLineHeight / 2);
47 | }
48 | .nav-collapse .nav > li {
49 | float: none;
50 | }
51 | .nav-collapse .nav > li > a {
52 | margin-bottom: 2px;
53 | }
54 | .nav-collapse .nav > .divider-vertical {
55 | display: none;
56 | }
57 | .nav-collapse .nav .nav-header {
58 | color: @navbarText;
59 | text-shadow: none;
60 | }
61 | // Nav and dropdown links in navbar
62 | .nav-collapse .nav > li > a,
63 | .nav-collapse .dropdown-menu a {
64 | padding: 6px 15px;
65 | font-weight: bold;
66 | color: @navbarLinkColor;
67 | .border-radius(3px);
68 | }
69 | // Buttons
70 | .nav-collapse .btn {
71 | padding: 4px 10px 4px;
72 | font-weight: normal;
73 | .border-radius(4px);
74 | }
75 | .nav-collapse .dropdown-menu li + li a {
76 | margin-bottom: 2px;
77 | }
78 | .nav-collapse .nav > li > a:hover,
79 | .nav-collapse .dropdown-menu a:hover {
80 | background-color: @navbarBackground;
81 | }
82 | // Buttons in the navbar
83 | .nav-collapse.in .btn-group {
84 | margin-top: 5px;
85 | padding: 0;
86 | }
87 | // Dropdowns in the navbar
88 | .nav-collapse .dropdown-menu {
89 | position: static;
90 | top: auto;
91 | left: auto;
92 | float: none;
93 | display: block;
94 | max-width: none;
95 | margin: 0 15px;
96 | padding: 0;
97 | background-color: transparent;
98 | border: none;
99 | .border-radius(0);
100 | .box-shadow(none);
101 | }
102 | .nav-collapse .dropdown-menu:before,
103 | .nav-collapse .dropdown-menu:after {
104 | display: none;
105 | }
106 | .nav-collapse .dropdown-menu .divider {
107 | display: none;
108 | }
109 | // Forms in navbar
110 | .nav-collapse .navbar-form,
111 | .nav-collapse .navbar-search {
112 | float: none;
113 | padding: (@baseLineHeight / 2) 15px;
114 | margin: (@baseLineHeight / 2) 0;
115 | border-top: 1px solid @navbarBackground;
116 | border-bottom: 1px solid @navbarBackground;
117 | .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1)");
118 | }
119 | // Pull right (secondary) nav content
120 | .navbar .nav-collapse .nav.pull-right {
121 | float: none;
122 | margin-left: 0;
123 | }
124 | // Hide everything in the navbar save .brand and toggle button */
125 | .nav-collapse,
126 | .nav-collapse.collapse {
127 | overflow: hidden;
128 | height: 0;
129 | }
130 | // Navbar button
131 | .navbar .btn-navbar {
132 | display: block;
133 | }
134 |
135 | // STATIC NAVBAR
136 | // -------------
137 | .navbar-static .navbar-inner {
138 | padding-left: 10px;
139 | padding-right: 10px;
140 | }
141 | }
142 |
143 |
144 | // DEFAULT DESKTOP
145 | // ---------------
146 |
147 | // Required to make the collapsing navbar work on regular desktops
148 | @media (min-width: 980px) {
149 | .nav-collapse.collapse {
150 | height: auto !important;
151 | overflow: visible !important;
152 | }
153 | }
--------------------------------------------------------------------------------
/views/bootstrap/responsive-utilities.less:
--------------------------------------------------------------------------------
1 | // RESPONSIVE CLASSES
2 | // ------------------
3 |
4 | // Hide from screenreaders and browsers
5 | // Credit: HTML5 Boilerplate
6 | .hidden {
7 | display: none;
8 | visibility: hidden;
9 | }
10 |
11 | // Visibility utilities
12 |
13 | // For desktops
14 | .visible-phone { display: none !important; }
15 | .visible-tablet { display: none !important; }
16 | .visible-desktop { } // Don't set initially
17 | .hidden-phone { }
18 | .hidden-tablet { }
19 | .hidden-desktop { display: none !important; }
20 |
21 | // Phones only
22 | @media (max-width: 767px) {
23 | // Show
24 | .visible-phone { display: inherit !important; } // Use inherit to restore previous behavior
25 | // Hide
26 | .hidden-phone { display: none !important; }
27 | // Hide everything else
28 | .hidden-desktop { display: inherit !important; }
29 | .visible-desktop { display: none !important; }
30 | }
31 |
32 | // Tablets & small desktops only
33 | @media (min-width: 768px) and (max-width: 979px) {
34 | // Show
35 | .visible-tablet { display: inherit !important; }
36 | // Hide
37 | .hidden-tablet { display: none !important; }
38 | // Hide everything else
39 | .hidden-desktop { display: inherit !important; }
40 | .visible-desktop { display: none !important ; }
41 | }
42 |
--------------------------------------------------------------------------------
/views/bootstrap/responsive.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Responsive v2.0.4
3 | *
4 | * Copyright 2012 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world @twitter by @mdo and @fat.
9 | */
10 |
11 |
12 | // Responsive.less
13 | // For phone and tablet devices
14 | // -------------------------------------------------------------
15 |
16 |
17 | // REPEAT VARIABLES & MIXINS
18 | // -------------------------
19 | // Required since we compile the responsive stuff separately
20 |
21 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc
22 | @import "mixins.less";
23 |
24 |
25 | // RESPONSIVE CLASSES
26 | // ------------------
27 |
28 | @import "responsive-utilities.less";
29 |
30 |
31 | // MEDIA QUERIES
32 | // ------------------
33 |
34 | // Phones to portrait tablets and narrow desktops
35 | @import "responsive-767px-max.less";
36 |
37 | // Tablets to regular desktops
38 | @import "responsive-768px-979px.less";
39 |
40 | // Large desktops
41 | @import "responsive-1200px-min.less";
42 |
43 |
44 | // RESPONSIVE NAVBAR
45 | // ------------------
46 |
47 | // From 979px and below, show a button to toggle navbar contents
48 | @import "responsive-navbar.less";
49 |
--------------------------------------------------------------------------------
/views/bootstrap/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 | font-family: @baseFontFamily;
12 | font-size: @baseFontSize;
13 | line-height: @baseLineHeight;
14 | color: @textColor;
15 | background-color: @bodyBackground;
16 | }
17 |
18 |
19 | // Links
20 | // -----
21 |
22 | a {
23 | color: @linkColor;
24 | text-decoration: none;
25 | }
26 | a:hover {
27 | color: @linkColorHover;
28 | text-decoration: underline;
29 | }
30 |
--------------------------------------------------------------------------------
/views/bootstrap/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 | background-color: @tableBackground;
13 | border-collapse: collapse;
14 | border-spacing: 0;
15 | }
16 |
17 | // BASELINE STYLES
18 | // ---------------
19 |
20 | .table {
21 | width: 100%;
22 | margin-bottom: @baseLineHeight;
23 | // Cells
24 | th,
25 | td {
26 | padding: 8px;
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 | caption + thead tr:first-child th,
41 | caption + thead tr:first-child td,
42 | colgroup + thead tr:first-child th,
43 | colgroup + thead tr:first-child td,
44 | thead:first-child tr:first-child th,
45 | thead:first-child tr:first-child td {
46 | border-top: 0;
47 | }
48 | // Account for multiple tbody instances
49 | tbody + tbody {
50 | border-top: 2px solid @tableBorder;
51 | }
52 | }
53 |
54 |
55 |
56 | // CONDENSED TABLE W/ HALF PADDING
57 | // -------------------------------
58 |
59 | .table-condensed {
60 | th,
61 | td {
62 | padding: 4px 5px;
63 | }
64 | }
65 |
66 |
67 | // BORDERED VERSION
68 | // ----------------
69 |
70 | .table-bordered {
71 | border: 1px solid @tableBorder;
72 | border-collapse: separate; // Done so we can round those corners!
73 | *border-collapse: collapsed; // IE7 can't round corners anyway
74 | border-left: 0;
75 | .border-radius(4px);
76 | th,
77 | td {
78 | border-left: 1px solid @tableBorder;
79 | }
80 | // Prevent a double border
81 | caption + thead tr:first-child th,
82 | caption + tbody tr:first-child th,
83 | caption + tbody tr:first-child td,
84 | colgroup + thead tr:first-child th,
85 | colgroup + tbody tr:first-child th,
86 | colgroup + tbody tr:first-child td,
87 | thead:first-child tr:first-child th,
88 | tbody:first-child tr:first-child th,
89 | tbody:first-child tr:first-child td {
90 | border-top: 0;
91 | }
92 | // For first th or td in the first row in the first thead or tbody
93 | thead:first-child tr:first-child th:first-child,
94 | tbody:first-child tr:first-child td:first-child {
95 | -webkit-border-top-left-radius: 4px;
96 | border-top-left-radius: 4px;
97 | -moz-border-radius-topleft: 4px;
98 | }
99 | thead:first-child tr:first-child th:last-child,
100 | tbody:first-child tr:first-child td:last-child {
101 | -webkit-border-top-right-radius: 4px;
102 | border-top-right-radius: 4px;
103 | -moz-border-radius-topright: 4px;
104 | }
105 | // For first th or td in the first row in the first thead or tbody
106 | thead:last-child tr:last-child th:first-child,
107 | tbody:last-child tr:last-child td:first-child {
108 | .border-radius(0 0 0 4px);
109 | -webkit-border-bottom-left-radius: 4px;
110 | border-bottom-left-radius: 4px;
111 | -moz-border-radius-bottomleft: 4px;
112 | }
113 | thead:last-child tr:last-child th:last-child,
114 | tbody:last-child tr:last-child td:last-child {
115 | -webkit-border-bottom-right-radius: 4px;
116 | border-bottom-right-radius: 4px;
117 | -moz-border-radius-bottomright: 4px;
118 | }
119 | }
120 |
121 |
122 | // ZEBRA-STRIPING
123 | // --------------
124 |
125 | // Default zebra-stripe styles (alternating gray and transparent backgrounds)
126 | .table-striped {
127 | tbody {
128 | tr:nth-child(odd) td,
129 | tr:nth-child(odd) th {
130 | background-color: @tableBackgroundAccent;
131 | }
132 | }
133 | }
134 |
135 |
136 | // HOVER EFFECT
137 | // ------------
138 | // Placed here since it has to come after the potential zebra striping
139 | .table {
140 | tbody tr:hover td,
141 | tbody tr:hover th {
142 | background-color: @tableBackgroundHover;
143 | }
144 | }
145 |
146 |
147 | // TABLE CELL SIZING
148 | // -----------------
149 |
150 | // Change the columns
151 | table {
152 | .span1 { .tableColumns(1); }
153 | .span2 { .tableColumns(2); }
154 | .span3 { .tableColumns(3); }
155 | .span4 { .tableColumns(4); }
156 | .span5 { .tableColumns(5); }
157 | .span6 { .tableColumns(6); }
158 | .span7 { .tableColumns(7); }
159 | .span8 { .tableColumns(8); }
160 | .span9 { .tableColumns(9); }
161 | .span10 { .tableColumns(10); }
162 | .span11 { .tableColumns(11); }
163 | .span12 { .tableColumns(12); }
164 | .span13 { .tableColumns(13); }
165 | .span14 { .tableColumns(14); }
166 | .span15 { .tableColumns(15); }
167 | .span16 { .tableColumns(16); }
168 | .span17 { .tableColumns(17); }
169 | .span18 { .tableColumns(18); }
170 | .span19 { .tableColumns(19); }
171 | .span20 { .tableColumns(20); }
172 | .span21 { .tableColumns(21); }
173 | .span22 { .tableColumns(22); }
174 | .span23 { .tableColumns(23); }
175 | .span24 { .tableColumns(24); }
176 | }
177 |
--------------------------------------------------------------------------------
/views/bootstrap/tests/css-tests.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap CSS Tests
3 | */
4 |
5 |
6 | /* Remove background image */
7 | body {
8 | background-image: none;
9 | }
10 |
11 | /* Space out subhead */
12 | .subhead {
13 | margin-bottom: 36px;
14 | }
15 | h4 {
16 | margin-bottom: 5px;
17 | }
18 |
19 |
20 | /* colgroup tests */
21 | .col1 {
22 | background-color: rgba(255,0,0,.1);
23 | }
24 | .col2 {
25 | background-color: rgba(0,255,0,.1);
26 | }
27 | .col3 {
28 | background-color: rgba(0,0,255,.1);
29 | }
30 |
31 |
32 | /* Fluid row inputs */
33 | #rowInputs .row > [class*=span],
34 | #fluidRowInputs .row-fluid > [class*=span] {
35 | background-color: rgba(255,0,0,.1);
36 | }
37 |
38 |
39 | /* Fluid grid */
40 | .fluid-grid {
41 | margin-bottom: 45px;
42 | }
43 | .fluid-grid .row {
44 | height: 40px;
45 | padding-top: 10px;
46 | margin-top: 10px;
47 | color: #ddd;
48 | text-align: center;
49 | }
50 | .fluid-grid .span1 {
51 | background-color: #999;
52 | }
53 |
--------------------------------------------------------------------------------
/views/bootstrap/tests/forms.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Bootstrap, from Twitter
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
177 |
178 |
179 |
180 |
--------------------------------------------------------------------------------
/views/bootstrap/tests/navbar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Bootstrap, from Twitter
6 |
7 |
8 |
9 |
10 |
11 |
12 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
55 |
56 |
57 |
58 |
59 |
78 |
79 |
80 |
81 |
Navbar example
82 |
This example is a quick exercise to illustrate how the default, static navbar and fixed to top navbar work. It includes the responsive CSS and HTML, so it also adapts to your viewport and device.
83 |
84 | View navbar docs »
85 |
86 |
87 |
88 |
89 |
90 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/views/bootstrap/thumbnails.less:
--------------------------------------------------------------------------------
1 | // THUMBNAILS
2 | // ----------
3 | // Note: `.thumbnails` and `.thumbnails > li` are overriden in responsive files
4 |
5 | // Make wrapper ul behave like the grid
6 | .thumbnails {
7 | margin-left: -@gridGutterWidth;
8 | list-style: none;
9 | .clearfix();
10 | }
11 | // Fluid rows have no left margin
12 | .row-fluid .thumbnails {
13 | margin-left: 0;
14 | }
15 |
16 | // Float li to make thumbnails appear in a row
17 | .thumbnails > li {
18 | float: left; // Explicity set the float since we don't require .span* classes
19 | margin-bottom: @baseLineHeight;
20 | margin-left: @gridGutterWidth;
21 | }
22 |
23 | // The actual thumbnail (can be `a` or `div`)
24 | .thumbnail {
25 | display: block;
26 | padding: 4px;
27 | line-height: 1;
28 | border: 1px solid #ddd;
29 | .border-radius(4px);
30 | .box-shadow(0 1px 1px rgba(0,0,0,.075));
31 | }
32 | // Add a hover state for linked versions only
33 | a.thumbnail:hover {
34 | border-color: @linkColor;
35 | .box-shadow(0 1px 4px rgba(0,105,214,.25));
36 | }
37 |
38 | // Images and captions
39 | .thumbnail > img {
40 | display: block;
41 | max-width: 100%;
42 | margin-left: auto;
43 | margin-right: auto;
44 | }
45 | .thumbnail .caption {
46 | padding: 9px;
47 | }
48 |
--------------------------------------------------------------------------------
/views/bootstrap/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 |
--------------------------------------------------------------------------------
/views/bootstrap/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 | small {
12 | font-size: @baseFontSize - 2;
13 | color: @grayLight;
14 | }
15 | }
16 | .lead {
17 | margin-bottom: @baseLineHeight;
18 | font-size: 20px;
19 | font-weight: 200;
20 | line-height: @baseLineHeight * 1.5;
21 | }
22 |
23 | // HEADINGS
24 | // --------
25 |
26 | h1, h2, h3, h4, h5, h6 {
27 | margin: 0;
28 | font-family: @headingsFontFamily;
29 | font-weight: @headingsFontWeight;
30 | color: @headingsColor;
31 | text-rendering: optimizelegibility; // Fix the character spacing for headings
32 | small {
33 | font-weight: normal;
34 | color: @grayLight;
35 | }
36 | }
37 | h1 {
38 | font-size: 30px;
39 | line-height: @baseLineHeight * 2;
40 | small {
41 | font-size: 18px;
42 | }
43 | }
44 | h2 {
45 | font-size: 24px;
46 | line-height: @baseLineHeight * 2;
47 | small {
48 | font-size: 18px;
49 | }
50 | }
51 | h3 {
52 | font-size: 18px;
53 | line-height: @baseLineHeight * 1.5;
54 | small {
55 | font-size: 14px;
56 | }
57 | }
58 | h4, h5, h6 {
59 | line-height: @baseLineHeight;
60 | }
61 | h4 {
62 | font-size: 14px;
63 | small {
64 | font-size: 12px;
65 | }
66 | }
67 | h5 {
68 | font-size: 12px;
69 | }
70 | h6 {
71 | font-size: 11px;
72 | color: @grayLight;
73 | text-transform: uppercase;
74 | }
75 |
76 | // Page header
77 | .page-header {
78 | padding-bottom: @baseLineHeight - 1;
79 | margin: @baseLineHeight 0;
80 | border-bottom: 1px solid @grayLighter;
81 | }
82 | .page-header h1 {
83 | line-height: 1;
84 | }
85 |
86 |
87 |
88 | // LISTS
89 | // -----
90 |
91 | // Unordered and Ordered lists
92 | ul, ol {
93 | padding: 0;
94 | margin: 0 0 @baseLineHeight / 2 25px;
95 | }
96 | ul ul,
97 | ul ol,
98 | ol ol,
99 | ol ul {
100 | margin-bottom: 0;
101 | }
102 | ul {
103 | list-style: disc;
104 | }
105 | ol {
106 | list-style: decimal;
107 | }
108 | li {
109 | line-height: @baseLineHeight;
110 | }
111 | ul.unstyled,
112 | ol.unstyled {
113 | margin-left: 0;
114 | list-style: none;
115 | }
116 |
117 | // Description Lists
118 | dl {
119 | margin-bottom: @baseLineHeight;
120 | }
121 | dt,
122 | dd {
123 | line-height: @baseLineHeight;
124 | }
125 | dt {
126 | font-weight: bold;
127 | line-height: @baseLineHeight - 1; // fix jank Helvetica Neue font bug
128 | }
129 | dd {
130 | margin-left: @baseLineHeight / 2;
131 | }
132 | // Horizontal layout (like forms)
133 | .dl-horizontal {
134 | dt {
135 | float: left;
136 | width: 120px;
137 | clear: left;
138 | text-align: right;
139 | .text-overflow();
140 | }
141 | dd {
142 | margin-left: 130px;
143 | }
144 | }
145 |
146 | // MISC
147 | // ----
148 |
149 | // Horizontal rules
150 | hr {
151 | margin: @baseLineHeight 0;
152 | border: 0;
153 | border-top: 1px solid @hrBorder;
154 | border-bottom: 1px solid @white;
155 | }
156 |
157 | // Emphasis
158 | strong {
159 | font-weight: bold;
160 | }
161 | em {
162 | font-style: italic;
163 | }
164 | .muted {
165 | color: @grayLight;
166 | }
167 |
168 | // Abbreviations and acronyms
169 | abbr[title] {
170 | cursor: help;
171 | border-bottom: 1px dotted @grayLight;
172 | }
173 | abbr.initialism {
174 | font-size: 90%;
175 | text-transform: uppercase;
176 | }
177 |
178 | // Blockquotes
179 | blockquote {
180 | padding: 0 0 0 15px;
181 | margin: 0 0 @baseLineHeight;
182 | border-left: 5px solid @grayLighter;
183 | p {
184 | margin-bottom: 0;
185 | #font > .shorthand(16px,300,@baseLineHeight * 1.25);
186 | }
187 | small {
188 | display: block;
189 | line-height: @baseLineHeight;
190 | color: @grayLight;
191 | &:before {
192 | content: '\2014 \00A0';
193 | }
194 | }
195 |
196 | // Float right with text-align: right
197 | &.pull-right {
198 | float: right;
199 | padding-right: 15px;
200 | padding-left: 0;
201 | border-right: 5px solid @grayLighter;
202 | border-left: 0;
203 | p,
204 | small {
205 | text-align: right;
206 | }
207 | }
208 | }
209 |
210 | // Quotes
211 | q:before,
212 | q:after,
213 | blockquote:before,
214 | blockquote:after {
215 | content: "";
216 | }
217 |
218 | // Addresses
219 | address {
220 | display: block;
221 | margin-bottom: @baseLineHeight;
222 | font-style: normal;
223 | line-height: @baseLineHeight;
224 | }
225 |
226 | // Misc
227 | small {
228 | font-size: 100%;
229 | }
230 | cite {
231 | font-style: normal;
232 | }
233 |
--------------------------------------------------------------------------------
/views/bootstrap/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 |
--------------------------------------------------------------------------------
/views/bootstrap/variables.less:
--------------------------------------------------------------------------------
1 | // Variables.less
2 | // Variables to customize the look and feel of Bootstrap
3 | // -----------------------------------------------------
4 |
5 |
6 |
7 | // GLOBAL VALUES
8 | // --------------------------------------------------
9 |
10 |
11 | // Grays
12 | // -------------------------
13 | @black: #000;
14 | @grayDarker: #222;
15 | @grayDark: #333;
16 | @gray: #555;
17 | @grayLight: #999;
18 | @grayLighter: #eee;
19 | @white: #fff;
20 |
21 |
22 | // Accent colors
23 | // -------------------------
24 | @blue: #049cdb;
25 | @blueDark: #0064cd;
26 | @green: #46a546;
27 | @red: #9d261d;
28 | @yellow: #ffc40d;
29 | @orange: #f89406;
30 | @pink: #c3325f;
31 | @purple: #7a43b6;
32 |
33 |
34 | // Scaffolding
35 | // -------------------------
36 | @bodyBackground: @white;
37 | @textColor: @grayDark;
38 |
39 |
40 | // Links
41 | // -------------------------
42 | @linkColor: #08c;
43 | @linkColorHover: darken(@linkColor, 15%);
44 |
45 |
46 | // Typography
47 | // -------------------------
48 | @sansFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif;
49 | @serifFontFamily: Georgia, "Times New Roman", Times, serif;
50 | @monoFontFamily: Menlo, Monaco, Consolas, "Courier New", monospace;
51 |
52 | @baseFontSize: 13px;
53 | @baseFontFamily: @sansFontFamily;
54 | @baseLineHeight: 18px;
55 | @altFontFamily: @serifFontFamily;
56 |
57 | @headingsFontFamily: inherit; // empty to use BS default, @baseFontFamily
58 | @headingsFontWeight: bold; // instead of browser default, bold
59 | @headingsColor: inherit; // empty to use BS default, @textColor
60 |
61 |
62 | // Tables
63 | // -------------------------
64 | @tableBackground: transparent; // overall background-color
65 | @tableBackgroundAccent: #f9f9f9; // for striping
66 | @tableBackgroundHover: #f5f5f5; // for hover
67 | @tableBorder: #ddd; // table and cell border
68 |
69 |
70 | // Buttons
71 | // -------------------------
72 | @btnBackground: @white;
73 | @btnBackgroundHighlight: darken(@white, 10%);
74 | @btnBorder: #ccc;
75 |
76 | @btnPrimaryBackground: @linkColor;
77 | @btnPrimaryBackgroundHighlight: spin(@btnPrimaryBackground, 15%);
78 |
79 | @btnInfoBackground: #5bc0de;
80 | @btnInfoBackgroundHighlight: #2f96b4;
81 |
82 | @btnSuccessBackground: #62c462;
83 | @btnSuccessBackgroundHighlight: #51a351;
84 |
85 | @btnWarningBackground: lighten(@orange, 15%);
86 | @btnWarningBackgroundHighlight: @orange;
87 |
88 | @btnDangerBackground: #ee5f5b;
89 | @btnDangerBackgroundHighlight: #bd362f;
90 |
91 | @btnInverseBackground: @gray;
92 | @btnInverseBackgroundHighlight: @grayDarker;
93 |
94 |
95 | // Forms
96 | // -------------------------
97 | @inputBackground: @white;
98 | @inputBorder: #ccc;
99 | @inputBorderRadius: 3px;
100 | @inputDisabledBackground: @grayLighter;
101 | @formActionsBackground: #f5f5f5;
102 |
103 | // Dropdowns
104 | // -------------------------
105 | @dropdownBackground: @white;
106 | @dropdownBorder: rgba(0,0,0,.2);
107 | @dropdownLinkColor: @grayDark;
108 | @dropdownLinkColorHover: @white;
109 | @dropdownLinkBackgroundHover: @linkColor;
110 | @dropdownDividerTop: #e5e5e5;
111 | @dropdownDividerBottom: @white;
112 |
113 |
114 |
115 | // COMPONENT VARIABLES
116 | // --------------------------------------------------
117 |
118 | // Z-index master list
119 | // -------------------------
120 | // Used for a bird's eye view of components dependent on the z-axis
121 | // Try to avoid customizing these :)
122 | @zindexDropdown: 1000;
123 | @zindexPopover: 1010;
124 | @zindexTooltip: 1020;
125 | @zindexFixedNavbar: 1030;
126 | @zindexModalBackdrop: 1040;
127 | @zindexModal: 1050;
128 |
129 |
130 | // Sprite icons path
131 | // -------------------------
132 | @iconSpritePath: "../img/glyphicons-halflings.png";
133 | @iconWhiteSpritePath: "../img/glyphicons-halflings-white.png";
134 |
135 |
136 | // Input placeholder text color
137 | // -------------------------
138 | @placeholderText: @grayLight;
139 |
140 |
141 | // Hr border color
142 | // -------------------------
143 | @hrBorder: @grayLighter;
144 |
145 |
146 | // Navbar
147 | // -------------------------
148 | @navbarHeight: 40px;
149 | @navbarBackground: @grayDarker;
150 | @navbarBackgroundHighlight: @grayDark;
151 |
152 | @navbarText: @grayLight;
153 | @navbarLinkColor: @grayLight;
154 | @navbarLinkColorHover: @white;
155 | @navbarLinkColorActive: @navbarLinkColorHover;
156 | @navbarLinkBackgroundHover: transparent;
157 | @navbarLinkBackgroundActive: @navbarBackground;
158 |
159 | @navbarSearchBackground: lighten(@navbarBackground, 25%);
160 | @navbarSearchBackgroundFocus: @white;
161 | @navbarSearchBorder: darken(@navbarSearchBackground, 30%);
162 | @navbarSearchPlaceholderColor: #ccc;
163 | @navbarBrandColor: @navbarLinkColor;
164 |
165 |
166 | // Hero unit
167 | // -------------------------
168 | @heroUnitBackground: @grayLighter;
169 | @heroUnitHeadingColor: inherit;
170 | @heroUnitLeadColor: inherit;
171 |
172 |
173 | // Form states and alerts
174 | // -------------------------
175 | @warningText: #c09853;
176 | @warningBackground: #fcf8e3;
177 | @warningBorder: darken(spin(@warningBackground, -10), 3%);
178 |
179 | @errorText: #b94a48;
180 | @errorBackground: #f2dede;
181 | @errorBorder: darken(spin(@errorBackground, -10), 3%);
182 |
183 | @successText: #468847;
184 | @successBackground: #dff0d8;
185 | @successBorder: darken(spin(@successBackground, -10), 5%);
186 |
187 | @infoText: #3a87ad;
188 | @infoBackground: #d9edf7;
189 | @infoBorder: darken(spin(@infoBackground, -10), 7%);
190 |
191 |
192 |
193 | // GRID
194 | // --------------------------------------------------
195 |
196 | // Default 940px grid
197 | // -------------------------
198 | @gridColumns: 12;
199 | @gridColumnWidth: 60px;
200 | @gridGutterWidth: 20px;
201 | @gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1));
202 |
203 | // Fluid grid
204 | // -------------------------
205 | @fluidGridColumnWidth: 6.382978723%;
206 | @fluidGridGutterWidth: 2.127659574%;
207 |
--------------------------------------------------------------------------------
/views/bootstrap/wells.less:
--------------------------------------------------------------------------------
1 | // WELLS
2 | // -----
3 |
4 | .well {
5 | min-height: 20px;
6 | padding: 19px;
7 | margin-bottom: 20px;
8 | background-color: #f5f5f5;
9 | border: 1px solid #eee;
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 | }
18 |
19 | // Sizes
20 | .well-large {
21 | padding: 24px;
22 | .border-radius(6px);
23 | }
24 | .well-small {
25 | padding: 9px;
26 | .border-radius(3px);
27 | }
28 |
--------------------------------------------------------------------------------
/views/dashboard.erb:
--------------------------------------------------------------------------------
1 | <% unless @error %>
2 |
3 |
4 |
5 |
6 |
7 |
<%= @dashboard.name %> <%= @dashboard.description %>
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 | <%= erb :_interval_filter, :layout => false unless @interval_filters.empty? %>
18 |
19 |
20 |
21 |
22 |
23 |
24 | <% span=12/@graph_columns %>
25 | <% @graphs.each_with_index do |graph,i| %>
26 | <% if graph %>
27 | <% if i>0 and i%@graph_columns==0 %>
28 |
29 |
30 | <%end%>
31 |
32 | <%= erb(:graph, :locals => { :graph => graph }) %>
33 |
34 | <% end %>
35 | <% end %>
36 |
37 |
38 |
39 |
47 | <% end %>
48 |
--------------------------------------------------------------------------------
/views/detailed_dashboard.erb:
--------------------------------------------------------------------------------
1 | <% unless @error %>
2 |
3 |
4 |
5 |
6 |
<%= @dashboard.name %> <%= @dashboard.description %>
7 |
8 |
9 |
10 | <% span=12/@graph_columns %>
11 |
12 |
13 | <% @graphs.each_with_index do |graph,i| %>
14 | <% if graph %>
15 | <% if i>0 and i%@graph_columns==0 %>
16 |
17 |
18 | <%end%>
19 |
20 | <%= erb(:graph, :locals => { :graph => graph, :omit_link => true }) %>
21 |
22 | <% end %>
23 | <% end %>
24 |
25 |
26 |
34 | <% end %>
35 |
--------------------------------------------------------------------------------
/views/full_size_dashboard.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <% if @error %>
7 | <%= @error %>
8 | <% else %>
9 |
10 |
11 | <% span=12/@graph_columns %>
12 |
13 |
14 | <% @graphs.each_with_index do |graph,i| %>
15 | <% if graph %>
16 | <% if i>0 and i%@graph_columns==0 %>
17 |
18 |
19 | <%end%>
20 |
21 | <%= erb(:graph, :locals => { :graph => graph, :omit_link => true }) %>
22 |
23 | <% end %>
24 | <% end %>
25 |
26 |
27 |
39 |
40 | <% end %>
41 |
42 |
--------------------------------------------------------------------------------
/views/graph.erb:
--------------------------------------------------------------------------------
1 | <% omit_link ||= false %>
2 | <% unless omit_link %>
3 | <%qp="?#{request.env['rack.request.query_string']}" %>
4 |
'>
5 | <% end %>
6 |
7 | <% unless omit_link %>
8 |
9 | <% end %>
10 |
--------------------------------------------------------------------------------
/views/index.erb:
--------------------------------------------------------------------------------
1 | <% unless @error %>
2 | <% @dashboard_to_display.keys.sort.each do |key| %>
3 |
<%= key.capitalize %>
4 |
5 |
6 | Name
7 | Description
8 |
9 |
10 | <% @dashboard_to_display[key].sort_by{|b| b[:name].to_s}.each do |board| %>
11 |
12 | <%= board[:name] %> <%= board[:description] %>
13 |
14 | <% end %>
15 |
16 |
17 | <% end %>
18 | <% end %>
19 |
--------------------------------------------------------------------------------
/views/layout.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
<%= @dash_title %>
21 |
22 |
23 |
24 |
25 |
26 |
<%= @dash_title %>
27 |
28 | <% @top_level.keys.sort.each do |category| %>
29 |
37 | <% end %>
38 |
39 |
40 |
41 | Menu
42 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | <% if @error %>
69 |
70 | ×
71 |
Error!
72 | <%= @error %>
73 |
74 | <% end %>
75 | <%= yield %>
76 |
77 |
78 |
79 |
80 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/views/print_dashboard.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 | <% if @error %>
15 | <%= @error %>
16 | <% else %>
17 |
18 | <%= @dashboard.name %> <%= @dashboard.description %>
19 | <% span=12/@graph_columns %>
20 |
21 |
22 | <% @graphs.each_with_index do |graph,i| %>
23 | <% if graph %>
24 | <% if i>0 and i%@graph_columns==0 %>
25 |
26 |
27 | <%end%>
28 |
29 | <%= erb(:graph, :locals => { :graph => graph}) %>
30 |
31 | <% end %>
32 | <% end %>
33 |
34 |
35 |
47 |
48 | <% end %>
49 |
50 |
--------------------------------------------------------------------------------
/views/print_detailed_dashboard.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 |
15 | <% if @error %>
16 | <%= @error %>
17 | <% else %>
18 | <%= @dashboard.name %> <%= @dashboard.description %>
19 | <% span=12/@graph_columns %>
20 |
21 |
22 | <% @graphs.each_with_index do |graph,i| %>
23 | <% if graph %>
24 | <% if i>0 and i%@graph_columns==0 %>
25 |
26 |
27 | <%end%>
28 |
29 | <%= erb(:graph, :locals => { :graph => graph, :omit_link => true }) %>
30 |
31 | <% end %>
32 | <% end %>
33 |
34 |
35 | <% end %>
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------