murlsh 0.9.0 → 0.10.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/README.textile CHANGED
@@ -8,7 +8,7 @@ Site for sharing and archiving links.
8
8
  * embeds Flash mp3 player for mp3 urls
9
9
  * looks good on iPhone
10
10
  * PubSubHubbub notification
11
- * plug-in interface
11
+ * plugin interface
12
12
  * rack interface
13
13
  * Gravatar support
14
14
 
@@ -22,6 +22,8 @@ h1. Installation
22
22
 
23
23
  h2. Phusion Passenger
24
24
 
25
+ DESTINATION_DIRECTORY is the web directory to install the murlsh site to.
26
+
25
27
  <pre>
26
28
  <code>
27
29
  gem sources -a http://gemcutter.org/
@@ -33,6 +35,36 @@ rake init
33
35
  </code>
34
36
  </pre>
35
37
 
38
+ h1. Updating
39
+
40
+ If you are using the gem and it gets updated to a new version you should run
41
+ the murlsh command again from your web directory to update plugins, javascript
42
+ and css. It will prompt before overwriting anything in case you have made
43
+ modifications.
44
+
45
+ h1. Plugins
46
+
47
+ Classes in the plugins directory can be used to change behavior at certain
48
+ points. Each class that extends Murlsh::Plugin and sets an instance variable
49
+ called @hook will be called for that hook. Each plugin has a run() method that
50
+ accepts arguments and returns something. These methods will be called in the
51
+ order of their class names sorted lexically. Some hooks pass the output of their
52
+ run() method to the next plugin for that hook so that the data can be passed
53
+ through a chain of methods that each do something to it.
54
+
55
+ A lot of the standard behavior is implemented as plugins. See the plugins
56
+ directory for examples.
57
+
58
+ Plugin hooks
59
+
60
+ |Hook|Description|run() arguments|Returns|
61
+ |add_pre|called before a new url is saved|url, config hash|undefined|
62
+ |add_post|called after a new url is saved|config hash|undefined|
63
+ |hostrec|post process the domain that is shown after links|domain, url, title|text to display|
64
+ |html_parse|parse HTML using something like Hpricot or Nokogiri|parseable|parsed HTML, only first plugin is run (cannot be chained)|
65
+ |time|convert the time of a post into a string for display|time|time display text|
66
+ |via|convert a via url into a string for display|via url|via url display text|
67
+
36
68
  h1. PubSubHubbub
37
69
 
38
70
  Murlsh can notify "PubSubHubbub":http://code.google.com/p/pubsubhubbub/ hubs
@@ -52,11 +84,4 @@ subscribe_url is what gets put in the feed as link rel="hub"
52
84
 
53
85
  This will make updates to your feed show up in Google Reader instantly.
54
86
 
55
- h1. Updating
56
-
57
- If you are using the gem and it gets updated to a new version you should run
58
- the murlsh command again from your web directory to update plugins, javascript
59
- and css. It will prompt before overwriting anything in case you have made
60
- modifications.
61
-
62
87
  Questions and comments: "matthewm@boedicker.org":mailto:matthewm@boedicker.org
data/Rakefile CHANGED
@@ -154,23 +154,47 @@ namespace :user do
154
154
 
155
155
  end
156
156
 
157
- desc "Validate XHTML."
158
- task :validate do
159
- net_http = Net::HTTP.new('validator.w3.org', 80)
160
- #net_http.set_debug_output(STDOUT)
161
-
162
- check_url = config.fetch('root_url')
163
-
164
- print "validating #{check_url} : "
157
+ # Validate a document with the W3C validation service.
158
+ def validate(check_url, options={})
159
+ opts = {
160
+ :validator_host => 'validator.w3.org',
161
+ :validator_port => 80,
162
+ :validator_path =>
163
+ "/check?uri=#{CGI::escape(check_url)}&charset=(detect+automatically)&doctype=Inline&group=0",
164
+ }.merge(options)
165
+
166
+ net_http = Net::HTTP.new(opts[:validator_host], opts[:validator_port])
167
+ # net_http.set_debug_output(STDOUT)
165
168
 
166
169
  net_http.start do |http|
167
- resp = http.request_head(
168
- "/check?uri=#{CGI::escape(check_url)}&charset=(detect+automatically)&doctype=Inline&group=0")
169
- result = resp['X-W3C-Validator-Status']
170
- errors = resp['X-W3C-Validator-Errors']
171
- warnings = resp['X-W3C-Validator-Warnings']
170
+ resp = http.request_head(opts[:validator_path])
171
+ result = {
172
+ :response => resp
173
+ }
174
+ if Net::HTTPSuccess === resp
175
+ result.merge!(
176
+ :status => resp['X-W3C-Validator-Status'],
177
+ :errors => resp['X-W3C-Validator-Errors'],
178
+ :warnings => resp['X-W3C-Validator-Warnings']
179
+ )
180
+ end
181
+ result
182
+ end
172
183
 
173
- puts "#{result} (#{errors} errors, #{warnings} warnings)"
184
+ end
185
+
186
+ namespace :validate do
187
+
188
+ desc 'Validate XHTML.'
189
+ task :xhtml do
190
+ check_url = config['root_url']
191
+ print "validating #{check_url} : "
192
+ result = validate(check_url)
193
+ if Net::HTTPSuccess === result[:response]
194
+ puts "#{result[:status]} (#{result[:errors]} errors, #{result[:warnings]} warnings)"
195
+ else
196
+ puts result[:response]
197
+ end
174
198
  end
175
199
 
176
200
  end
@@ -294,6 +318,9 @@ begin
294
318
  gemspec.authors = ['Matthew M. Boedicker']
295
319
  gemspec.executables = %w{murlsh}
296
320
 
