robut 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +17 -0
- data/README.rdoc +124 -0
- data/Rakefile +27 -0
- data/bin/robut +9 -0
- data/examples/Chatfile +22 -0
- data/lib/robut.rb +5 -0
- data/lib/robut/connection.rb +167 -0
- data/lib/robut/plugin.rb +16 -0
- data/lib/robut/plugin/base.rb +70 -0
- data/lib/robut/plugin/calc.rb +20 -0
- data/lib/robut/plugin/echo.rb +14 -0
- data/lib/robut/plugin/later.rb +73 -0
- data/lib/robut/plugin/lunch.rb +57 -0
- data/lib/robut/plugin/meme.rb +30 -0
- data/lib/robut/plugin/ping.rb +11 -0
- data/lib/robut/plugin/rdio.rb +70 -0
- data/lib/robut/plugin/rdio/public/css/rdio.css +141 -0
- data/lib/robut/plugin/rdio/public/css/style.css +79 -0
- data/lib/robut/plugin/rdio/public/css/style_after.css +42 -0
- data/lib/robut/plugin/rdio/public/images/background.png +0 -0
- data/lib/robut/plugin/rdio/public/images/no-album.png +0 -0
- data/lib/robut/plugin/rdio/public/index.html +42 -0
- data/lib/robut/plugin/rdio/public/js/libs/dd_belatedpng.js +13 -0
- data/lib/robut/plugin/rdio/public/js/libs/jquery-1.5.1.min.js +16 -0
- data/lib/robut/plugin/rdio/public/js/libs/modernizr-1.7.min.js +2 -0
- data/lib/robut/plugin/rdio/public/js/rdio.js +129 -0
- data/lib/robut/plugin/rdio/public/js/script.js +3 -0
- data/lib/robut/plugin/rdio/server.rb +34 -0
- data/lib/robut/plugin/say.rb +20 -0
- data/lib/robut/plugin/sayings.rb +31 -0
- data/lib/robut/plugin/twss.rb +13 -0
- data/lib/robut/storage.rb +3 -0
- data/lib/robut/storage/base.rb +21 -0
- data/lib/robut/storage/hash_store.rb +26 -0
- data/lib/robut/storage/yaml_store.rb +51 -0
- data/lib/robut/version.rb +4 -0
- data/robut.gemspec +23 -0
- data/test/mocks/connection_mock.rb +26 -0
- data/test/simplecov_helper.rb +2 -0
- data/test/test_helper.rb +7 -0
- data/test/unit/connection_test.rb +123 -0
- data/test/unit/plugin/base_test.rb +16 -0
- data/test/unit/plugin/echo_test.rb +26 -0
- data/test/unit/plugin/later_test.rb +39 -0
- data/test/unit/plugin/lunch_test.rb +35 -0
- data/test/unit/plugin/ping_test.rb +21 -0
- data/test/unit/plugin/say_test.rb +28 -0
- data/test/unit/storage/hash_store_test.rb +15 -0
- data/test/unit/storage/yaml_store_test.rb +42 -0
- data/test/unit/storage/yaml_test.yml +1 -0
- metadata +135 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'calc'
|
2
|
+
|
3
|
+
# A simple calculator. This delegates all calculations to the 'calc'
|
4
|
+
# gem.
|
5
|
+
class Robut::Plugin::Calc < Robut::Plugin::Base
|
6
|
+
|
7
|
+
# Perform the calculation specified in +message+, and send the
|
8
|
+
# result back.
|
9
|
+
def handle(time, sender_nick, message)
|
10
|
+
if sent_to_me?(message) && words(message).first == 'calc'
|
11
|
+
calculation = words(message, 'calc').join(' ')
|
12
|
+
begin
|
13
|
+
reply("#{calculation} = #{Calc.evaluate(calculation)}")
|
14
|
+
rescue
|
15
|
+
reply("Can't calculate that.")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
# A plugin that tells robut to repeat whatever he's told.
|
3
|
+
class Robut::Plugin::Echo < Robut::Plugin::Base
|
4
|
+
|
5
|
+
# Responds with +message+ if the command sent to robut is 'echo'.
|
6
|
+
def handle(time, sender_nick, message)
|
7
|
+
words = words(message)
|
8
|
+
if sent_to_me?(message) && words.first == 'echo'
|
9
|
+
phrase = words[1..-1].join(' ')
|
10
|
+
reply(phrase) unless phrase.empty?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# The Later plugin allows you to send messages/commands to robut based on
|
2
|
+
# a time delay. Like so:
|
3
|
+
#
|
4
|
+
# @robut in 5 minutes lunch?
|
5
|
+
# @robut in 1 hr echo @justin wake up!
|
6
|
+
#
|
7
|
+
# @robut will respond effectively the same as he would if someone had told
|
8
|
+
# him "lunch?" 5 minutes from now. In the case of the Lunch plugin, he
|
9
|
+
# would repond with a lunch suggestion. The Later plugin works with all other
|
10
|
+
# plugins as you follow this syntax:
|
11
|
+
#
|
12
|
+
# @robut in <number> <mins|hrs|secs> [command]
|
13
|
+
#
|
14
|
+
# Where command is the message you want to send to @robut in the future. For
|
15
|
+
# the time denominations it also recognizes minute, minutes, hour, hours,
|
16
|
+
# second, seconds.
|
17
|
+
#
|
18
|
+
class Robut::Plugin::Later < Robut::Plugin::Base
|
19
|
+
|
20
|
+
# Passes +message+ back through the plugin chain if we've been given
|
21
|
+
# a time to execute it later.
|
22
|
+
def handle(time, sender_nick, message)
|
23
|
+
if sent_to_me?(message)
|
24
|
+
phrase = words(message).join(' ')
|
25
|
+
if phrase =~ timer_regex
|
26
|
+
count = $1.to_i
|
27
|
+
scale = $2
|
28
|
+
future_message = at_nick + ' ' + $3
|
29
|
+
|
30
|
+
sleep_time = count * scale_multiplier(scale)
|
31
|
+
|
32
|
+
reply("Ok, see you in #{count} #{scale}")
|
33
|
+
|
34
|
+
connection = self.connection
|
35
|
+
threader do
|
36
|
+
sleep sleep_time
|
37
|
+
# TODO: ensure this connection is threadsafe
|
38
|
+
plugins = Robut::Plugin.plugins.map { |p| p.new(connection, private_sender) }
|
39
|
+
connection.handle_message(plugins, Time.now, sender_nick, future_message)
|
40
|
+
end
|
41
|
+
return true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# A regex that detects a time.
|
49
|
+
def timer_regex
|
50
|
+
/in (.*) (sec|secs|second|seconds|min|mins|minute|minutes|hr|hrs|hour|hours) (.*)$/
|
51
|
+
end
|
52
|
+
|
53
|
+
# Takes a time scale (secs, mins, hours, etc.) and returns the
|
54
|
+
# factor to convert it into seconds.
|
55
|
+
def scale_multiplier(time_scale)
|
56
|
+
case time_scale
|
57
|
+
when /sec(s|ond|onds)?/
|
58
|
+
1
|
59
|
+
when /min(s|ute|utes)?/
|
60
|
+
60
|
61
|
+
when /(hr|hrs|hour|hours)/
|
62
|
+
60 * 60
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Asynchronously runs the given block.
|
67
|
+
def threader
|
68
|
+
Thread.new do
|
69
|
+
yield
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Where should we go to lunch today?
|
2
|
+
class Robut::Plugin::Lunch < Robut::Plugin::Base
|
3
|
+
|
4
|
+
# Replies with a random string selected from +places+.
|
5
|
+
def handle(time, sender_nick, message)
|
6
|
+
words = words(message)
|
7
|
+
phrase = words.join(' ')
|
8
|
+
# lunch?
|
9
|
+
if phrase =~ /(lunch|food)\?/i
|
10
|
+
if places.empty?
|
11
|
+
reply "I don't know about any lunch places"
|
12
|
+
else
|
13
|
+
reply places[rand(places.length)] + "!"
|
14
|
+
end
|
15
|
+
# @robut lunch places
|
16
|
+
elsif phrase == "lunch places" && sent_to_me?(message)
|
17
|
+
if places.empty?
|
18
|
+
reply "I don't know about any lunch places"
|
19
|
+
else
|
20
|
+
reply places.join(', ')
|
21
|
+
end
|
22
|
+
# @robut new lunch place Green Leaf
|
23
|
+
elsif phrase =~ /new lunch place (.*)/i && sent_to_me?(message)
|
24
|
+
place = $1
|
25
|
+
new_place(place)
|
26
|
+
reply "Ok, I'll add \"#{place}\" to the the list of lunch places"
|
27
|
+
# @robut remove luynch place Green Leaf
|
28
|
+
elsif phrase =~ /remove lunch place (.*)/i && sent_to_me?(message)
|
29
|
+
place = $1
|
30
|
+
remove_place(place)
|
31
|
+
reply "I removed \"#{place}\" from the list of lunch places"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Stores +place+ as a new lunch place.
|
36
|
+
def new_place(place)
|
37
|
+
store["lunch_places"] ||= []
|
38
|
+
store["lunch_places"] = (store["lunch_places"] + Array(place)).uniq
|
39
|
+
end
|
40
|
+
|
41
|
+
# Removes +place+ from the list of lunch places.
|
42
|
+
def remove_place(place)
|
43
|
+
store["lunch_places"] ||= []
|
44
|
+
store["lunch_places"] = store["lunch_places"] - Array(place)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the list of lunch places we know about.
|
48
|
+
def places
|
49
|
+
store["lunch_places"] ||= []
|
50
|
+
end
|
51
|
+
|
52
|
+
# Sets the list of lunch places to +v+
|
53
|
+
def places=(v)
|
54
|
+
store["lunch_places"] = v
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'meme'
|
2
|
+
|
3
|
+
# A simple plugin that wraps meme_generator. Requires the
|
4
|
+
# 'meme_generator' gem.
|
5
|
+
class Robut::Plugin::Meme < Robut::Plugin::Base
|
6
|
+
|
7
|
+
# This plugin is activated when robut is sent a message starting
|
8
|
+
# with the name of a meme. The list of generators can be discovered
|
9
|
+
# by running
|
10
|
+
#
|
11
|
+
# meme list
|
12
|
+
#
|
13
|
+
# from the command line. Example:
|
14
|
+
#
|
15
|
+
# @robut h_mermaid look at this stuff, isn't it neat; my vinyl collection is almost complete
|
16
|
+
#
|
17
|
+
# Send message to the specified meme generator. If the meme requires
|
18
|
+
# more than one line of text, lines should be separated with a semicolon.
|
19
|
+
def handle(time, sender_nick, message)
|
20
|
+
word = words(message).first
|
21
|
+
if sent_to_me?(message) && Meme::GENERATORS.has_key?(word.upcase)
|
22
|
+
words = words(message)
|
23
|
+
g = Meme.new(words.shift.upcase)
|
24
|
+
line1, line2 = words.join(' ').split(';')
|
25
|
+
|
26
|
+
reply(g.generate(line1, line2))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
|
2
|
+
# A simple plugin that replies with "pong" when messaged with ping
|
3
|
+
class Robut::Plugin::Ping < Robut::Plugin::Base
|
4
|
+
|
5
|
+
# Responds "pong" if +message+ is "ping"
|
6
|
+
def handle(time, sender_nick, message)
|
7
|
+
words = words(message)
|
8
|
+
reply("pong") if sent_to_me?(message) && words.length == 1 && words.first.downcase == 'ping'
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'rdio'
|
2
|
+
require 'robut/plugin/rdio/server'
|
3
|
+
|
4
|
+
# A plugin that hooks into Rdio, allowing you to queue songs from
|
5
|
+
# HipChat. +key+ and +secret+ must be set before we can deal with any
|
6
|
+
# Rdio commands. Additionally, you must call +start_server+ in your
|
7
|
+
# Chatfile to start the Rdio web server.
|
8
|
+
class Robut::Plugin::Rdio < Robut::Plugin::Base
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# Your Rdio API Key
|
12
|
+
attr_accessor :key
|
13
|
+
|
14
|
+
# Your Rdio API app secret
|
15
|
+
attr_accessor :secret
|
16
|
+
|
17
|
+
# The port the Rdio web player will run on. Defaults to 4567.
|
18
|
+
attr_accessor :port
|
19
|
+
|
20
|
+
# The host the Rdio web player will run on. Defaults to localhost.
|
21
|
+
attr_accessor :host
|
22
|
+
end
|
23
|
+
|
24
|
+
# Starts a Robut::Plugin::Rdio::Server server for communicating with
|
25
|
+
# the actual Rdio web player. You must call this in the Chatfile if
|
26
|
+
# you plan on using this gem.
|
27
|
+
def self.start_server
|
28
|
+
@server = Thread.new { Server.run! :host => (host || "localhost"), :port => (port || 4567) }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Queues songs into the Rdio web player. @nick play search query
|
32
|
+
# will queue the first search result matching 'search query' into
|
33
|
+
# the web player. It can be an artist, album, or song.
|
34
|
+
def handle(time, sender_nick, message)
|
35
|
+
words = words(message)
|
36
|
+
if sent_to_me?(message) && words.first == 'play'
|
37
|
+
results = search(words)
|
38
|
+
result = results.first
|
39
|
+
if result
|
40
|
+
Server.queue << result.key
|
41
|
+
name = result.name
|
42
|
+
name = "#{result.artist_name} - #{name}" if result.respond_to?(:artist_name) && result.artist_name
|
43
|
+
reply("Playing #{name}")
|
44
|
+
else
|
45
|
+
reply("I couldn't find #{query_string} on Rdio.")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Searches Rdio for sources matching +words+. If the first word is
|
53
|
+
# 'track', it only searches tracks, same for 'album'. Otherwise,
|
54
|
+
# matches both albums and tracks.
|
55
|
+
def search(words)
|
56
|
+
api = Rdio::Api.new(self.class.key, self.class.secret)
|
57
|
+
|
58
|
+
if words[1] == "album"
|
59
|
+
query_string = words[2..-1].join(' ')
|
60
|
+
results = api.search(query_string, "Album")
|
61
|
+
elsif words[1] == "track"
|
62
|
+
query_string = words[2..-1].join(' ')
|
63
|
+
results = api.search(query_string, "Track")
|
64
|
+
else
|
65
|
+
query_string = words[1..-1].join(' ')
|
66
|
+
results = api.search(query_string, "Album,Track")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
|
2
|
+
#content {
|
3
|
+
margin: 16px 120px;
|
4
|
+
}
|
5
|
+
|
6
|
+
body {
|
7
|
+
background-color: #bbb;
|
8
|
+
background: url('/images/background.png');
|
9
|
+
}
|
10
|
+
|
11
|
+
h1, h2 {
|
12
|
+
background-color: #fff;
|
13
|
+
text-rendering: optimizeLegibility;
|
14
|
+
padding: 8px 12px;
|
15
|
+
display: none;
|
16
|
+
}
|
17
|
+
|
18
|
+
h1 {
|
19
|
+
font-size: 2.2em;
|
20
|
+
margin-bottom: 24px;
|
21
|
+
}
|
22
|
+
|
23
|
+
h2 {
|
24
|
+
clear: left;
|
25
|
+
font-size: 1.8em;
|
26
|
+
-moz-border-radius-topleft: 4px;
|
27
|
+
-moz-border-radius-topright: 4px;
|
28
|
+
-moz-border-radius-bottomright: 0px;
|
29
|
+
-moz-border-radius-bottomleft: 0px;
|
30
|
+
border-top-left-radius: 4px;
|
31
|
+
border-top-right-radius: 4px;
|
32
|
+
border-bottom-right-radius: 0px;
|
33
|
+
border-bottom-left-radius: 0px;
|
34
|
+
}
|
35
|
+
|
36
|
+
#player {
|
37
|
+
margin-bottom: 32px;
|
38
|
+
}
|
39
|
+
|
40
|
+
#player:after {
|
41
|
+
content: " ";
|
42
|
+
display: block;
|
43
|
+
height: 0;
|
44
|
+
clear: both;
|
45
|
+
visibility: hidden;
|
46
|
+
}
|
47
|
+
|
48
|
+
#filter {
|
49
|
+
background: none; /* Old browsers */
|
50
|
+
|
51
|
+
background: -webkit-gradient(linear, 0 0, 0 100%,
|
52
|
+
color-stop(0, rgba(255,255,255,0.30)),
|
53
|
+
color-stop(0.5, rgba(255,255,255,0.25)),
|
54
|
+
color-stop(0.5, rgba(255,255,255,0.1)),
|
55
|
+
color-stop(1, rgba(255,255,255,0.30)));
|
56
|
+
|
57
|
+
position: absolute;
|
58
|
+
top: 0px;
|
59
|
+
left: 0px;
|
60
|
+
height: 200px;
|
61
|
+
width: 200px;
|
62
|
+
}
|
63
|
+
|
64
|
+
#controls {
|
65
|
+
position: relative;
|
66
|
+
width: 200px;
|
67
|
+
float: left;
|
68
|
+
min-height: 200px;
|
69
|
+
}
|
70
|
+
|
71
|
+
#controls img {
|
72
|
+
-webkit-box-shadow: 0px 3px 4px #666;
|
73
|
+
-moz-box-shadow: 0px 3px 4px #666;
|
74
|
+
box-shadow: 0px 3px 4px #666;
|
75
|
+
}
|
76
|
+
|
77
|
+
#controls img, #controls #filter, h1 {
|
78
|
+
-moz-border-radius: 4px;
|
79
|
+
border-radius: 4px;
|
80
|
+
}
|
81
|
+
|
82
|
+
ul {
|
83
|
+
margin: 0px;
|
84
|
+
list-style-type: none;
|
85
|
+
margin-bottom: 24px;
|
86
|
+
}
|
87
|
+
|
88
|
+
#now_playing {
|
89
|
+
margin-left: 220px;
|
90
|
+
}
|
91
|
+
|
92
|
+
li, h1, h2 {
|
93
|
+
text-shadow: 0px 1px 1px #ffffff;
|
94
|
+
filter: dropshadow(color=#ffffff, offx=0, offy=1);
|
95
|
+
background: #eeeeee; /* Old browsers */
|
96
|
+
background: -moz-linear-gradient(top, #eeeeee 0%, #cccccc 100%); /* FF3.6+ */
|
97
|
+
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#eeeeee), color-stop(100%,#cccccc)); /* Chrome,Safari4+ */
|
98
|
+
background: -webkit-linear-gradient(top, #eeeeee 0%,#cccccc 100%); /* Chrome10+,Safari5.1+ */
|
99
|
+
background: -o-linear-gradient(top, #eeeeee 0%,#cccccc 100%); /* Opera11.10+ */
|
100
|
+
background: -ms-linear-gradient(top, #eeeeee 0%,#cccccc 100%); /* IE10+ */
|
101
|
+
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#cccccc',GradientType=0 ); /* IE6-9 */
|
102
|
+
background: linear-gradient(top, #eeeeee 0%,#cccccc 100%); /* W3C */
|
103
|
+
}
|
104
|
+
|
105
|
+
li, h2, h1 {
|
106
|
+
border: 1px solid #989898;
|
107
|
+
}
|
108
|
+
|
109
|
+
li, h2 {
|
110
|
+
border-bottom-width: 0px;
|
111
|
+
}
|
112
|
+
|
113
|
+
li {
|
114
|
+
padding: 8px;
|
115
|
+
font-weight: bold;
|
116
|
+
}
|
117
|
+
|
118
|
+
li:last-child {
|
119
|
+
border-bottom-width: 1px;
|
120
|
+
}
|
121
|
+
|
122
|
+
li.playing {
|
123
|
+
border-color: #007cf9;
|
124
|
+
background: #7abcff; /* Old browsers */
|
125
|
+
background: -moz-linear-gradient(top, #7abcff 0%, #60abf8 44%, #4096ee 100%); /* FF3.6+ */
|
126
|
+
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#7abcff), color-stop(44%,#60abf8), color-stop(100%,#4096ee)); /* Chrome,Safari4+ */
|
127
|
+
background: -webkit-linear-gradient(top, #7abcff 0%,#60abf8 44%,#4096ee 100%); /* Chrome10+,Safari5.1+ */
|
128
|
+
background: -o-linear-gradient(top, #7abcff 0%,#60abf8 44%,#4096ee 100%); /* Opera11.10+ */
|
129
|
+
background: -ms-linear-gradient(top, #7abcff 0%,#60abf8 44%,#4096ee 100%); /* IE10+ */
|
130
|
+
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#7abcff', endColorstr='#4096ee',GradientType=0 ); /* IE6-9 */
|
131
|
+
background: linear-gradient(top, #7abcff 0%,#60abf8 44%,#4096ee 100%); /* W3C */
|
132
|
+
color: #fff;
|
133
|
+
text-shadow: 0px -1px 1px #777777;
|
134
|
+
filter: dropshadow(color=#777777, offx=0, offy=-1);
|
135
|
+
}
|
136
|
+
|
137
|
+
li.playing+li {
|
138
|
+
border-top-color: #007cf9;
|
139
|
+
}
|
140
|
+
|
141
|
+
|
@@ -0,0 +1,79 @@
|
|
1
|
+
|
2
|
+
/* ==== Scroll down to find where to put your styles :) ==== */
|
3
|
+
|
4
|
+
/* HTML5 ✰ Boilerplate */
|
5
|
+
|
6
|
+
html, body, div, span, object, iframe,
|
7
|
+
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
8
|
+
abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp,
|
9
|
+
small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li,
|
10
|
+
fieldset, form, label, legend,
|
11
|
+
table, caption, tbody, tfoot, thead, tr, th, td,
|
12
|
+
article, aside, canvas, details, figcaption, figure,
|
13
|
+
footer, header, hgroup, menu, nav, section, summary,
|
14
|
+
time, mark, audio, video {
|
15
|
+
margin: 0;
|
16
|
+
padding: 0;
|
17
|
+
border: 0;
|
18
|
+
font-size: 100%;
|
19
|
+
font: inherit;
|
20
|
+
vertical-align: baseline;
|
21
|
+
}
|
22
|
+
|
23
|
+
article, aside, details, figcaption, figure,
|
24
|
+
footer, header, hgroup, menu, nav, section {
|
25
|
+
display: block;
|
26
|
+
}
|
27
|
+
|
28
|
+
blockquote, q { quotes: none; }
|
29
|
+
blockquote:before, blockquote:after,
|
30
|
+
q:before, q:after { content: ''; content: none; }
|
31
|
+
ins { background-color: #ff9; color: #000; text-decoration: none; }
|
32
|
+
mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }
|
33
|
+
del { text-decoration: line-through; }
|
34
|
+
abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; }
|
35
|
+
table { border-collapse: collapse; border-spacing: 0; }
|
36
|
+
hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
|
37
|
+
input, select { vertical-align: middle; }
|
38
|
+
|
39
|
+
body { font:13px/1.231 sans-serif; *font-size:small; }
|
40
|
+
select, input, textarea, button { font:99% sans-serif; }
|
41
|
+
pre, code, kbd, samp { font-family: monospace, sans-serif; }
|
42
|
+
|
43
|
+
html { overflow-y: scroll; }
|
44
|
+
a:hover, a:active { outline: none; }
|
45
|
+
ul, ol { margin-left: 2em; }
|
46
|
+
ol { list-style-type: decimal; }
|
47
|
+
nav ul, nav li { margin: 0; list-style:none; list-style-image: none; }
|
48
|
+
small { font-size: 85%; }
|
49
|
+
strong, th { font-weight: bold; }
|
50
|
+
td { vertical-align: top; }
|
51
|
+
|
52
|
+
sub, sup { font-size: 75%; line-height: 0; position: relative; }
|
53
|
+
sup { top: -0.5em; }
|
54
|
+
sub { bottom: -0.25em; }
|
55
|
+
|
56
|
+
pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; padding: 15px; }
|
57
|
+
textarea { overflow: auto; }
|
58
|
+
.ie6 legend, .ie7 legend { margin-left: -7px; }
|
59
|
+
input[type="radio"] { vertical-align: text-bottom; }
|
60
|
+
input[type="checkbox"] { vertical-align: bottom; }
|
61
|
+
.ie7 input[type="checkbox"] { vertical-align: baseline; }
|
62
|
+
.ie6 input { vertical-align: text-bottom; }
|
63
|
+
label, input[type="button"], input[type="submit"], input[type="image"], button { cursor: pointer; }
|
64
|
+
button, input, select, textarea { margin: 0; }
|
65
|
+
input:valid, textarea:valid { }
|
66
|
+
input:invalid, textarea:invalid { border-radius: 1px; -moz-box-shadow: 0px 0px 5px red; -webkit-box-shadow: 0px 0px 5px red; box-shadow: 0px 0px 5px red; }
|
67
|
+
.no-boxshadow input:invalid, .no-boxshadow textarea:invalid { background-color: #f0dddd; }
|
68
|
+
|
69
|
+
::-moz-selection{ background: #FF5E99; color:#fff; text-shadow: none; }
|
70
|
+
::selection { background:#FF5E99; color:#fff; text-shadow: none; }
|
71
|
+
a:link { -webkit-tap-highlight-color: #FF5E99; }
|
72
|
+
|
73
|
+
button { width: auto; overflow: visible; }
|
74
|
+
.ie7 img { -ms-interpolation-mode: bicubic; }
|
75
|
+
|
76
|
+
body, select, input, textarea { color: #444; }
|
77
|
+
h1, h2, h3, h4, h5, h6 { font-weight: bold; }
|
78
|
+
a, a:active, a:visited { color: #607890; }
|
79
|
+
a:hover { color: #036; }
|