ts-admin 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +24 -0
- data/LICENSE.txt +22 -0
- data/README.md +39 -0
- data/Rakefile +3 -0
- data/app.rb +21 -0
- data/app.yml.example +12 -0
- data/bin/ts-admin +26 -0
- data/config.ru +19 -0
- data/controller/init.rb +55 -0
- data/controller/main.rb +10 -0
- data/controller/remap.rb +86 -0
- data/layout/default.xhtml +47 -0
- data/lib/ts-admin/traffic_server.rb +157 -0
- data/lib/ts-admin/version.rb +3 -0
- data/lib/ts-admin.rb +5 -0
- data/public/css/font-awesome.min.css +24 -0
- data/public/css/grid.css +107 -0
- data/public/css/layout.css +254 -0
- data/public/css/reset.css +123 -0
- data/public/css/text.css +109 -0
- data/public/favicon.ico +0 -0
- data/public/font/FontAwesome.otf +0 -0
- data/public/font/fontawesome-webfont.eot +0 -0
- data/public/font/fontawesome-webfont.svg +339 -0
- data/public/font/fontawesome-webfont.ttf +0 -0
- data/public/font/fontawesome-webfont.woff +0 -0
- data/public/images/bg.png +0 -0
- data/spec/helper.rb +39 -0
- data/task/ramaze.rake +57 -0
- data/ts-admin.gemspec +25 -0
- data/view/index.xhtml +3 -0
- data/view/remap/form.xhtml +18 -0
- data/view/remap/index.xhtml +2 -0
- data/view/remap/list.xhtml +23 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a38d80d69b3c2bbae9e237d74acf92504366ce6a
|
4
|
+
data.tar.gz: e84f8bf4205e3ed0120aeeb2534f8501c53cccea
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 443bd6c4b09851e653eaf1fa7d097c39c03d08b792e22b5a7b765b8ebc71b4248f6221c2a3332d6ec66e024dc035d85c78027f67b711cdc39b42a65489f4c6b0
|
7
|
+
data.tar.gz: f6700bed37709a9d606bbcfd7be7720969f07ebbf0a93f4ac641f3f1a690b73632160fd56c2f3fe5f8124ee7e8821d520c043de0b5b42b2150eefaae580f75c9
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
ts-admin (0.2.0)
|
5
|
+
ramaze (>= 2.0.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
innate (2013.02.21)
|
11
|
+
rack (~> 1.5.2)
|
12
|
+
rack (1.5.2)
|
13
|
+
rake (10.0.4)
|
14
|
+
ramaze (2012.12.08)
|
15
|
+
innate (>= 2012.12)
|
16
|
+
rake
|
17
|
+
|
18
|
+
PLATFORMS
|
19
|
+
ruby
|
20
|
+
|
21
|
+
DEPENDENCIES
|
22
|
+
bundler (~> 1.3)
|
23
|
+
rake
|
24
|
+
ts-admin!
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 SIC! Software GmbH
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# Apache Traffic Server Frontend
|
2
|
+
|
3
|
+
This is a simple web frontend for the remap.config configuration file for the Apache Traffic Server.
|
4
|
+
It is used to manage reverse proxy mappings and 301 redirects. Please refer to [this blog post](http://blog.sic-software.com/2012/03/02/eine-administrationsoberflache-fur-den-apache-traffic-server/) for more information.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
$ gem install ts-admin
|
9
|
+
|
10
|
+
## Usage
|
11
|
+
|
12
|
+
Create a configuration file:
|
13
|
+
|
14
|
+
```yaml
|
15
|
+
port: 7000
|
16
|
+
|
17
|
+
auth:
|
18
|
+
username: admin
|
19
|
+
password: d033e22ae348aeb5660fc2140aec35850c4da997 # SHA1 hash
|
20
|
+
|
21
|
+
traffic_server:
|
22
|
+
config_path: /etc/trafficserver
|
23
|
+
restart_cmd: /etc/init.d/trafficserver restart
|
24
|
+
|
25
|
+
info:
|
26
|
+
external_ip: 192.168.0.1
|
27
|
+
```
|
28
|
+
|
29
|
+
Start the server:
|
30
|
+
|
31
|
+
$ ts-admin --config /path/to/your/config.yml
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
1. Fork it
|
36
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
37
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
38
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
39
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/app.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# This file contains your application, it requires dependencies and necessary
|
2
|
+
# parts of the application.
|
3
|
+
require 'rubygems'
|
4
|
+
require 'yaml'
|
5
|
+
require 'ramaze'
|
6
|
+
|
7
|
+
# Load app configuration
|
8
|
+
config_path = __DIR__('app.yml')
|
9
|
+
config_path = OPTIONS[:config] if defined?(OPTIONS)
|
10
|
+
APP_CONFIG = {
|
11
|
+
'auth' => {
|
12
|
+
'username' => 'admin',
|
13
|
+
'password' => '-'
|
14
|
+
}
|
15
|
+
}.merge(YAML.load_file(config_path))
|
16
|
+
|
17
|
+
# Make sure that Ramaze knows where you are
|
18
|
+
Ramaze.options.roots = [__DIR__]
|
19
|
+
|
20
|
+
require __DIR__('lib/ts-admin/traffic_server')
|
21
|
+
require __DIR__('controller/init')
|
data/app.yml.example
ADDED
data/bin/ts-admin
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Parse options
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
OPTIONS = {}
|
7
|
+
OptionParser.new do |opts|
|
8
|
+
opts.banner = "Usage: tsadmin [options]"
|
9
|
+
|
10
|
+
opts.on("-c", "--config [path]", "Port") do |config|
|
11
|
+
OPTIONS[:config] = config
|
12
|
+
end
|
13
|
+
opts.on("-p", "--port [port]", "Port") do |port|
|
14
|
+
OPTIONS[:port] = port.to_i
|
15
|
+
end
|
16
|
+
end.parse!
|
17
|
+
|
18
|
+
# Start TSAdmin
|
19
|
+
require File.expand_path('../../app', __FILE__)
|
20
|
+
|
21
|
+
Ramaze.start(
|
22
|
+
:adapter => :webrick,
|
23
|
+
:port => OPTIONS[:port] || APP_CONFIG['port'] || 7000,
|
24
|
+
:file => __FILE__,
|
25
|
+
:root => Ramaze.options.roots
|
26
|
+
)
|
data/config.ru
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env rackup
|
2
|
+
#
|
3
|
+
# config.ru for ramaze apps
|
4
|
+
#
|
5
|
+
# Rackup is a useful tool for running Rack applications, which uses the
|
6
|
+
# Rack::Builder DSL to configure middleware and build up applications easily.
|
7
|
+
#
|
8
|
+
# Rackup automatically figures out the environment it is run in, and runs your
|
9
|
+
# application as FastCGI, CGI, or standalone with Mongrel or WEBrick -- all from
|
10
|
+
# the same configuration.
|
11
|
+
#
|
12
|
+
# Do not set the adapter.handler in here, it will be ignored.
|
13
|
+
# You can choose the adapter like `ramaze start -s mongrel` or set it in the
|
14
|
+
# 'start.rb' and use `ruby start.rb` instead.
|
15
|
+
require ::File.expand_path('../app', __FILE__)
|
16
|
+
|
17
|
+
Ramaze.start(:root => Ramaze.options.roots, :started => true)
|
18
|
+
|
19
|
+
run Ramaze
|
data/controller/init.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Define a subclass of Ramaze::Controller holding your defaults for all controllers. Note
|
2
|
+
# that these changes can be overwritten in sub controllers by simply calling the method
|
3
|
+
# but with a different value.
|
4
|
+
|
5
|
+
class Controller < Ramaze::Controller
|
6
|
+
layout :default
|
7
|
+
helper :auth
|
8
|
+
helper :xhtml
|
9
|
+
helper :link
|
10
|
+
helper :flash
|
11
|
+
engine :etanni
|
12
|
+
|
13
|
+
def login
|
14
|
+
if logged_in?
|
15
|
+
call(r('/'))
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def auth_template
|
24
|
+
<<-TEMPLATE.strip!
|
25
|
+
<div class="grid_12 content login"><header><h2>Login</h2></header><div class="list-form">#{super}</div></div>
|
26
|
+
TEMPLATE
|
27
|
+
end
|
28
|
+
|
29
|
+
def auth_login(username, password)
|
30
|
+
return unless username and password
|
31
|
+
return if username.empty? or password.empty?
|
32
|
+
|
33
|
+
return unless APP_CONFIG['auth']['username'].downcase == username.downcase
|
34
|
+
return unless APP_CONFIG['auth']['password'] == Digest::SHA1.hexdigest(password)
|
35
|
+
|
36
|
+
session[:logged_in] = true
|
37
|
+
session[:username] = APP_CONFIG['auth']['username']
|
38
|
+
end
|
39
|
+
|
40
|
+
def login_required
|
41
|
+
super
|
42
|
+
@username = session[:username]
|
43
|
+
end
|
44
|
+
|
45
|
+
def traffic_server
|
46
|
+
@_traffic_server ||= ::TSAdmin::TrafficServer.new(APP_CONFIG['traffic_server'])
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
# Here you can require all your other controllers. Note that if you have multiple
|
52
|
+
# controllers you might want to do something like the following:
|
53
|
+
Dir.glob("#{__DIR__}/*.rb").each do |controller|
|
54
|
+
require(controller)
|
55
|
+
end
|
data/controller/main.rb
ADDED
data/controller/remap.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
class RemapController < Controller
|
2
|
+
|
3
|
+
def index
|
4
|
+
return unless login_required
|
5
|
+
set_env
|
6
|
+
|
7
|
+
@traffic_server = traffic_server
|
8
|
+
end
|
9
|
+
|
10
|
+
def new
|
11
|
+
return unless login_required
|
12
|
+
set_env
|
13
|
+
|
14
|
+
if request.post?
|
15
|
+
@from = request[:from]
|
16
|
+
@to = request[:to]
|
17
|
+
if traffic_server.add_remap(@type, @from, @to)
|
18
|
+
traffic_server.save
|
19
|
+
traffic_server.restart
|
20
|
+
flash[:info] = "Remap entry added"
|
21
|
+
call(r('/'))
|
22
|
+
else
|
23
|
+
flash[:error] = "Invalid Remap entry"
|
24
|
+
render_view :form
|
25
|
+
end
|
26
|
+
else
|
27
|
+
render_view :form
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def edit
|
32
|
+
return unless login_required
|
33
|
+
set_env
|
34
|
+
|
35
|
+
@id = request[:id]
|
36
|
+
|
37
|
+
if request.post?
|
38
|
+
@from = request[:from]
|
39
|
+
@to = request[:to]
|
40
|
+
if traffic_server.edit_remap(@id, @from, @to)
|
41
|
+
traffic_server.save
|
42
|
+
traffic_server.restart
|
43
|
+
flash[:info] = "Remap entry updated"
|
44
|
+
call(r('/'))
|
45
|
+
else
|
46
|
+
flash[:error] = "Invalid Remap entry"
|
47
|
+
render_view :form
|
48
|
+
end
|
49
|
+
else
|
50
|
+
@entry = traffic_server.find_remap_by_id(@id)
|
51
|
+
@from = @entry[:from]
|
52
|
+
@to = @entry[:to]
|
53
|
+
@type = @entry[:type]
|
54
|
+
render_view :form
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def delete
|
59
|
+
return unless login_required
|
60
|
+
set_env
|
61
|
+
|
62
|
+
traffic_server.delete_remap(request[:id])
|
63
|
+
traffic_server.save
|
64
|
+
traffic_server.restart
|
65
|
+
|
66
|
+
flash[:info] = "Remap entry removed"
|
67
|
+
|
68
|
+
call(r('/'))
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def remap_url_highlight_scheme(url)
|
74
|
+
scheme, hostpath = url.downcase.split(/:\/\//, 2)
|
75
|
+
<<-LINK.strip!
|
76
|
+
<span class="#{scheme}">#{scheme}</span>://#{hostpath}
|
77
|
+
LINK
|
78
|
+
end
|
79
|
+
|
80
|
+
def set_env
|
81
|
+
@title = 'Remap'
|
82
|
+
@nav = :remap
|
83
|
+
@type = request[:type]
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8" />
|
5
|
+
<title>TSAdmin#{" :: #{@title}" if defined?(@title)}</title>
|
6
|
+
#{css('reset')}
|
7
|
+
#{css('grid')}
|
8
|
+
#{css('layout')}
|
9
|
+
#{css('text')}
|
10
|
+
#{css('font-awesome.min')}
|
11
|
+
</head>
|
12
|
+
<body>
|
13
|
+
<div class="container">
|
14
|
+
<div id="container" class="row">
|
15
|
+
<div id="top" class="grid_12">
|
16
|
+
<header class="grid_6">
|
17
|
+
<h1><a href="/">TSAdmin</a></h1>
|
18
|
+
</header>
|
19
|
+
|
20
|
+
<?r if @username ?>
|
21
|
+
<nav class="grid_6 last">
|
22
|
+
<ul class="clearfix">
|
23
|
+
<li>
|
24
|
+
<a href="/remap" class="#{'current' if @nav == :remap}">Remap</a>
|
25
|
+
</li>
|
26
|
+
<li>
|
27
|
+
<a href="/logout">Logout</a>
|
28
|
+
</li>
|
29
|
+
</ul>
|
30
|
+
</nav>
|
31
|
+
<?r end ?>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
#{flashbox}
|
35
|
+
|
36
|
+
#{@content}
|
37
|
+
</div>
|
38
|
+
|
39
|
+
<footer id="footer">
|
40
|
+
<p>
|
41
|
+
© 2012-2013 SIC! Software GmbH<br />
|
42
|
+
TSAdmin is free software and is licensed under the MIT license.
|
43
|
+
</p>
|
44
|
+
</footer>
|
45
|
+
</div>
|
46
|
+
</body>
|
47
|
+
</html>
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module TSAdmin
|
5
|
+
class TrafficServer
|
6
|
+
MARKER_TEXT = "CONFIGURATION FROM TSADMIN"
|
7
|
+
|
8
|
+
def initialize(options={})
|
9
|
+
@options = {
|
10
|
+
'config_path' => '/etc/trafficserver',
|
11
|
+
'restart_cmd' => '/etc/init.d/trafficserver restart'
|
12
|
+
}.merge(options)
|
13
|
+
@config_path = @options['config_path']
|
14
|
+
end
|
15
|
+
|
16
|
+
def redirects
|
17
|
+
load unless defined?(@redirects)
|
18
|
+
@redirects.dup.freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
def maps
|
22
|
+
load unless defined?(@maps)
|
23
|
+
@maps.dup.freeze
|
24
|
+
end
|
25
|
+
|
26
|
+
def find_remap_by_id(id)
|
27
|
+
(maps + redirects).select{|m| m[:id] == id}.first
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_remap(type, from, to, options={})
|
31
|
+
load
|
32
|
+
|
33
|
+
return unless list = case type.to_sym
|
34
|
+
when :map
|
35
|
+
@maps
|
36
|
+
when :redirect
|
37
|
+
@redirects
|
38
|
+
else
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
id = Digest::SHA1.hexdigest("#{from}_#{to}")
|
43
|
+
|
44
|
+
return false if find_remap_by_id(id)
|
45
|
+
return false unless validate_url(from) && validate_url(to)
|
46
|
+
|
47
|
+
list << {:id => id, :type => type.to_sym, :from => from, :to => to, :options => options}
|
48
|
+
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def edit_remap(id, from, to, options=nil)
|
53
|
+
return false unless validate_url(from) && validate_url(to)
|
54
|
+
return false unless entry = find_remap_by_id(id)
|
55
|
+
entry[:from] = from
|
56
|
+
entry[:to] = to
|
57
|
+
entry[:options] = options unless options.nil?
|
58
|
+
entry[:id] = Digest::SHA1.hexdigest("#{from}_#{to}")
|
59
|
+
end
|
60
|
+
|
61
|
+
def delete_remap(id)
|
62
|
+
load
|
63
|
+
|
64
|
+
@maps.delete_if{|m| m[:id] == id}
|
65
|
+
@redirects.delete_if{|m| m[:id] == id}
|
66
|
+
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
def save
|
71
|
+
own_config = ''
|
72
|
+
own_config << "#{marker_begin}\n"
|
73
|
+
redirects.each do |redirect|
|
74
|
+
own_config << "redirect #{redirect[:from]} #{redirect[:to]} #{redirect[:options] if redirect[:options]}\n"
|
75
|
+
end
|
76
|
+
maps.each do |map|
|
77
|
+
own_config << "map #{map[:from]} #{map[:to]} #{map[:options] if map[:options]}\n"
|
78
|
+
end
|
79
|
+
own_config << "#{marker_end}\n"
|
80
|
+
|
81
|
+
file_content = ''
|
82
|
+
in_config_block = false
|
83
|
+
own_config_written = false
|
84
|
+
File.read(remap_path).each_line do |line|
|
85
|
+
if line.strip == marker_begin
|
86
|
+
in_config_block = true
|
87
|
+
next
|
88
|
+
elsif line.strip == marker_end
|
89
|
+
in_config_block = false
|
90
|
+
next
|
91
|
+
end
|
92
|
+
|
93
|
+
if in_config_block
|
94
|
+
file_content << own_config unless own_config_written
|
95
|
+
own_config_written = true
|
96
|
+
else
|
97
|
+
file_content << line
|
98
|
+
end
|
99
|
+
end
|
100
|
+
file_content << own_config unless own_config_written
|
101
|
+
|
102
|
+
remap_file = File.new(remap_path, 'w')
|
103
|
+
remap_file.write(file_content)
|
104
|
+
remap_file.close
|
105
|
+
end
|
106
|
+
|
107
|
+
def restart
|
108
|
+
`#{@options['restart_cmd']}`
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def marker_begin
|
114
|
+
"# BEGIN #{MARKER_TEXT}"
|
115
|
+
end
|
116
|
+
|
117
|
+
def marker_end
|
118
|
+
"# END #{MARKER_TEXT}"
|
119
|
+
end
|
120
|
+
|
121
|
+
def remap_path
|
122
|
+
File.join(@config_path, 'remap.config')
|
123
|
+
end
|
124
|
+
|
125
|
+
def load
|
126
|
+
@redirects = []
|
127
|
+
@maps = []
|
128
|
+
|
129
|
+
in_config_block = false
|
130
|
+
File.read(remap_path).each_line do |line|
|
131
|
+
if line.strip == marker_begin
|
132
|
+
in_config_block = true
|
133
|
+
next
|
134
|
+
elsif line.strip == marker_end
|
135
|
+
in_config_block = false
|
136
|
+
next
|
137
|
+
end
|
138
|
+
|
139
|
+
next unless in_config_block
|
140
|
+
|
141
|
+
type, from, to, *options = line.split(/\s+/)
|
142
|
+
id = Digest::SHA1.hexdigest("#{from}_#{to}")
|
143
|
+
case type.to_sym
|
144
|
+
when :redirect
|
145
|
+
@redirects << {:id => id, :type => type.to_sym, :from => from, :to => to, :options => {}}
|
146
|
+
when :map
|
147
|
+
@maps << {:id => id, :type => type.to_sym, :from => from, :to => to, :options => {}}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def validate_url(url)
|
153
|
+
!!(url =~ URI::regexp) rescue false
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
data/lib/ts-admin.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
/*!
|
2
|
+
* Font Awesome 3.1.0
|
3
|
+
* the iconic font designed for Bootstrap
|
4
|
+
* -------------------------------------------------------
|
5
|
+
* The full suite of pictographic icons, examples, and documentation
|
6
|
+
* can be found at: http://fontawesome.io
|
7
|
+
*
|
8
|
+
* License
|
9
|
+
* -------------------------------------------------------
|
10
|
+
* - The Font Awesome font is licensed under the SIL Open Font License v1.1 -
|
11
|
+
* http://scripts.sil.org/OFL
|
12
|
+
* - Font Awesome CSS, LESS, and SASS files are licensed under the MIT License -
|
13
|
+
* http://opensource.org/licenses/mit-license.html
|
14
|
+
* - Font Awesome documentation licensed under CC BY 3.0 License -
|
15
|
+
* http://creativecommons.org/licenses/by/3.0/
|
16
|
+
* - Attribution is no longer required in Font Awesome 3.0, but much appreciated:
|
17
|
+
* "Font Awesome by Dave Gandy - http://fontawesome.io"
|
18
|
+
|
19
|
+
* Contact
|
20
|
+
* -------------------------------------------------------
|
21
|
+
* Email: dave@fontawesome.io
|
22
|
+
* Twitter: http://twitter.com/fortaweso_me
|
23
|
+
* Work: Lead Product Designer @ http://kyruus.com
|
24
|
+
*/@font-face{font-family:'FontAwesome';src:url('../font/fontawesome-webfont.eot?v=3.1.0');src:url('../font/fontawesome-webfont.eot?#iefix&v=3.1.0') format('embedded-opentype'),url('../font/fontawesome-webfont.woff?v=3.1.0') format('woff'),url('../font/fontawesome-webfont.ttf?v=3.1.0') format('truetype'),url('../font/fontawesome-webfont.svg#fontawesomeregular?v=3.1.0') format('svg');font-weight:normal;font-style:normal}[class^="icon-"],[class*=" icon-"]{font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em}[class^="icon-"]:before,[class*=" icon-"]:before{text-decoration:inherit;display:inline-block;speak:none}.icon-large:before{vertical-align:-10%;font-size:1.3333333333333333em}a [class^="icon-"],a [class*=" icon-"],a [class^="icon-"]:before,a [class*=" icon-"]:before{display:inline}[class^="icon-"].icon-fixed-width,[class*=" icon-"].icon-fixed-width{display:inline-block;width:1.2857142857142858em;text-align:center}[class^="icon-"].icon-fixed-width.icon-large,[class*=" icon-"].icon-fixed-width.icon-large{width:1.5714285714285714em}ul.icons-ul{list-style-type:none;text-indent:-0.7142857142857143em;margin-left:2.142857142857143em}ul.icons-ul>li .icon-li{width:.7142857142857143em;display:inline-block;text-align:center}[class^="icon-"].hide,[class*=" icon-"].hide{display:none}.icon-muted{color:#eee}.icon-light{color:#fff}.icon-dark{color:#333}.icon-border{border:solid 1px #eee;padding:.2em .25em .15em;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.icon-2x{font-size:2em}.icon-2x.icon-border{border-width:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.icon-3x{font-size:3em}.icon-3x.icon-border{border-width:3px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.icon-4x{font-size:4em}.icon-4x.icon-border{border-width:4px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.icon-5x{font-size:5em}.icon-5x.icon-border{border-width:5px;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.pull-right{float:right}.pull-left{float:left}[class^="icon-"].pull-left,[class*=" icon-"].pull-left{margin-right:.3em}[class^="icon-"].pull-right,[class*=" icon-"].pull-right{margin-left:.3em}[class^="icon-"],[class*=" icon-"]{display:inline;width:auto;height:auto;line-height:normal;vertical-align:baseline;background-image:none;background-position:0 0;background-repeat:repeat;margin-top:0}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"]{background-image:none}.btn [class^="icon-"].icon-large,.nav [class^="icon-"].icon-large,.btn [class*=" icon-"].icon-large,.nav [class*=" icon-"].icon-large{line-height:.9em}.btn [class^="icon-"].icon-spin,.nav [class^="icon-"].icon-spin,.btn [class*=" icon-"].icon-spin,.nav [class*=" icon-"].icon-spin{display:inline-block}.nav-tabs [class^="icon-"],.nav-pills [class^="icon-"],.nav-tabs [class*=" icon-"],.nav-pills [class*=" icon-"],.nav-tabs [class^="icon-"].icon-large,.nav-pills [class^="icon-"].icon-large,.nav-tabs [class*=" icon-"].icon-large,.nav-pills [class*=" icon-"].icon-large{line-height:.9em}.btn [class^="icon-"].pull-left.icon-2x,.btn [class*=" icon-"].pull-left.icon-2x,.btn [class^="icon-"].pull-right.icon-2x,.btn [class*=" icon-"].pull-right.icon-2x{margin-top:.18em}.btn [class^="icon-"].icon-spin.icon-large,.btn [class*=" icon-"].icon-spin.icon-large{line-height:.8em}.btn.btn-small [class^="icon-"].pull-left.icon-2x,.btn.btn-small [class*=" icon-"].pull-left.icon-2x,.btn.btn-small [class^="icon-"].pull-right.icon-2x,.btn.btn-small [class*=" icon-"].pull-right.icon-2x{margin-top:.25em}.btn.btn-large [class^="icon-"],.btn.btn-large [class*=" icon-"]{margin-top:0}.btn.btn-large [class^="icon-"].pull-left.icon-2x,.btn.btn-large [class*=" icon-"].pull-left.icon-2x,.btn.btn-large [class^="icon-"].pull-right.icon-2x,.btn.btn-large [class*=" icon-"].pull-right.icon-2x{margin-top:.05em}.btn.btn-large [class^="icon-"].pull-left.icon-2x,.btn.btn-large [class*=" icon-"].pull-left.icon-2x{margin-right:.2em}.btn.btn-large [class^="icon-"].pull-right.icon-2x,.btn.btn-large [class*=" icon-"].pull-right.icon-2x{margin-left:.2em}.icon-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:-35%}.icon-stack [class^="icon-"],.icon-stack [class*=" icon-"]{display:block;text-align:center;position:absolute;width:100%;height:100%;font-size:1em;line-height:inherit;*line-height:2em}.icon-stack .icon-stack-base{font-size:2em;*line-height:1em}.icon-spin{display:inline-block;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.icon-rotate-90:before{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1)}.icon-rotate-180:before{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2)}.icon-rotate-270:before{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3)}.icon-flip-horizontal:before{-webkit-transform:scale(-1,1);-moz-transform:scale(-1,1);-ms-transform:scale(-1,1);-o-transform:scale(-1,1);transform:scale(-1,1)}.icon-flip-vertical:before{-webkit-transform:scale(1,-1);-moz-transform:scale(1,-1);-ms-transform:scale(1,-1);-o-transform:scale(1,-1);transform:scale(1,-1)}.icon-glass:before{content:"\f000"}.icon-music:before{content:"\f001"}.icon-search:before{content:"\f002"}.icon-envelope:before{content:"\f003"}.icon-heart:before{content:"\f004"}.icon-star:before{content:"\f005"}.icon-star-empty:before{content:"\f006"}.icon-user:before{content:"\f007"}.icon-film:before{content:"\f008"}.icon-th-large:before{content:"\f009"}.icon-th:before{content:"\f00a"}.icon-th-list:before{content:"\f00b"}.icon-ok:before{content:"\f00c"}.icon-remove:before{content:"\f00d"}.icon-zoom-in:before{content:"\f00e"}.icon-zoom-out:before{content:"\f010"}.icon-off:before{content:"\f011"}.icon-signal:before{content:"\f012"}.icon-cog:before{content:"\f013"}.icon-trash:before{content:"\f014"}.icon-home:before{content:"\f015"}.icon-file:before{content:"\f016"}.icon-time:before{content:"\f017"}.icon-road:before{content:"\f018"}.icon-download-alt:before{content:"\f019"}.icon-download:before{content:"\f01a"}.icon-upload:before{content:"\f01b"}.icon-inbox:before{content:"\f01c"}.icon-play-circle:before{content:"\f01d"}.icon-repeat:before,.icon-rotate-right:before{content:"\f01e"}.icon-refresh:before{content:"\f021"}.icon-list-alt:before{content:"\f022"}.icon-lock:before{content:"\f023"}.icon-flag:before{content:"\f024"}.icon-headphones:before{content:"\f025"}.icon-volume-off:before{content:"\f026"}.icon-volume-down:before{content:"\f027"}.icon-volume-up:before{content:"\f028"}.icon-qrcode:before{content:"\f029"}.icon-barcode:before{content:"\f02a"}.icon-tag:before{content:"\f02b"}.icon-tags:before{content:"\f02c"}.icon-book:before{content:"\f02d"}.icon-bookmark:before{content:"\f02e"}.icon-print:before{content:"\f02f"}.icon-camera:before{content:"\f030"}.icon-font:before{content:"\f031"}.icon-bold:before{content:"\f032"}.icon-italic:before{content:"\f033"}.icon-text-height:before{content:"\f034"}.icon-text-width:before{content:"\f035"}.icon-align-left:before{content:"\f036"}.icon-align-center:before{content:"\f037"}.icon-align-right:before{content:"\f038"}.icon-align-justify:before{content:"\f039"}.icon-list:before{content:"\f03a"}.icon-indent-left:before{content:"\f03b"}.icon-indent-right:before{content:"\f03c"}.icon-facetime-video:before{content:"\f03d"}.icon-picture:before{content:"\f03e"}.icon-pencil:before{content:"\f040"}.icon-map-marker:before{content:"\f041"}.icon-adjust:before{content:"\f042"}.icon-tint:before{content:"\f043"}.icon-edit:before{content:"\f044"}.icon-share:before{content:"\f045"}.icon-check:before{content:"\f046"}.icon-move:before{content:"\f047"}.icon-step-backward:before{content:"\f048"}.icon-fast-backward:before{content:"\f049"}.icon-backward:before{content:"\f04a"}.icon-play:before{content:"\f04b"}.icon-pause:before{content:"\f04c"}.icon-stop:before{content:"\f04d"}.icon-forward:before{content:"\f04e"}.icon-fast-forward:before{content:"\f050"}.icon-step-forward:before{content:"\f051"}.icon-eject:before{content:"\f052"}.icon-chevron-left:before{content:"\f053"}.icon-chevron-right:before{content:"\f054"}.icon-plus-sign:before{content:"\f055"}.icon-minus-sign:before{content:"\f056"}.icon-remove-sign:before{content:"\f057"}.icon-ok-sign:before{content:"\f058"}.icon-question-sign:before{content:"\f059"}.icon-info-sign:before{content:"\f05a"}.icon-screenshot:before{content:"\f05b"}.icon-remove-circle:before{content:"\f05c"}.icon-ok-circle:before{content:"\f05d"}.icon-ban-circle:before{content:"\f05e"}.icon-arrow-left:before{content:"\f060"}.icon-arrow-right:before{content:"\f061"}.icon-arrow-up:before{content:"\f062"}.icon-arrow-down:before{content:"\f063"}.icon-share-alt:before,.icon-mail-forward:before{content:"\f064"}.icon-resize-full:before{content:"\f065"}.icon-resize-small:before{content:"\f066"}.icon-plus:before{content:"\f067"}.icon-minus:before{content:"\f068"}.icon-asterisk:before{content:"\f069"}.icon-exclamation-sign:before{content:"\f06a"}.icon-gift:before{content:"\f06b"}.icon-leaf:before{content:"\f06c"}.icon-fire:before{content:"\f06d"}.icon-eye-open:before{content:"\f06e"}.icon-eye-close:before{content:"\f070"}.icon-warning-sign:before{content:"\f071"}.icon-plane:before{content:"\f072"}.icon-calendar:before{content:"\f073"}.icon-random:before{content:"\f074"}.icon-comment:before{content:"\f075"}.icon-magnet:before{content:"\f076"}.icon-chevron-up:before{content:"\f077"}.icon-chevron-down:before{content:"\f078"}.icon-retweet:before{content:"\f079"}.icon-shopping-cart:before{content:"\f07a"}.icon-folder-close:before{content:"\f07b"}.icon-folder-open:before{content:"\f07c"}.icon-resize-vertical:before{content:"\f07d"}.icon-resize-horizontal:before{content:"\f07e"}.icon-bar-chart:before{content:"\f080"}.icon-twitter-sign:before{content:"\f081"}.icon-facebook-sign:before{content:"\f082"}.icon-camera-retro:before{content:"\f083"}.icon-key:before{content:"\f084"}.icon-cogs:before{content:"\f085"}.icon-comments:before{content:"\f086"}.icon-thumbs-up:before{content:"\f087"}.icon-thumbs-down:before{content:"\f088"}.icon-star-half:before{content:"\f089"}.icon-heart-empty:before{content:"\f08a"}.icon-signout:before{content:"\f08b"}.icon-linkedin-sign:before{content:"\f08c"}.icon-pushpin:before{content:"\f08d"}.icon-external-link:before{content:"\f08e"}.icon-signin:before{content:"\f090"}.icon-trophy:before{content:"\f091"}.icon-github-sign:before{content:"\f092"}.icon-upload-alt:before{content:"\f093"}.icon-lemon:before{content:"\f094"}.icon-phone:before{content:"\f095"}.icon-check-empty:before{content:"\f096"}.icon-bookmark-empty:before{content:"\f097"}.icon-phone-sign:before{content:"\f098"}.icon-twitter:before{content:"\f099"}.icon-facebook:before{content:"\f09a"}.icon-github:before{content:"\f09b"}.icon-unlock:before{content:"\f09c"}.icon-credit-card:before{content:"\f09d"}.icon-rss:before{content:"\f09e"}.icon-hdd:before{content:"\f0a0"}.icon-bullhorn:before{content:"\f0a1"}.icon-bell:before{content:"\f0a2"}.icon-certificate:before{content:"\f0a3"}.icon-hand-right:before{content:"\f0a4"}.icon-hand-left:before{content:"\f0a5"}.icon-hand-up:before{content:"\f0a6"}.icon-hand-down:before{content:"\f0a7"}.icon-circle-arrow-left:before{content:"\f0a8"}.icon-circle-arrow-right:before{content:"\f0a9"}.icon-circle-arrow-up:before{content:"\f0aa"}.icon-circle-arrow-down:before{content:"\f0ab"}.icon-globe:before{content:"\f0ac"}.icon-wrench:before{content:"\f0ad"}.icon-tasks:before{content:"\f0ae"}.icon-filter:before{content:"\f0b0"}.icon-briefcase:before{content:"\f0b1"}.icon-fullscreen:before{content:"\f0b2"}.icon-group:before{content:"\f0c0"}.icon-link:before{content:"\f0c1"}.icon-cloud:before{content:"\f0c2"}.icon-beaker:before{content:"\f0c3"}.icon-cut:before{content:"\f0c4"}.icon-copy:before{content:"\f0c5"}.icon-paper-clip:before{content:"\f0c6"}.icon-save:before{content:"\f0c7"}.icon-sign-blank:before{content:"\f0c8"}.icon-reorder:before{content:"\f0c9"}.icon-list-ul:before{content:"\f0ca"}.icon-list-ol:before{content:"\f0cb"}.icon-strikethrough:before{content:"\f0cc"}.icon-underline:before{content:"\f0cd"}.icon-table:before{content:"\f0ce"}.icon-magic:before{content:"\f0d0"}.icon-truck:before{content:"\f0d1"}.icon-pinterest:before{content:"\f0d2"}.icon-pinterest-sign:before{content:"\f0d3"}.icon-google-plus-sign:before{content:"\f0d4"}.icon-google-plus:before{content:"\f0d5"}.icon-money:before{content:"\f0d6"}.icon-caret-down:before{content:"\f0d7"}.icon-caret-up:before{content:"\f0d8"}.icon-caret-left:before{content:"\f0d9"}.icon-caret-right:before{content:"\f0da"}.icon-columns:before{content:"\f0db"}.icon-sort:before{content:"\f0dc"}.icon-sort-down:before{content:"\f0dd"}.icon-sort-up:before{content:"\f0de"}.icon-envelope-alt:before{content:"\f0e0"}.icon-linkedin:before{content:"\f0e1"}.icon-undo:before,.icon-rotate-left:before{content:"\f0e2"}.icon-legal:before{content:"\f0e3"}.icon-dashboard:before{content:"\f0e4"}.icon-comment-alt:before{content:"\f0e5"}.icon-comments-alt:before{content:"\f0e6"}.icon-bolt:before{content:"\f0e7"}.icon-sitemap:before{content:"\f0e8"}.icon-umbrella:before{content:"\f0e9"}.icon-paste:before{content:"\f0ea"}.icon-lightbulb:before{content:"\f0eb"}.icon-exchange:before{content:"\f0ec"}.icon-cloud-download:before{content:"\f0ed"}.icon-cloud-upload:before{content:"\f0ee"}.icon-user-md:before{content:"\f0f0"}.icon-stethoscope:before{content:"\f0f1"}.icon-suitcase:before{content:"\f0f2"}.icon-bell-alt:before{content:"\f0f3"}.icon-coffee:before{content:"\f0f4"}.icon-food:before{content:"\f0f5"}.icon-file-alt:before{content:"\f0f6"}.icon-building:before{content:"\f0f7"}.icon-hospital:before{content:"\f0f8"}.icon-ambulance:before{content:"\f0f9"}.icon-medkit:before{content:"\f0fa"}.icon-fighter-jet:before{content:"\f0fb"}.icon-beer:before{content:"\f0fc"}.icon-h-sign:before{content:"\f0fd"}.icon-plus-sign-alt:before{content:"\f0fe"}.icon-double-angle-left:before{content:"\f100"}.icon-double-angle-right:before{content:"\f101"}.icon-double-angle-up:before{content:"\f102"}.icon-double-angle-down:before{content:"\f103"}.icon-angle-left:before{content:"\f104"}.icon-angle-right:before{content:"\f105"}.icon-angle-up:before{content:"\f106"}.icon-angle-down:before{content:"\f107"}.icon-desktop:before{content:"\f108"}.icon-laptop:before{content:"\f109"}.icon-tablet:before{content:"\f10a"}.icon-mobile-phone:before{content:"\f10b"}.icon-circle-blank:before{content:"\f10c"}.icon-quote-left:before{content:"\f10d"}.icon-quote-right:before{content:"\f10e"}.icon-spinner:before{content:"\f110"}.icon-circle:before{content:"\f111"}.icon-reply:before,.icon-mail-reply:before{content:"\f112"}.icon-folder-close-alt:before{content:"\f114"}.icon-folder-open-alt:before{content:"\f115"}.icon-expand-alt:before{content:"\f116"}.icon-collapse-alt:before{content:"\f117"}.icon-smile:before{content:"\f118"}.icon-frown:before{content:"\f119"}.icon-meh:before{content:"\f11a"}.icon-gamepad:before{content:"\f11b"}.icon-keyboard:before{content:"\f11c"}.icon-flag-alt:before{content:"\f11d"}.icon-flag-checkered:before{content:"\f11e"}.icon-terminal:before{content:"\f120"}.icon-code:before{content:"\f121"}.icon-reply-all:before{content:"\f122"}.icon-mail-reply-all:before{content:"\f122"}.icon-star-half-full:before,.icon-star-half-empty:before{content:"\f123"}.icon-location-arrow:before{content:"\f124"}.icon-crop:before{content:"\f125"}.icon-code-fork:before{content:"\f126"}.icon-unlink:before{content:"\f127"}.icon-question:before{content:"\f128"}.icon-info:before{content:"\f129"}.icon-exclamation:before{content:"\f12a"}.icon-superscript:before{content:"\f12b"}.icon-subscript:before{content:"\f12c"}.icon-eraser:before{content:"\f12d"}.icon-puzzle-piece:before{content:"\f12e"}.icon-microphone:before{content:"\f130"}.icon-microphone-off:before{content:"\f131"}.icon-shield:before{content:"\f132"}.icon-calendar-empty:before{content:"\f133"}.icon-fire-extinguisher:before{content:"\f134"}.icon-rocket:before{content:"\f135"}.icon-maxcdn:before{content:"\f136"}.icon-chevron-sign-left:before{content:"\f137"}.icon-chevron-sign-right:before{content:"\f138"}.icon-chevron-sign-up:before{content:"\f139"}.icon-chevron-sign-down:before{content:"\f13a"}.icon-html5:before{content:"\f13b"}.icon-css3:before{content:"\f13c"}.icon-anchor:before{content:"\f13d"}.icon-unlock-alt:before{content:"\f13e"}.icon-bullseye:before{content:"\f140"}.icon-ellipsis-horizontal:before{content:"\f141"}.icon-ellipsis-vertical:before{content:"\f142"}.icon-rss-sign:before{content:"\f143"}.icon-play-sign:before{content:"\f144"}.icon-ticket:before{content:"\f145"}.icon-minus-sign-alt:before{content:"\f146"}.icon-check-minus:before{content:"\f147"}.icon-level-up:before{content:"\f148"}.icon-level-down:before{content:"\f149"}.icon-check-sign:before{content:"\f14a"}.icon-edit-sign:before{content:"\f14b"}.icon-external-link-sign:before{content:"\f14c"}.icon-share-sign:before{content:"\f14d"}
|