321
+ # gemspec.signing_key = '/home/mmb/src/keys/gem-private_key.pem'
322
+ # gemspec.cert_chain = %w{/home/mmb/src/keys/gem-public_cert.pem}
323
+
297
324
  %w{
298
325
  activerecord 2.3.4
299
326
  bcrypt-ruby 2.1.2
@@ -301,6 +328,7 @@ begin
301
328
  hpricot 0.8.1
302
329
  htmlentities 4.2.0
303
330
  json 1.2.3
331
+ push-notify 0.1.0
304
332
  rack 1.0.0
305
333
  rack-cache 0.5.2
306
334
  rack-throttle 0.3.0
@@ -310,5 +338,5 @@ begin
310
338
 
311
339
  end
312
340
  rescue LoadError
313
- puts 'Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com'
341
+ puts "Jeweler not available. Install it with: gem install jeweler"
314
342
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.0
1
+ 0.10.0
data/config.yaml CHANGED
@@ -1,7 +1,11 @@
1
1
  ---
2
+ apple_icon_hosts: []
3
+
2
4
  auth_file: murlsh_users
3
5
  cache_entitystore: file:tmp/cache/rack/body
4
6
  cache_metastore: file:tmp/cache/rack/meta
7
+ config_js:
8
+ - apple_icon_hosts
5
9
  css_files:
6
10
  - css/jquery.jgrowl.css
7
11
  - css/screen.css
@@ -0,0 +1,32 @@
1
+ %w{
2
+ digest/sha1
3
+
4
+ json
5
+ rack
6
+ }.each { |m| require m }
7
+
8
+ module Murlsh
9
+
10
+ # Serve a JSON subset of the configuration.
11
+ #
12
+ # Will include all config keys contained in the config key named config_js.
13
+ class ConfigServer
14
+
15
+ def initialize(config)
16
+ @config_json =
17
+ config.reject { |k,v| !config.fetch('config_js', []).
18
+ include?(k) }.to_json
19
+
20
+ @headers = {
21
+ 'Content-Type' => 'application/json',
22
+ 'ETag' => "\"#{Digest::SHA1.hexdigest(@config_json)}\"",
23
+ 'Last-Modified' => Time.now.httpdate
24
+ }
25
+ end
26
+
27
+ # Serve a JSON subset of the configuration.
28
+ def get(req); Rack::Response.new(@config_json, 200, @headers); end
29
+
30
+ end
31
+
32
+ end
@@ -20,6 +20,7 @@ module Murlsh
20
20
  db = ActiveRecord::Base.connection.instance_variable_get(:@connection)
21
21
 
22
22
  url_server = Murlsh::UrlServer.new(@config, db)
23
+ config_server = Murlsh::ConfigServer.new(@config)
23
24
  flickr_server = Murlsh::FlickrServer.new(@config)
24
25
  twitter_server = Murlsh::TwitterServer.new
25
26
 
@@ -28,8 +29,9 @@ module Murlsh
28
29
  @dispatch = [
29
30
  [%r{^GET #{root_path}(url)?$}, url_server.method(:get)],
30
31
  [%r{^POST #{root_path}(url)?$}, url_server.method(:post)],
31
- [%r{^GET /flickr$}, flickr_server.method(:get)],
32
- [%r{^GET /twitter/.+$}, twitter_server.method(:get)],
32
+ [%r{^GET #{root_path}config$}, config_server.method(:get)],
33
+ [%r{^GET #{root_path}flickr$}, flickr_server.method(:get)],
34
+ [%r{^GET #{root_path}twitter/.+$}, twitter_server.method(:get)],
33
35
  ]
34
36
  end
35
37
 
data/murlsh.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{murlsh}
8
- s.version = "0.9.0"
8
+ s.version = "0.10.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Matthew M. Boedicker"]
12
- s.date = %q{2010-06-03}
12
+ s.date = %q{2010-08-05}
13
13
  s.default_executable = %q{murlsh}
14
14
  s.description = %q{url sharing site framework with easy adding, title lookup, atom feed, thumbnails and embedding}
15
15
  s.email = %q{matthewm@boedicker.org}
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
29
29
  "config.yaml",
30
30
  "lib/murlsh.rb",
31
31
  "lib/murlsh/auth.rb",
32
+ "lib/murlsh/config_server.rb",
32
33
  "lib/murlsh/dispatch.rb",
33
34
  "lib/murlsh/doc.rb",
34
35
  "lib/murlsh/etag_add_encoding.rb",
@@ -63,6 +64,7 @@ Gem::Specification.new do |s|
63
64
  "plugins/via_50_domain.rb",
64
65
  "public/css/jquery.jgrowl.css",
65
66
  "public/css/screen.css",
67
+ "public/js/comments.json",
66
68
  "public/js/jquery-1.4.2.min.js",
67
69
  "public/js/jquery.jgrowl_compressed.js",
68
70
  "public/js/js.js",
@@ -107,6 +109,7 @@ Gem::Specification.new do |s|
107
109
  s.add_runtime_dependency(%q<hpricot>, [">= 0.8.1"])
108
110
  s.add_runtime_dependency(%q<htmlentities>, [">= 4.2.0"])
109
111
  s.add_runtime_dependency(%q<json>, [">= 1.2.3"])
112
+ s.add_runtime_dependency(%q<push-notify>, [">= 0.1.0"])
110
113
  s.add_runtime_dependency(%q<rack>, [">= 1.0.0"])
111
114
  s.add_runtime_dependency(%q<rack-cache>, [">= 0.5.2"])
112
115
  s.add_runtime_dependency(%q<rack-throttle>, [">= 0.3.0"])
@@ -119,6 +122,7 @@ Gem::Specification.new do |s|
119
122
  s.add_dependency(%q<hpricot>, [">= 0.8.1"])
120
123
  s.add_dependency(%q<htmlentities>, [">= 4.2.0"])
121
124
  s.add_dependency(%q<json>, [">= 1.2.3"])
125
+ s.add_dependency(%q<push-notify>, [">= 0.1.0"])
122
126
  s.add_dependency(%q<rack>, [">= 1.0.0"])
123
127
  s.add_dependency(%q<rack-cache>, [">= 0.5.2"])
124
128
  s.add_dependency(%q<rack-throttle>, [">= 0.3.0"])
@@ -132,6 +136,7 @@ Gem::Specification.new do |s|
132
136
  s.add_dependency(%q<hpricot>, [">= 0.8.1"])
133
137
  s.add_dependency(%q<htmlentities>, [">= 4.2.0"])
134
138
  s.add_dependency(%q<json>, [">= 1.2.3"])
139
+ s.add_dependency(%q<push-notify>, [">= 0.1.0"])
135
140
  s.add_dependency(%q<rack>, [">= 1.0.0"])
136
141
  s.add_dependency(%q<rack-cache>, [">= 0.5.2"])
137
142
  s.add_dependency(%q<rack-throttle>, [">= 0.3.0"])
@@ -13,23 +13,14 @@ module Murlsh
13
13
  hubs = config.fetch('pubsubhubbub_hubs', [])
14
14
 
15
15
  unless hubs.empty?
16
- require 'eventmachine'
17
- require 'pubsubhubbub'
16
+ require 'push-notify'
18
17
 
19
18
  feed_url = URI.join(config['root_url'], config['feed_file'])
20
-
21
- hubs.each do |hub|
22
- EventMachine.run {
23
- pub = EventMachine::PubSubHubbub.new(hub['publish_url']).publish(
24
- feed_url)
25
-
26
- pub.callback { EventMachine.stop }
27
- pub.errback { EventMachine.stop }
28
- }
19
+ begin
20
+ PushNotify::Content.new(feed_url).tell(*hubs.map { |h| h['publish_url'] })
21
+ rescue Exception
29
22
  end
30
-
31
23
  end
32
-
33
24
  end
34
25
 
35
26
  end
@@ -12,6 +12,10 @@ img {
12
12
  width : 600px;
13
13
  }
14
14
 
15
+ ul.comments {
16
+ list-style-type : none;
17
+ }
18
+
15
19
  li {
16
20
  clear : both;
17
21
  float : left;
@@ -49,9 +53,6 @@ div.name {
49
53
  img.thumb, li object {
50
54
  float : left;
51
55
  margin-right : 10px;
52
- border : 1px solid #808080;
53
- -moz-border-radius : 5px;
54
- -webkit-border-radius : 5px;
55
56
  }
56
57
 
57
58
  img.thumb.twitter {
@@ -59,6 +60,11 @@ img.thumb.twitter {
59
60
  width : 48px;
60
61
  }
61
62
 
63
+ img.thumb.apple {
64
+ height : 48px;
65
+ width : 48px;
66
+ }
67
+
62
68
  span.host {
63
69
  color : #808080;
64
70
  font-family : monospace;
@@ -88,7 +94,7 @@ fieldset#add label {
88
94
  }
89
95
 
90
96
  a.feed {
91
- background-color : #fb9e3a;
97
+ background : #fb9e3a;
92
98
  border-top : 1px solid #fba141;
93
99
  border-right : 1px solid #bc4d04;
94
100
  border-bottom : 1px solid #bc4d04;
@@ -129,7 +135,7 @@ div.jGrowl div.jGrowl-closer {
129
135
  width : 290px;
130
136
  }
131
137
 
132
- #urls li {
138
+ #urls>li {
133
139
  overflow : hidden;
134
140
  border-bottom : 1px solid #ccc;
135
141
  }
@@ -0,0 +1,12 @@
1
+ {
2
+ "http://url/being/commented/on": [
3
+ {
4
+ "authorAvatar": "http://url/of/your/avatar/image",
5
+ "authorName": "yourname",
6
+ "authorUrl": "http://url/of/your/site",
7
+ "comment": "your comment",
8
+ "createdTime": 1275005075,
9
+ "updatedTime": 1275005075
10
+ },
11
+ ],
12
+ }
data/public/js/js.js CHANGED
@@ -1,347 +1,434 @@
1
- /*global $, window*/
1
+ /*global $, document, navigator, window*/
2
2
 
3
3
  "use strict";
4
4
 
5
- var Murlsh = {};
6
-
7
- Murlsh.img = function(src, text) {
8
- text = text || '';
9
- return $('<img />', {
10
- src : src,
11
- alt : text,
12
- title : text
13
- });
14
- };
5
+ var Murlsh = function (config, $, navigator, window) {
6
+ var my = {},
7
+ hrefRes = {
8
+ flickr :
9
+ /^http:\/\/(?:www\.)?flickr\.com\/photos\/[@\w\-]+?\/([\d]+)/i,
10
+ imageshack :
11
+ /^(http:\/\/img\d+\.imageshack\.us\/img\d+\/\d+\/\w+\.)(jpe?g|gif|png)$/i,
12
+ imgur :
13
+ /^(http:\/\/(?:i\.)?imgur\.com\/[a-z\d]+)(\.(?:jpe?g|gif|png))$/i,
14
+ mp3 :
15
+ /\.mp3$/i,
16
+ s3 :
17
+ /^(http:\/\/static\.mmb\.s3\.amazonaws\.com\/[\w\-]+\.)(jpe?g|gif|pdf|png)$/i,
18
+ twitter :
19
+ /^https?:\/\/twitter\.com\/\w+\/status(?:es)?\/(\d+)$/i,
20
+ vimeo :
21
+ /^http:\/\/(?:www\.)?vimeo\.com\/(\d+)$/i,
22
+ youtube :
23
+ /^http:\/\/(?:(?:www|uk)\.)?youtube\.com\/watch\?v=([\w\-]+)(?:&|$)/i
24
+ },
25
+ hostRe = /^http:\/\/([a-z\d\.\-]+(?::\d+)?)\//i;
26
+
27
+ function autoLink(s) {
28
+ // turn urls into links
29
+ var result = s.replace(
30
+ /https?:\/\/(?:[0-9a-z](?:[0-9a-z\-]{0,61}[0-9a-z])?\.)+[a-z]+\/[0-9a-z$_.+!*'(),\/?#\-]*/gi,
31
+ '<a href="$&">$&</a>');
32
+
33
+ return result;
34
+ }
15
35
 
16
- Murlsh.makeFit = function(e, maxWidth, maxHeight) {
17
- var height = e.height();
18
- var scale;
19
- var width = e.width();
36
+ function escapeXml(s) {
37
+ return s.replace(/&/g, '&amp;');
38
+ }
20
39
 
21
- if (width > maxWidth || height > maxHeight) {
22
- scale = Math.min(maxWidth / width, maxHeight / height);
23
- e.width(Math.round(width * scale));
24
- e.height(Math.round(height * scale));
40
+ function img(src, text) {
41
+ text = text || '';
42
+ return $('<img />', {
43
+ src : src,
44
+ alt : text,
45
+ title : text
46
+ });
25
47
  }
26
- };
27
48
 
28
- Murlsh.closerAdd = function(x, header) {
29
- var html = (typeof x === 'object') ? $('<div />').append(x).html() : x;
30
-
31
- $.jGrowl(html, {
32
- closeTemplate : 'X',
33
- glue : 'before',
34
- header : header,
35
- sticky : true,
36
- beforeOpen : function(e) {
37
- e.find('.message img').load(function() {
38
- Murlsh.makeFit($(this),
39
- Math.round($(window).width() / 2),
40
- Math.round($(window).height() - 100));
41
- });
49
+ function makeFit(e, maxWidth, maxHeight) {
50
+ var height = e.height(),
51
+ scale,
52
+ width = e.width();
53
+
54
+ if (width > maxWidth || height > maxHeight) {
55
+ scale = Math.min(maxWidth / width, maxHeight / height);
56
+ e.width(Math.round(width * scale));
57
+ e.height(Math.round(height * scale));
42
58
  }
43
- });
44
- };
59
+ }
45
60
 
46
- Murlsh.escapeXml = function(s) {
47
- return s.replace(/&/g, '&amp;');
48
- };
61
+ function objectTag(data, height, width, params) {
62
+ // this does not use jQuery to build tags because building object
63
+ // tags is broken in IE
64
+ var result = '<object data="' + escapeXml(data) +
65
+ '" height="' + height +
66
+ '" type="application/x-shockwave-flash" width="' + width + '">';
49
67
 
50
- Murlsh.objectTag = function(data, height, width, params) {
51
- // this does not use jQuery to build tags because building object
52
- // tags is broken in IE
53
- var result = '<object data="' + Murlsh.escapeXml(data) +
54
- '" height="' + height +
55
- '" type="application/x-shockwave-flash" width="' + width + '">';
68
+ $.each(params, function (i, v) {
69
+ result += '<param name="' + v.name + '" value="' +
70
+ escapeXml(v.value) + '" />';
71
+ });
56
72
 
57
- $.each(params, function(i, v) {
58
- result += '<param name="' + v.name + '" value="' +
59
- Murlsh.escapeXml(v.value) + '" />';
60
- });
73
+ result += '</object>';
61
74
 
62
- result += '</object>';
75
+ return result;
76
+ }
63
77
 
64
- return result;
65
- };
78
+ function appleThumb(host) {
79
+ return img('http://' + host + '/apple-touch-icon.png', '').addClass(
80
+ 'thumb apple');
81
+ }
66
82
 
67
- Murlsh.flickrThumb = function(d) {
68
- var base;
69
- var owner;
70
- var photo = d.photo;
71
- var zoom;
83
+ function closerAdd(x, header) {
84
+ var html = (typeof x === 'object') ? $('<div />').append(x).html() : x;
85
+
86
+ $.jGrowl(html, {
87
+ closeTemplate : 'X',
88
+ glue : 'before',
89
+ header : header,
90
+ sticky : true,
91
+ beforeOpen : function (e) {
92
+ e.find('.message img').load(function () {
93
+ makeFit($(this), Math.round($(window).width() / 2),
94
+ Math.round($(window).height() - 100));
95
+ });
96
+ }
97
+ });
98
+ }
99
+
100
+ function flickrClick() {
101
+ closerAdd(img($(this).data('zoom')));
102
+ }
72
103
 
73
- if (d.stat === 'ok') {
74
- base = 'http://farm' + photo.farm + '.static.flickr.com/' +
75
- photo.server + '/' + photo.id + '_';
76
- zoom = base + photo.secret + '_m.jpg';
104
+ function flickrThumb(d) {
105
+ var base,
106
+ owner,
107
+ photo = d.photo,
108
+ zoom;
77
109
 
78
- if (photo.originalsecret) {
79
- zoom = base + photo.originalsecret + '_o.' + photo.originalformat;
110
+ if (d.stat === 'ok') {
111
+ base = 'http://farm' + photo.farm + '.static.flickr.com/' +
112
+ photo.server + '/' + photo.id + '_';
113
+ zoom = base + photo.secret + '_m.jpg';
114
+
115
+ if (photo.originalsecret) {
116
+ zoom = base + photo.originalsecret + '_o.' +
117
+ photo.originalformat;
118
+ }
119
+
120
+ owner = photo.owner;
121
+ return img(base + photo.secret + '_s.jpg', photo.title._content +
122
+ (owner && owner.username ? ' by ' + owner.username : '')
123
+ ).addClass('thumb flickr').data('zoom', zoom);
80
124
  }
125
+ }
81
126
 
82
- owner = photo.owner;
83
- return Murlsh.img(base + photo.secret + '_s.jpg',
84
- photo.title._content +
85
- (owner && owner.username ? ' by ' + owner.username : '')
86
- ).addClass('thumb flickr').data('zoom', zoom);
127
+ function imgClick() {
128
+ closerAdd(img($(this).data('href')));
87
129
  }
88
- };
89
130
 
90
- Murlsh.flickrClick = function() {
91
- Murlsh.closerAdd(Murlsh.img($(this).data('zoom')));
92
- };
131
+ function imgThumb() {
132
+ var i,
133
+ lastIndex,
134
+ urlParts = [];
93
135
 
94
- Murlsh.imgThumb = function() {
95
- var lastIndex;
96
- var urlParts = [];
136
+ for (i = 0; i < arguments.length; i += 1) {
137
+ urlParts.push(arguments[i]);
138
+ }
97
139
 
98
- for (var i = 0; i < arguments.length; i += 1) {
99
- urlParts.push(arguments[i]);
100
- }
140
+ lastIndex = urlParts.length - 1;
101
141
 
102
- lastIndex = urlParts.length - 1;
142
+ // if pdf the thumbnail will be .png
143
+ if (urlParts[lastIndex].match(/^pdf$/i)) {
144
+ urlParts.splice(lastIndex, 1, 'png');
145
+ }
103
146
 
104
- // if pdf the thumbnail will be .png
105
- if (urlParts[lastIndex].match(/^pdf$/i)) {
106
- urlParts.splice(lastIndex, 1, 'png');
147
+ return img(urlParts.join('')).addClass('thumb');
107
148
  }
108
149
 
109
- return Murlsh.img(urlParts.join('')).addClass('thumb');
110
- };
150
+ function thumbInsert(img, clickFunction, a) {
151
+ if (img) {
152
+ if (my.isIphone()) {
153
+ a.prepend(img);
154
+ } else {
155
+ if (clickFunction) {
156
+ img.click(clickFunction);
157
+ }
158
+ a.before(img);
159
+ }
160
+ }
161
+ }
111
162
 
112
- Murlsh.imgClick = function() {
113
- Murlsh.closerAdd(Murlsh.img($(this).data('href')));
114
- };
163
+ function twitterAddLinks(s) {
164
+ // turn urls into links and Twitter usernames into links to Twitter
165
+ var result = autoLink(s);
115
166
 
116
- Murlsh.twitterThumb = function(d) {
117
- return Murlsh.img(d.user.profile_image_url).addClass('thumb twitter');
118
- }
167
+ result = result.replace(
168
+ /(^|[\s,(])@([0-9a-z_]+)($|[\s,.)])/gi,
169
+ '$1<a href="http://twitter.com/$2">@$2</a>$3');
119
170
 
120
- Murlsh.vimeoThumb = function(d) {
121
- return Murlsh.img(d.thumbnail_medium, d.title).addClass('thumb vimeo');
122
- };
171
+ return result;
172
+ }
123
173
 
124
- Murlsh.vimeoClick = function() {
125
- Murlsh.closerAdd($(this).data('embedHtml'));
126
- };
174
+ function twitterThumb(d) {
175
+ return img(d.user.profile_image_url).addClass('thumb twitter');
176
+ }
127
177
 
128
- Murlsh.youtubeThumb = function(id) {
129
- return Murlsh.img('http://img.youtube.com/vi/' + id + '/default.jpg',
130
- 'click to watch').addClass('thumb youtube').data('id', id);
131
- };
178
+ function vimeoClick() {
179
+ closerAdd($(this).data('embedHtml'));
180
+ }
132
181
 
133
- Murlsh.youtubeClick = function() {
134
- var movie = 'http://www.youtube.com/v/' + $(this).data('id') + '?' +
135
- $.param({
136
- fs : 1,
137
- hd : 1,
138
- hl : 'en',
139
- iv_load_policy : 3,
140
- showinfo : 0,
141
- showsearch : 0
142
- });
182
+ function vimeoThumb(d) {
183
+ return img(d.thumbnail_medium, d.title).addClass('thumb vimeo');
184
+ }
143
185
 
144
- Murlsh.closerAdd(Murlsh.objectTag(movie, 505, 640,
145
- [{ name : 'movie', value : movie }]));
146
- };
186
+ function youtubeClick() {
187
+ var movie = 'http://www.youtube.com/v/' + $(this).data('id') + '?' +
188
+ $.param({
189
+ fs : 1,
190
+ hd : 1,
191
+ hl : 'en',
192
+ iv_load_policy : 3,
193
+ showinfo : 0,
194
+ showsearch : 0
195
+ });
147
196
 
148
- Murlsh.thumbInsert = function(img, clickFunction, a) {
149
- if (img) {
150
- if (Murlsh.isIphone()) {
151
- a.prepend(img);
152
- } else {
153
- if (clickFunction) {
154
- img.click(clickFunction);
155
- }
156
- a.before(img);
157
- }
197
+ closerAdd(objectTag(movie, 505, 640, [{
198
+ name : 'movie',
199
+ value : movie
200
+ }]));
158
201
  }
159
- };
160
202
 
161
- Murlsh.isIphone = function() {
162
- return navigator.userAgent.match(/i(phone|pod)/i);
163
- };
164
-
165
- Murlsh.hrefRes = {
166
- flickr :
167
- /^http:\/\/(?:www\.)?flickr\.com\/photos\/[@\w\-]+?\/([\d]+)/i,
168
- imageshack :
169
- /^(http:\/\/img\d+\.imageshack\.us\/img\d+\/\d+\/\w+\.)(jpe?g|gif|png)$/i,
170
- imgur :
171
- /^(http:\/\/(?:i\.)?imgur\.com\/[a-z\d]+)(\.(?:jpe?g|gif|png))$/i,
172
- mp3 :
173
- /\.mp3$/i,
174
- s3 :
175
- /^(http:\/\/static\.mmb\.s3\.amazonaws\.com\/[\w\-]+\.)(jpe?g|gif|pdf|png)$/i,
176
- twitter :
177
- /^https?:\/\/twitter\.com\/\w+\/status(?:es)?\/(\d+)$/i,
178
- vimeo :
179
- /^http:\/\/(?:www\.)?vimeo\.com\/(\d+)$/i,
180
- youtube :
181
- /^http:\/\/(?:(?:www|uk)\.)?youtube\.com\/watch\?v=([\w\-]+)(?:&|$)/i
182
- };
203
+ function youtubeThumb(id) {
204
+ return img('http://img.youtube.com/vi/' + id + '/default.jpg',
205
+ 'click to watch').addClass('thumb youtube').data('id', id);
206
+ }
183
207
 
184
- Murlsh.addExtra = function() {
185
- var href = $(this).attr('href');
186
- var match = {};
187
- var swf = 'swf/player_mp3_mini.swf';
188
- var thumb;
208
+ my.addComments = function (link, comments) {
209
+ var avatar,
210
+ comment,
211
+ commentElement,
212
+ i,
213
+ ul = $('<ul />').addClass('comments').appendTo(link.parent());
214
+
215
+ for (i = 0; i < comments.length; i += 1) {
216
+ comment = comments[i];
217
+ commentElement = $('<li />');
218
+ if (comment.authorAvatar.length > 0) {
219
+ avatar = img(comment.authorAvatar).appendTo(commentElement);
220
+ if (comment.authorUrl.length > 0) {
221
+ avatar.wrapAll($('<a />').attr('href', comment.authorUrl));
222
+ }
223
+ commentElement.append(' ');
224
+ }
225
+ commentElement
226
+ .append($('<span />').append(comment.authorName).addClass(
227
+ 'comment-name'))
228
+ .append(' : ')
229
+ .append($('<span />').append(autoLink(comment.comment)).
230
+ addClass('comment-comment'))
231
+ .appendTo(ul);
232
+ }
233
+ };
189
234
 
190
- $.each(Murlsh.hrefRes, function(x, re) {
191
- return !(match[x] = re.exec(href));
192
- });
235
+ my.addExtra = function () {
236
+ var host,
237
+ hostMatch,
238
+ href = $(this).attr('href'),
239
+ match = {},
240
+ swf = 'swf/player_mp3_mini.swf',
241
+ thumb;
193
242
 
194
- if (match.flickr) {
195
- $.ajax({
196
- // url : 'http://api.flickr.com/services/rest/',
197
- url : 'flickr',
198
- data : {
199
- format : 'json',
200
- method : 'flickr.photos.getinfo',
201
- photo_id : match.flickr[1]
202
- },
203
- dataType : 'jsonp',
204
- jsonp : 'jsoncallback',
205
- success : function(d) {
206
- Murlsh.thumbInsert(Murlsh.flickrThumb(d),
207
- Murlsh.flickrClick, $(this));
208
- },
209
- context : $(this),
210
- jsonpCallback : 'flickrCallback' + match.flickr[1]
243
+ $.each(hrefRes, function (x, re) {
244
+ return !(match[x] = re.exec(href));
211
245
  });
212
- } else if (match.imageshack) {
213
- Murlsh.thumbInsert(
214
- Murlsh.imgThumb(match.imageshack[1], 'th.', match.imageshack[2]).data(
215
- 'href', match.imageshack[0]),
216
- Murlsh.imgClick, $(this).html('imageshack.us'));
217
- } else if (match.imgur) {
218
- Murlsh.thumbInsert(
219
- Murlsh.imgThumb(match.imgur[1], 's', match.imgur[2]).data('href', match.imgur[0]),
220
- Murlsh.imgClick, $(this).html('imgur.com'));
221
- } else if (match.mp3) {
222
- $(this).before(Murlsh.objectTag(swf, 20, 200, [
223
- { name : 'bgcolor', value : '#000000' },
224
- { name : 'FlashVars', value : 'mp3=' + href },
225
- { name : 'movie', value : swf }
226
- ]));
227
- } else if (match.s3) {
228
- thumb = Murlsh.imgThumb(match.s3[1], 'th.', match.s3[2]);
229
-
230
- if (match.s3[2].match(/^pdf$/i)) {
231
- $(this).before(thumb).html('pdf');
232
- } else {
233
- if (Murlsh.isIphone()) {
234
- $(this).html(thumb);
246
+
247
+ if (match.flickr) {
248
+ $.ajax({
249
+ // url : 'http://api.flickr.com/services/rest/',
250
+ url : 'flickr',
251
+ data : {
252
+ format : 'json',
253
+ method : 'flickr.photos.getinfo',
254
+ photo_id : match.flickr[1]
255
+ },
256
+ dataType : 'jsonp',
257
+ jsonp : 'jsoncallback',
258
+ success : function (d) {
259
+ thumbInsert(flickrThumb(d), flickrClick, $(this));
260
+ },
261
+ context : $(this),
262
+ jsonpCallback : 'flickrCallback' + match.flickr[1]
263
+ });
264
+ } else if (match.imageshack) {
265
+ thumbInsert(imgThumb(match.imageshack[1], 'th.',
266
+ match.imageshack[2]).data('href', match.imageshack[0]),
267
+ imgClick, $(this).html('imageshack.us'));
268
+ } else if (match.imgur) {
269
+ thumbInsert(imgThumb(match.imgur[1], 's', match.imgur[2]).data(
270
+ 'href', match.imgur[0]), imgClick, $(this).html('imgur.com'));
271
+ } else if (match.mp3) {
272
+ $(this).before(objectTag(swf, 20, 200, [
273
+ { name : 'bgcolor', value : '#000000' },
274
+ { name : 'FlashVars', value : 'mp3=' + href },
275
+ { name : 'movie', value : swf }
276
+ ]));
277
+ } else if (match.s3) {
278
+ thumb = imgThumb(match.s3[1], 'th.', match.s3[2]);
279
+
280
+ if (match.s3[2].match(/^pdf$/i)) {
281
+ $(this).before(thumb).html('pdf');
235
282
  } else {
236
- $(this).html('link');
237
- $(this).before(thumb.data('href', match.s3[0]).click(
238
- Murlsh.imgClick));
283
+ if (my.isIphone()) {
284
+ $(this).html(thumb);
285
+ } else {
286
+ $(this).html('link');
287
+ $(this).before(thumb.data('href', match.s3[0]).click(
288
+ imgClick));
289
+ }
290
+ }
291
+ } else if (match.twitter) {
292
+ $.ajax({
293
+ // url : 'http://api.twitter.com/1/statuses/show/' +
294
+ url : '/twitter/1/statuses/show/' +
295
+ match.twitter[1] + '.json',
296
+ dataType : 'jsonp',
297
+ success : function (d) {
298
+ var nameLink = $('<a />', {
299
+ href: 'http://twitter.com/' + d.user.screen_name +
300
+ '/status/' + d.id,
301
+ text: '@' + d.user.screen_name
302
+ }),
303
+ tweet = $('<span />').addClass('tweet').append(
304
+ nameLink).append(': ').append(twitterAddLinks(
305
+ d.text));
306
+
307
+ thumbInsert(twitterThumb(d), null, nameLink);
308
+
309
+ $(this).replaceWith(tweet);
310
+ },
311
+ context : $(this),
312
+ jsonpCallback : 'twitterCallback' + match.twitter[1]
313
+ });
314
+ } else if (match.vimeo) {
315
+ $.ajax({
316
+ url : 'http://vimeo.com/api/v2/video/' + match.vimeo[1] +
317
+ '.json',
318
+ dataType : 'jsonp',
319
+ success : function (d) {
320
+ var video = d[0],
321
+ movie = 'http://vimeo.com/moogaloop.swf?clip_id=' +
322
+ video.id;
323
+
324
+ thumbInsert(vimeoThumb(video).data('embedHtml',
325
+ objectTag(movie, video.height, video.width, [
326
+ { name : 'movie', value : movie }
327
+ ])), vimeoClick, $(this));
328
+ },
329
+ context : $(this),
330
+ jsonpCallback : 'vimeoCallback' + match.vimeo[1]
331
+ });
332
+ } else if (match.youtube) {
333
+ thumbInsert(youtubeThumb(match.youtube[1]), youtubeClick, $(this));
334
+ } else {
335
+ // Apple touch icon if available
336
+ hostMatch = hostRe.exec(href);
337
+ if (hostMatch) {
338
+ host = hostMatch[1];
339
+ if ($.inArray(host, config.apple_icon_hosts) > -1) {
340
+ thumbInsert(appleThumb(host), null, $(this));
341
+ }
239
342
  }
240
343
  }
241
- } else if (match.twitter) {
242
- $.ajax({
243
- // url : 'http://api.twitter.com/1/statuses/show/' +
244
- url : '/twitter/1/statuses/show/' +
245
- match.twitter[1] + '.json',
246
- dataType : 'jsonp',
247
- success : function(d) {
248
- var nameLink = $('<a />', {
249
- href: 'http://twitter.com/' + d.user.screen_name,
250
- text: d.user.screen_name
251
- });
344
+ };
252
345
 
253
- $(this).html(d.text).before(nameLink).before(
254
- document.createTextNode(': '));
346
+ my.formatLi = function (d) {
347
+ var iconSize = 32,
348
+ li = $('<li />').append($('<a />', {
349
+ href : d.url,
350
+ text : d.title
351
+ }));
255
352
 
256
- Murlsh.thumbInsert(Murlsh.twitterThumb(d), null, nameLink);
257
- },
258
- context : $(this),
259
- jsonpCallback : 'twitterCallback' + match.twitter[1]
260
- });
261
- } else if (match.vimeo) {
262
- $.ajax({
263
- url : 'http://vimeo.com/api/v2/video/' + match.vimeo[1] + '.json',
264
- dataType : 'jsonp',
265
- success : function(d) {
266
- var video = d[0];
267
- var movie = 'http://vimeo.com/moogaloop.swf?clip_id=' + video.id;
268
-
269
- Murlsh.thumbInsert(Murlsh.vimeoThumb(video).data(
270
- 'embedHtml',
271
- Murlsh.objectTag(movie, video.height, video.width, [
272
- { name : 'movie', value : movie }
273
- ])), Murlsh.vimeoClick, $(this));
274
- },
275
- context : $(this),
276
- jsonpCallback : 'vimeoCallback' + match.vimeo[1]
277
- });
278
- } else if (match.youtube) {
279
- Murlsh.thumbInsert(Murlsh.youtubeThumb(match.youtube[1]),
280
- Murlsh.youtubeClick, $(this));
281
- }
282
- };
353
+ if (d.name) {
354
+ li.prepend($('<div />', { text : d.name }).addClass('name'));
355
+ }
283
356
 
284
- Murlsh.formatLi = function(d) {
285
- var iconSize = 32;
286
- var li = $('<li />').append($('<a />', {
287
- href : d.url,
288
- text : d.title
289
- }));
357
+ if (d.email) {
358
+ li.prepend($('<div />').addClass('icon').append(
359
+ img('http://www.gravatar.com/avatar/' + d.email + '?s=' +
360
+ iconSize, d.name).attr({
361
+ width : iconSize,
362
+ height : iconSize
363
+ })));
364
+ }
290
365
 
291
- if (d.name) {
292
- li.prepend($('<div />', { text : d.name }).addClass('name'));
293
- }
366
+ return li;
367
+ };
294
368
 
295
- if (d.email) {
296
- li.prepend($('<div />').addClass('icon').append(
297
- Murlsh.img(
298
- 'http://www.gravatar.com/avatar/' + d.email + '?s=' + iconSize,
299
- d.name).attr({
300
- width : iconSize,
301
- height : iconSize
302
- })));
303
- }
369
+ my.iphoneInit = function () {
370
+ window.onorientationchange = function () {
371
+ var width = 450;
372
+ if (window.orientation === 0 || window.orientation === 180) {
373
+ width = 290;
374
+ }
375
+ $('#urls').width(width);
376
+ };
304
377
 
305
- return li;
306
- };
378
+ window.onorientationchange();
307
379
 
308
- Murlsh.iphoneInit = function() {
309
- window.onorientationchange = function() {
310
- var width = 450;
311
- if (window.orientation === 0 || window.orientation === 180) {
312
- width = 290;
313
- }
314
- $('#urls').width(width);
380
+ $('a.feed').replaceWith($('<a />', {
381
+ href : '#bottom',
382
+ text : 'bottom'
383
+ }));
315
384
  };
316
385
 
317
- window.onorientationchange();
386
+ my.isIphone = function () {
387
+ return navigator.userAgent.match(/i(phone|pod)/i);
388
+ };
318
389
 
319
- $('a.feed').replaceWith($('<a />', {
320
- href : '#bottom',
321
- text : 'bottom'
322
- }));
390
+ return my;
323
391
  };
324
392
 
325
- $(document).ready(function() {
326
- if (Murlsh.isIphone()) {
327
- Murlsh.iphoneInit();
328
- }
329
- $('a.m').map(Murlsh.addExtra);
330
-
331
- $('#submit').click(function() {
332
- $.post('url', {
333
- url : $('#url').val(),
334
- via : $('#via').val(),
335
- auth : $('#auth').val()
336
- }, function (d) {
337
- $.each(d, function(i, v) {
338
- var li = Murlsh.formatLi(v);
339
- $('#urls > li:first').after(li);
340
- $(li).children('a:first').map(Murlsh.addExtra);
393
+ $(document).ready(function () {
394
+ $.getJSON('config', function (config) {
395
+ var murlsh = new Murlsh(config, $, navigator, window),
396
+ urls;
397
+
398
+ if (murlsh.isIphone()) {
399
+ murlsh.iphoneInit();
400
+ }
401
+
402
+ $('#submit').click(function () {
403
+ $.post('url', {
404
+ url : $('#url').val(),
405
+ via : $('#via').val(),
406
+ auth : $('#auth').val()
407
+ }, function (d) {
408
+ $.each(d, function (i, v) {
409
+ var li = murlsh.formatLi(v);
410
+ $('#urls > li:first').after(li);
411
+ $(li).children('a:first').each(murlsh.addExtra);
412
+ });
413
+ $('#url').val('');
414
+ $('#via').val('');
415
+ }, 'json');
416
+ });
417
+
418
+ urls = $('a.m');
419
+
420
+ urls.each(murlsh.addExtra);
421
+
422
+ /*
423
+ // experimental comment support, to enable uncomment and edit comments.json
424
+ $.getJSON('/js/comments.json', function (data) {
425
+ urls.each(function () {
426
+ var href = $(this).attr('href');
427
+ if (href in data) {
428
+ murlsh.addComments($(this), data[href]);
429
+ }
341
430
  });
342
- $('#url').val('');
343
- $('#via').val('');
344
- }, 'json');
431
+ });
432
+ */
345
433
  });
346
-
347
434
  });
data/spec/auth_spec.rb CHANGED
@@ -1,5 +1,3 @@
1
- $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
-
3
1
  %w{
4
2
  tempfile
5
3
 
@@ -1,5 +1,3 @@
1
- $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
-
3
1
  %w{
4
2
  rack/test
5
3
 
data/spec/doc_spec.rb CHANGED
@@ -1,5 +1,3 @@
1
- $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
-
3
1
  %w{
4
2
  hpricot
5
3
 
data/spec/markup_spec.rb CHANGED
@@ -1,5 +1,3 @@
1
- $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
-
3
1
  %w{
4
2
  murlsh
5
3
  }.each { |m| require m }
@@ -1,5 +1,3 @@
1
- $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
-
3
1
  %w{
4
2
  murlsh
5
3
  }.each { |m| require m }
data/spec/uri_ask_spec.rb CHANGED
@@ -1,5 +1,3 @@
1
- $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
-
3
1
  %w{
4
2
  uri
5
3
 
data/spec/uri_spec.rb CHANGED
@@ -1,5 +1,3 @@
1
- $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
-
3
1
  %w{
4
2
  murlsh
5
3
  }.each { |m| require m }
@@ -1,5 +1,3 @@
1
- $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
-
3
1
  %w{
4
2
  murlsh
5
3
  }.each { |m| require m }
@@ -1,5 +1,3 @@
1
- $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
-
3
1
  %w{
4
2
  murlsh
5
3
  }.each { |m| require m }
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: murlsh
3
3
  version: !ruby/object:Gem::Version
4
- hash: 59
4
+ hash: 55
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 9
8
+ - 10
9
9
  - 0
10
- version: 0.9.0
10
+ version: 0.10.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matthew M. Boedicker
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-06-03 00:00:00 -04:00
18
+ date: 2010-08-05 00:00:00 -04:00
19
19
  default_executable: murlsh
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -115,9 +115,25 @@ dependencies:
115
115
  type: :runtime
116
116
  version_requirements: *id006
117
117
  - !ruby/object:Gem::Dependency
118
- name: rack
118
+ name: push-notify
119
119
  prerelease: false
120
120
  requirement: &id007 !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ hash: 27
126
+ segments:
127
+ - 0
128
+ - 1
129
+ - 0
130
+ version: 0.1.0
131
+ type: :runtime
132
+ version_requirements: *id007
133
+ - !ruby/object:Gem::Dependency
134
+ name: rack
135
+ prerelease: false
136
+ requirement: &id008 !ruby/object:Gem::Requirement
121
137
  none: false
122
138
  requirements:
123
139
  - - ">="
@@ -129,11 +145,11 @@ dependencies:
129
145
  - 0
130
146
  version: 1.0.0
131
147
  type: :runtime
132
- version_requirements: *id007
148
+ version_requirements: *id008
133
149
  - !ruby/object:Gem::Dependency
134
150
  name: rack-cache
135
151
  prerelease: false
136
- requirement: &id008 !ruby/object:Gem::Requirement
152
+ requirement: &id009 !ruby/object:Gem::Requirement
137
153
  none: false
138
154
  requirements:
139
155
  - - ">="
@@ -145,11 +161,11 @@ dependencies:
145
161
  - 2
146
162
  version: 0.5.2
147
163
  type: :runtime
148
- version_requirements: *id008
164
+ version_requirements: *id009
149
165
  - !ruby/object:Gem::Dependency
150
166
  name: rack-throttle
151
167
  prerelease: false
152
- requirement: &id009 !ruby/object:Gem::Requirement
168
+ requirement: &id010 !ruby/object:Gem::Requirement
153
169
  none: false
154
170
  requirements:
155
171
  - - ">="
@@ -161,11 +177,11 @@ dependencies:
161
177
  - 0
162
178
  version: 0.3.0
163
179
  type: :runtime
164
- version_requirements: *id009
180
+ version_requirements: *id010
165
181
  - !ruby/object:Gem::Dependency
166
182
  name: sqlite3-ruby
167
183
  prerelease: false
168
- requirement: &id010 !ruby/object:Gem::Requirement
184
+ requirement: &id011 !ruby/object:Gem::Requirement
169
185
  none: false
170
186
  requirements:
171
187
  - - ">="
@@ -177,11 +193,11 @@ dependencies:
177
193
  - 1
178
194
  version: 1.2.1
179
195
  type: :runtime
180
- version_requirements: *id010
196
+ version_requirements: *id011
181
197
  - !ruby/object:Gem::Dependency
182
198
  name: tinyatom
183
199
  prerelease: false
184
- requirement: &id011 !ruby/object:Gem::Requirement
200
+ requirement: &id012 !ruby/object:Gem::Requirement
185
201
  none: false
186
202
  requirements:
187
203
  - - ">="
@@ -193,7 +209,7 @@ dependencies:
193
209
  - 1
194
210
  version: 0.1.1
195
211
  type: :runtime
196
- version_requirements: *id011
212
+ version_requirements: *id012
197
213
  description: url sharing site framework with easy adding, title lookup, atom feed, thumbnails and embedding
198
214
  email: matthewm@boedicker.org
199
215
  executables:
@@ -214,6 +230,7 @@ files:
214
230
  - config.yaml
215
231
  - lib/murlsh.rb
216
232
  - lib/murlsh/auth.rb
233
+ - lib/murlsh/config_server.rb
217
234
  - lib/murlsh/dispatch.rb
218
235
  - lib/murlsh/doc.rb
219
236
  - lib/murlsh/etag_add_encoding.rb
@@ -248,6 +265,7 @@ files:
248
265
  - plugins/via_50_domain.rb
249
266
  - public/css/jquery.jgrowl.css
250
267
  - public/css/screen.css
268
+ - public/js/comments.json
251
269
  - public/js/jquery-1.4.2.min.js
252
270
  - public/js/jquery.jgrowl_compressed.js
253
271
  - public/js/js.js