robut 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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; }
|