murlsh 0.2.1

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.
@@ -0,0 +1,23 @@
1
+ module Murlsh
2
+
3
+ class Plugin
4
+
5
+ def self.inherited(child)
6
+ registered << child
7
+ end
8
+
9
+ def self.hooks(name)
10
+ matches = registered.select { |p| p::Hook == name }
11
+
12
+ if block_given?
13
+ matches.each { |p| yield p }
14
+ end
15
+ matches
16
+ end
17
+
18
+ @registered = []
19
+ class << self; attr_reader :registered end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,47 @@
1
+ require 'cgi'
2
+ require 'uri'
3
+
4
+ module Murlsh
5
+
6
+ class Referrer
7
+
8
+ def initialize(url)
9
+ @url = url
10
+
11
+ begin
12
+ url_parsed = URI(url)
13
+ @hostpath = url_parsed.host + url_parsed.path
14
+ @query_string =
15
+ url_parsed.query.nil? ? {} : CGI::parse(url_parsed.query)
16
+ rescue Exception => e
17
+ @hostpath = ''
18
+ @query_string = {}
19
+ end
20
+ end
21
+
22
+ def search_query(qmap=Qmap)
23
+ if hostpath and query_string
24
+ qmap.each_pair do |r,v|
25
+ if hostpath[r] and !query_string[v].empty?
26
+ if block_given?
27
+ yield query_string[v].first
28
+ else
29
+ return query_string[v].first
30
+ end
31
+ end
32
+ end
33
+ end
34
+ nil
35
+ end
36
+
37
+ Qmap = {
38
+ /^www\.google\.(bs|ca|com|cz|dk|es|fi|nl)(\/m)?\/search$/ => 'q',
39
+ /^www\.bing\.com\/search$/ => 'q',
40
+ }
41
+
42
+ attr_accessor :url
43
+ attr_reader :hostpath
44
+ attr_reader :query_string
45
+ end
46
+
47
+ end
@@ -0,0 +1,12 @@
1
+ require 'active_record/connection_adapters/sqlite3_adapter'
2
+
3
+ class ActiveRecord::ConnectionAdapters::SQLite3Adapter
4
+
5
+ def initialize(connection, logger, config)
6
+ super
7
+ @connection.create_function('MATCH', 2) do |func,search_in,search_for|
8
+ func.result = search_in.to_s.match(/#{search_for}/i) ? 1 : nil
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,18 @@
1
+ require 'time'
2
+
3
+ class Time
4
+
5
+ def fuzzy
6
+ days_ago = (Time.now.to_i - to_i) / 86400
7
+
8
+ case days_ago
9
+ when 0 then 'today'
10
+ when 1 then 'yesterday'
11
+ when (2..4) then "#{days_ago} days ago"
12
+ when (5..7) then strftime('%a %e %b')
13
+ when (8..180) then strftime('%e %b').strip
14
+ else strftime('%e %b %Y').strip
15
+ end
16
+ end
17
+
18
+ end
data/lib/murlsh/url.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+
4
+ require 'uri'
5
+
6
+ module Murlsh
7
+
8
+ class Url < ActiveRecord::Base
9
+
10
+ def title
11
+ read_attribute(:title) || read_attribute(:url) || 'title missing'
12
+ end
13
+
14
+ def same_author?(other)
15
+ other and other.email and other.name and
16
+ email and name and email == other.email and name == other.name
17
+ end
18
+
19
+ Widely_known = %w{
20
+ wikipedia.org
21
+ flickr.com
22
+ github.com
23
+ twitter.com
24
+ vimeo.com
25
+ youtube.com
26
+ }
27
+
28
+ def hostrec
29
+ begin
30
+ domain = URI(url).host[/[a-z\d-]+\.[a-z]{2,}(\.[a-z]{2})?$/].downcase
31
+ rescue Exception => e
32
+ domain = nil
33
+ end
34
+ yield domain unless !domain or title.downcase.index(domain) or
35
+ Widely_known.include?(domain)
36
+ end
37
+
38
+ def is_image?
39
+ %w{image/gif image/jpeg image/png}.include?(content_type)
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,155 @@
1
+ module Murlsh
2
+
3
+ class UrlBody < Builder::XmlMarkup
4
+ include Murlsh::Markup
5
+
6
+ def initialize(config, db, req)
7
+ @config, @db, @req, @q = config, db, req, req.params['q']
8
+ super(:indent => 2)
9
+ end
10
+
11
+ def urls
12
+ Murlsh::Url.all(:conditions => search_conditions, :order => 'id DESC',
13
+ :limit => @req.params['n'] ? @req.params['n'].to_i :
14
+ @config.fetch('num_posts_page', 100))
15
+ end
16
+
17
+ def search_conditions
18
+ if @q
19
+ search_cols = %w{name title url}
20
+ [search_cols.collect { |x| "MATCH(#{x}, ?)" }.join(' OR ')].push(
21
+ *[@q] * search_cols.size)
22
+ else
23
+ []
24
+ end
25
+ end
26
+
27
+ def each
28
+ instruct! :xml
29
+ declare! :DOCTYPE, :html, :PUBLIC, '-//W3C//DTD XHTML 1.1//EN',
30
+ 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
31
+
32
+ yield html(:xmlns => 'http://www.w3.org/1999/xhtml',
33
+ :'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
34
+ :'xsi:schemaLocation' => 'http://www.w3.org/MarkUp/SCHEMA/xhtml11.xsd',
35
+ :'xml:lang' => 'en') {
36
+ headd
37
+ body {
38
+ ul(:id => 'urls') {
39
+ li { feed_icon ; search_form }
40
+
41
+ gravatar_size = @config.fetch('gravatar_size', 0)
42
+
43
+ last = nil
44
+ urls.each do |mu|
45
+ li {
46
+ unless mu.same_author?(last)
47
+ div(:class => 'icon') {
48
+ gravatar(mu.email, 's' => gravatar_size, :text => mu.name)
49
+ } if mu.email and gravatar_size > 0
50
+ div(mu.name, :class => 'name') if mu.name
51
+ end
52
+
53
+ a(mu.title.strip.gsub(/\s+/, ' '), :href => mu.url)
54
+
55
+ mu.hostrec { |hostrec| span(hostrec, :class => 'host') }
56
+ span(", #{mu.time.fuzzy}", :class => 'date') if
57
+ @config.fetch('show_dates', true) and mu.time
58
+ last = mu
59
+ }
60
+ end
61
+
62
+ li { add_form }
63
+ }
64
+
65
+ clear
66
+ powered_by
67
+ js
68
+ }
69
+ }
70
+ end
71
+
72
+ def headd
73
+ head {
74
+ titlee
75
+ metas(:description => @config.fetch('description', ''),
76
+ :viewport =>
77
+ 'width=device-width,minimum-scale=1.0,maximum-scale=1.0')
78
+ google_verify
79
+ css(@config.fetch('css_files', []),
80
+ :prefix => @config.fetch('css_prefix', ''))
81
+ css('phone.css', :media => 'only screen and (max-device-width: 480px)',
82
+ :prefix => @config.fetch('css_prefix', ''))
83
+ atom(@config.fetch('feed_file'))
84
+ }
85
+ end
86
+
87
+ def titlee
88
+ title(@config.fetch('page_title', '') + (@q ? " /#{@q}" : ''))
89
+ end
90
+
91
+ def google_verify
92
+ (gv = @config.fetch('google_verify')) and metas('verify-v1' => gv)
93
+ end
94
+
95
+ def feed_icon
96
+ div(:class => 'icon') {
97
+ a('feed', :href => @config.fetch('feed_file'), :class => 'feed')
98
+ }
99
+ end
100
+
101
+ def search_form
102
+ form(:action => '', :method => 'get') {
103
+ value = @q
104
+ Murlsh::Referrer.new(@req.referrer).search_query do |refq|
105
+ re_parts = refq.split.collect { |x| Regexp.escape(x) }
106
+ value = "\\b(#{re_parts.join('|')})\\b"
107
+ end
108
+ fieldset {
109
+ input(:type => 'text', :id => 'q', :name => 'q', :size => 32,
110
+ :value => value)
111
+ input(:type => 'submit', :value=> 'Regex Search')
112
+ }
113
+ }
114
+ end
115
+
116
+ def add_form
117
+ form(:action => '', :method => 'post') {
118
+ fieldset(:id => 'add') {
119
+ self.p { add_form_input('Add URL:', 'url', 32) }
120
+ self.p {
121
+ add_form_input('Password:', 'auth', 16, 'password')
122
+ input(:type => 'button', :id => 'submit', :value => 'Add')
123
+ }
124
+ }
125
+ }
126
+ end
127
+
128
+ def add_form_input(label, id, size, tipe='text')
129
+ label(label, :for => id)
130
+ input(:type => tipe, :id => id, :name => id, :size => size)
131
+ end
132
+
133
+ def clear
134
+ div(:style => 'clear : both')
135
+ end
136
+
137
+ def powered_by
138
+ self.p {
139
+ text! 'powered by '
140
+ a('murlsh', :href => 'http://github.com/mmb/murlsh/')
141
+ }
142
+ end
143
+
144
+ def js
145
+ javascript(%w{
146
+ jquery-1.3.2.min.js
147
+ jquery.cookie.js
148
+ jquery.jgrowl_compressed.js
149
+ js.js
150
+ }, :prefix => @config.fetch('js_prefix', ''))
151
+ end
152
+
153
+ end
154
+
155
+ end
@@ -0,0 +1,82 @@
1
+ %w{
2
+ rubygems
3
+ active_record
4
+ rack
5
+ }.each { |m| require m }
6
+
7
+ module Murlsh
8
+
9
+ class UrlServer
10
+
11
+ def initialize(config, db)
12
+ @config = config
13
+ @db = db
14
+ ActiveRecord::Base.default_timezone = :utc
15
+
16
+ Dir['plugins/*.rb'].each { |p| load p }
17
+ end
18
+
19
+ def get(req)
20
+ resp = Murlsh::XhtmlResponse.new
21
+
22
+ resp.set_content_type(req.env['HTTP_ACCEPT'], req.env['HTTP_USER_AGENT'])
23
+
24
+ last_db_update = File::Stat.new(@config['db_file']).mtime
25
+ resp['Last-Modified'] = last_db_update.httpdate
26
+ resp['ETag'] = "#{last_db_update.to_i}#{req.params.sort}"
27
+
28
+ resp.body = Murlsh::UrlBody.new(@config, @db, req)
29
+
30
+ resp
31
+ end
32
+
33
+ def post(req)
34
+ resp = Rack::Response.new
35
+
36
+ url = req.params['url']
37
+
38
+ unless url.empty?
39
+ auth = req.params['auth']
40
+ if user = auth.empty? ? nil : Murlsh::Auth.new(
41
+ @config.fetch('auth_file')).auth(auth)
42
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3',
43
+ :database => @config.fetch('db_file'))
44
+
45
+ content_type = Murlsh.get_content_type(url)
46
+ mu = Murlsh::Url.new do |u|
47
+ u.time = Time.now.gmtime
48
+ u.url = url
49
+ u.email = user[:email]
50
+ u.name = user[:name]
51
+ u.title = Murlsh.get_title(url, :content_type => content_type)
52
+ u.content_type = content_type
53
+ end
54
+
55
+ mu.save
56
+
57
+ Murlsh::Plugin.hooks('add_post') { |p| p.run(@config) }
58
+
59
+ resp['Content-Type'] = 'application/json'
60
+
61
+ resp.set_cookie('auth',
62
+ :expires => Time.mktime(2015, 6, 22),
63
+ :path => '/',
64
+ :value => auth)
65
+
66
+ resp.body = [mu].to_json
67
+ else
68
+ resp.status = 403
69
+ resp['Content-Type'] = 'text/plain'
70
+ resp.write('Permission denied')
71
+ end
72
+ else
73
+ resp.status = 500
74
+ resp['Content-Type'] = 'text/plain'
75
+ resp.write('No url')
76
+ end
77
+ resp
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,19 @@
1
+ module Murlsh
2
+
3
+ class XhtmlResponse < Rack::Response
4
+
5
+ # set the content type to application/xhtml+xml for anything that
6
+ # claims to accept it, for anything else or IE use text/html
7
+ def set_content_type(http_accept, http_user_agent)
8
+ self['Content-Type'] = if http_accept and
9
+ http_accept[/((\*|application)\/\*|application\/xhtml\+xml)/i] and
10
+ (!http_user_agent or !http_user_agent[/ msie /i])
11
+ 'application/xhtml+xml'
12
+ else
13
+ 'text/html'
14
+ end
15
+ end
16
+
17
+ end
18
+
19
+ end
data/lib/murlsh.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'murlsh/auth'
2
+ require 'murlsh/dispatch'
3
+ require 'murlsh/get_content_type'
4
+ require 'murlsh/get_title'
5
+ require 'murlsh/plugin'
6
+ require 'murlsh/referrer'
7
+ require 'murlsh/sqlite3_adapter'
8
+ require 'murlsh/time'
9
+ require 'murlsh/url_server'
10
+ require 'murlsh/url'
11
+ require 'murlsh/xhtml_response'
12
+
13
+ # requiring builder before active_record blows up
14
+ require 'murlsh/atom_feed'
15
+ require 'murlsh/markup'
16
+ require 'murlsh/url_body'
data/murlsh.gemspec ADDED
@@ -0,0 +1,109 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{murlsh}
8
+ s.version = "0.2.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Matthew M. Boedicker"]
12
+ s.date = %q{2009-10-23}
13
+ s.default_executable = %q{murlsh}
14
+ s.description = %q{url sharing site framework with easy adding, title lookup, atom feed, thumbnails and embedding}
15
+ s.email = %q{matthewm@boedicker.org}
16
+ s.executables = ["murlsh"]
17
+ s.extra_rdoc_files = [
18
+ "README.textile"
19
+ ]
20
+ s.files = [
21
+ ".gitignore",
22
+ ".htaccess",
23
+ "COPYING",
24
+ "README.textile",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "bin/murlsh",
28
+ "config.ru",
29
+ "config.yaml",
30
+ "lib/murlsh.rb",
31
+ "lib/murlsh/atom_feed.rb",
32
+ "lib/murlsh/auth.rb",
33
+ "lib/murlsh/dispatch.rb",
34
+ "lib/murlsh/get_content_type.rb",
35
+ "lib/murlsh/get_title.rb",
36
+ "lib/murlsh/markup.rb",
37
+ "lib/murlsh/plugin.rb",
38
+ "lib/murlsh/referrer.rb",
39
+ "lib/murlsh/sqlite3_adapter.rb",
40
+ "lib/murlsh/time.rb",
41
+ "lib/murlsh/url.rb",
42
+ "lib/murlsh/url_body.rb",
43
+ "lib/murlsh/url_server.rb",
44
+ "lib/murlsh/xhtml_response.rb",
45
+ "murlsh.gemspec",
46
+ "plugins/update_feed.rb",
47
+ "public/css/jquery.jgrowl.css",
48
+ "public/css/phone.css",
49
+ "public/css/screen.css",
50
+ "public/js/jquery-1.3.2.min.js",
51
+ "public/js/jquery.cookie.js",
52
+ "public/js/jquery.jgrowl_compressed.js",
53
+ "public/js/js.js",
54
+ "public/swf/player_mp3_mini.swf",
55
+ "test/auth_test.rb",
56
+ "test/get_charset_test.rb",
57
+ "test/get_content_type_test.rb",
58
+ "test/get_title_test.rb",
59
+ "test/markup_test.rb",
60
+ "test/referrer_test.rb",
61
+ "test/xhtml_response_test.rb"
62
+ ]
63
+ s.homepage = %q{http://github.com/mmb/murlsh}
64
+ s.rdoc_options = ["--charset=UTF-8"]
65
+ s.require_paths = ["lib"]
66
+ s.rubygems_version = %q{1.3.5}
67
+ s.summary = %q{url sharing site framework}
68
+ s.test_files = [
69
+ "test/xhtml_response_test.rb",
70
+ "test/markup_test.rb",
71
+ "test/referrer_test.rb",
72
+ "test/get_charset_test.rb",
73
+ "test/get_content_type_test.rb",
74
+ "test/auth_test.rb",
75
+ "test/get_title_test.rb"
76
+ ]
77
+
78
+ if s.respond_to? :specification_version then
79
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
80
+ s.specification_version = 3
81
+
82
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
83
+ s.add_runtime_dependency(%q<activerecord>, [">= 2.3.4"])
84
+ s.add_runtime_dependency(%q<bcrypt>, [">= 2.1.2"])
85
+ s.add_runtime_dependency(%q<builder>, [">= 2.1.2"])
86
+ s.add_runtime_dependency(%q<hpricot>, [">= 0.8.1"])
87
+ s.add_runtime_dependency(%q<htmlentities>, [">= 4.2.0"])
88
+ s.add_runtime_dependency(%q<rack>, [">= 1.0.1"])
89
+ s.add_runtime_dependency(%q<sqlite3>, [">= 1.2.5"])
90
+ else
91
+ s.add_dependency(%q<activerecord>, [">= 2.3.4"])
92
+ s.add_dependency(%q<bcrypt>, [">= 2.1.2"])
93
+ s.add_dependency(%q<builder>, [">= 2.1.2"])
94
+ s.add_dependency(%q<hpricot>, [">= 0.8.1"])
95
+ s.add_dependency(%q<htmlentities>, [">= 4.2.0"])
96
+ s.add_dependency(%q<rack>, [">= 1.0.1"])
97
+ s.add_dependency(%q<sqlite3>, [">= 1.2.5"])
98
+ end
99
+ else
100
+ s.add_dependency(%q<activerecord>, [">= 2.3.4"])
101
+ s.add_dependency(%q<bcrypt>, [">= 2.1.2"])
102
+ s.add_dependency(%q<builder>, [">= 2.1.2"])
103
+ s.add_dependency(%q<hpricot>, [">= 0.8.1"])
104
+ s.add_dependency(%q<htmlentities>, [">= 4.2.0"])
105
+ s.add_dependency(%q<rack>, [">= 1.0.1"])
106
+ s.add_dependency(%q<sqlite3>, [">= 1.2.5"])
107
+ end
108
+ end
109
+
@@ -0,0 +1,23 @@
1
+ require 'murlsh'
2
+
3
+ module Murlsh
4
+
5
+ # regenerate atom feed after a new url has been added
6
+ class UpdateFeed < Plugin
7
+
8
+ Hook = 'add_post'
9
+
10
+ def self.run(config)
11
+ latest = Murlsh::Url.all(:order => 'id DESC',
12
+ :limit => config.fetch('num_posts_feed', 25))
13
+
14
+ feed = Murlsh::AtomFeed.new(config.fetch('root_url'),
15
+ :filename => config.fetch('feed_file'),
16
+ :title => config.fetch('page_title', ''))
17
+
18
+ feed.write(latest, config.fetch('feed_file'))
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,127 @@
1
+
2
+ div.jGrowl {
3
+ padding: 10px;
4
+ z-index: 9999;
5
+ }
6
+
7
+ /** Special IE6 Style Positioning **/
8
+ div.ie6 {
9
+ position: absolute;
10
+ }
11
+
12
+ div.ie6.top-right {
13
+ right: auto;
14
+ bottom: auto;
15
+ left: expression( ( 0 - jGrowl.offsetWidth + ( document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth ) + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
16
+ top: expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
17
+ }
18
+
19
+ div.ie6.top-left {
20
+ left: expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
21
+ top: expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
22
+ }
23
+
24
+ div.ie6.bottom-right {
25
+ left: expression( ( 0 - jGrowl.offsetWidth + ( document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth ) + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
26
+ top: expression( ( 0 - jGrowl.offsetHeight + ( document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
27
+ }
28
+
29
+ div.ie6.bottom-left {
30
+ left: expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
31
+ top: expression( ( 0 - jGrowl.offsetHeight + ( document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
32
+ }
33
+
34
+ div.ie6.center {
35
+ left: expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
36
+ top: expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
37
+ width: 100%;
38
+ }
39
+
40
+ /** Normal Style Positions **/
41
+ body > div.jGrowl {
42
+ position: fixed;
43
+ }
44
+
45
+ body > div.jGrowl.top-left {
46
+ left: 0px;
47
+ top: 0px;
48
+ }
49
+
50
+ body > div.jGrowl.top-right {
51
+ right: 0px;
52
+ top: 0px;
53
+ }
54
+
55
+ body > div.jGrowl.bottom-left {
56
+ left: 0px;
57
+ bottom: 0px;
58
+ }
59
+
60
+ body > div.jGrowl.bottom-right {
61
+ right: 0px;
62
+ bottom: 0px;
63
+ }
64
+
65
+ body > div.jGrowl.center {
66
+ top: 0px;
67
+ width: 50%;
68
+ left: 25%;
69
+ }
70
+
71
+ /** Cross Browser Styling **/
72
+ div.center div.jGrowl-notification, div.center div.jGrowl-closer {
73
+ margin-left: auto;
74
+ margin-right: auto;
75
+ }
76
+
77
+ div.jGrowl div.jGrowl-notification, div.jGrowl div.jGrowl-closer {
78
+ background-color: #000;
79
+ color: #fff;
80
+ opacity: .85;
81
+ filter: alpha(opacity = 85);
82
+ zoom: 1;
83
+ width: 235px;
84
+ padding: 10px;
85
+ margin-top: 5px;
86
+ margin-bottom: 5px;
87
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
88
+ font-size: 12px;
89
+ text-align: left;
90
+ display: none;
91
+ -moz-border-radius: 5px;
92
+ -webkit-border-radius: 5px;
93
+ }
94
+
95
+ div.jGrowl div.jGrowl-notification {
96
+ min-height: 40px;
97
+ }
98
+
99
+ div.jGrowl div.jGrowl-notification div.header {
100
+ font-weight: bold;
101
+ font-size: 10px;
102
+ }
103
+
104
+ div.jGrowl div.jGrowl-notification div.close {
105
+ z-index: 99;
106
+ float: right;
107
+ font-weight: bold;
108
+ font-size: 12px;
109
+ cursor: pointer;
110
+ }
111
+
112
+ div.jGrowl div.jGrowl-closer {
113
+ height: 15px;
114
+ padding-top: 4px;
115
+ padding-bottom: 4px;
116
+ cursor: pointer;
117
+ font-size: 11px;
118
+ font-weight: bold;
119
+ text-align: center;
120
+ }
121
+
122
+ /** Hide jGrowl when printing **/
123
+ @media print {
124
+ div.jGrowl {
125
+ display: none;
126
+ }
127
+ }
@@ -0,0 +1,14 @@
1
+ body {
2
+ margin : 0;
3
+ padding : 0;
4
+ }
5
+
6
+ #urls {
7
+ margin : 0;
8
+ padding-left : 0;
9
+ width : 290px;
10
+ }
11
+
12
+ input#q {
13
+ width : 130px;
14
+ }