murlsh 0.11.0 → 1.0.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.
Files changed (56) hide show
  1. data/.htaccess +2 -0
  2. data/README.textile +3 -33
  3. data/Rakefile +12 -5
  4. data/VERSION +1 -1
  5. data/config.ru +10 -4
  6. data/config.yaml +6 -12
  7. data/lib/murlsh/dispatch.rb +0 -6
  8. data/lib/murlsh/img_store.rb +36 -0
  9. data/lib/murlsh/markup.rb +0 -1
  10. data/lib/murlsh/plugin.rb +2 -6
  11. data/lib/murlsh/uri_ask.rb +66 -22
  12. data/lib/murlsh/url.rb +0 -18
  13. data/lib/murlsh/url_body.rb +15 -23
  14. data/lib/murlsh/url_server.rb +5 -5
  15. data/murlsh.gemspec +44 -19
  16. data/plugins/add_post_50_update_feed.rb +17 -3
  17. data/plugins/add_post_50_update_podcast.rb +46 -0
  18. data/plugins/add_post_50_update_rss.rb +18 -2
  19. data/plugins/add_post_60_notify_hubs.rb +3 -1
  20. data/plugins/add_pre_40_convert_mobile.rb +30 -0
  21. data/plugins/add_pre_50_lookup_content_type_title.rb +11 -4
  22. data/plugins/add_pre_60_flickr.rb +38 -0
  23. data/plugins/add_pre_60_github_title.rb +5 -1
  24. data/plugins/add_pre_60_google_code_title.rb +5 -2
  25. data/plugins/add_pre_60_imageshack.rb +31 -0
  26. data/plugins/add_pre_60_imgur.rb +32 -0
  27. data/plugins/add_pre_60_s3_image.rb +34 -0
  28. data/plugins/add_pre_60_twitter.rb +35 -0
  29. data/plugins/add_pre_60_vimeo.rb +35 -0
  30. data/plugins/add_pre_60_youtube.rb +31 -0
  31. data/plugins/html_parse_50_hpricot.rb +2 -0
  32. data/plugins/url_display_add_45_mp3.rb +30 -0
  33. data/plugins/url_display_add_50_hostrec.rb +38 -0
  34. data/plugins/url_display_add_55_content_type.rb +27 -0
  35. data/plugins/url_display_add_60_via.rb +52 -0
  36. data/plugins/url_display_add_65_time.rb +22 -0
  37. data/public/css/jquery.jgrowl.css +0 -3
  38. data/public/css/screen.css +0 -18
  39. data/public/img/thumb/README +0 -0
  40. data/public/js/jquery-1.4.3.min.js +166 -0
  41. data/public/js/js.js +62 -234
  42. data/public/js/twitter-text-1.0.3.js +538 -0
  43. data/spec/img_store_spec.rb +53 -0
  44. data/spec/uri_ask_spec.rb +14 -4
  45. metadata +139 -37
  46. data/lib/murlsh/flickr_server.rb +0 -55
  47. data/lib/murlsh/twitter_server.rb +0 -45
  48. data/lib/murlsh/unwrap_jsonp.rb +0 -15
  49. data/lib/murlsh/xhtml_response.rb +0 -20
  50. data/plugins/hostrec_50_redundant.rb +0 -14
  51. data/plugins/hostrec_60_skip.rb +0 -24
  52. data/plugins/time_50_ago.rb +0 -16
  53. data/plugins/via_50_domain.rb +0 -36
  54. data/public/js/jquery-1.4.2.min.js +0 -154
  55. data/spec/unwrap_json_spec.rb +0 -21
  56. data/spec/xhtml_response_spec.rb +0 -112
data/public/js/js.js CHANGED
@@ -3,35 +3,28 @@
3
3
  "use strict";
4
4
 
5
5
  var Murlsh = function (config, $, navigator, window) {
6
- function compileRegexMap(regexMap) {
7
- var result = {};
8
- $.each(regexMap, function (reStr) {
9
- result[reStr] = new RegExp('^' + reStr + '$', 'i');
10
- });
11
-
12
- return result;
13
- }
14
6
 
15
7
  var my = {},
16
8
  hrefRes = {
17
- flickr :
18
- /^http:\/\/(?:www\.)?flickr\.com\/photos\/[@\w\-]+?\/([\d]+)/i,
19
9
  imageshack :
20
- /^(http:\/\/img\d+\.imageshack\.us\/img\d+\/\d+\/\w+\.)(jpe?g|gif|png)$/i,
10
+ /^http:\/\/img\d+\.imageshack\.us\/img\d+\/\d+\/\w+\.jpe?g|gif|png$/i,
21
11
  imgur :
22
- /^(http:\/\/(?:i\.)?imgur\.com\/)([a-z\d]+)(\.(?:jpe?g|gif|png))$/i,
23
- mp3 :
24
- /\.mp3$/i,
12
+ /^http:\/\/(?:i\.)?imgur\.com\/[a-z\d]+\.(?:jpe?g|gif|png)$/i,
25
13
  s3 :
26
- /^(http:\/\/static\.mmb\.s3\.amazonaws\.com\/[\w\-]+\.)(jpe?g|gif|pdf|png)$/i,
14
+ /^http:\/\/static\.mmb\.s3\.amazonaws\.com\/[\w\-]+\.(jpe?g|gif|pdf|png)$/i,
27
15
  twitter :
28
- /^https?:\/\/twitter\.com\/\w+\/status(?:es)?\/(\d+)$/i,
16
+ /^https?:\/\/twitter\.com\/\w+\/status(?:es)?\/\d+$/i,
29
17
  vimeo :
30
18
  /^http:\/\/(?:www\.)?vimeo\.com\/(\d+)$/i,
31
19
  youtube :
32
20
  /^http:\/\/(?:(?:www|uk)\.)?youtube\.com\/watch\?v=([\w\-]+)(?:&|$)/i
33
- },
34
- thumbLocatorsCompiled = compileRegexMap(config.thumb_locators);
21
+ };
22
+
23
+ function setupClickHandler(jQueryObject, dataKey, dataValue, handler) {
24
+ if (!my.isIphone()) {
25
+ jQueryObject.data(dataKey, dataValue).click(handler);
26
+ }
27
+ }
35
28
 
36
29
  function autoLink(s) {
37
30
  // turn urls into links
@@ -42,8 +35,13 @@ var Murlsh = function (config, $, navigator, window) {
42
35
  return result;
43
36
  }
44
37
 
45
- function escapeXml(s) {
46
- return s.replace(/&/g, '&');
38
+ function makeIframe(src) {
39
+ return $('<iframe />').attr({
40
+ src: src,
41
+ width: 640,
42
+ height: 385,
43
+ frameborder: 0
44
+ });
47
45
  }
48
46
 
49
47
  function img(src, text) {
@@ -67,23 +65,6 @@ var Murlsh = function (config, $, navigator, window) {
67
65
  }
68
66
  }
69
67
 
70
- function objectTag(data, height, width, params) {
71
- // this does not use jQuery to build tags because building object
72
- // tags is broken in IE
73
- var result = '<object data="' + escapeXml(data) +
74
- '" height="' + height +
75
- '" type="application/x-shockwave-flash" width="' + width + '">';
76
-
77
- $.each(params, function (i, v) {
78
- result += '<param name="' + v.name + '" value="' +
79
- escapeXml(v.value) + '" />';
80
- });
81
-
82
- result += '</object>';
83
-
84
- return result;
85
- }
86
-
87
68
  function closerAdd(x, header) {
88
69
  var html = (typeof x === 'object') ? $('<div />').append(x).html() : x;
89
70
 
@@ -97,116 +78,31 @@ var Murlsh = function (config, $, navigator, window) {
97
78
  makeFit($(this), Math.round($(window).width() / 2),
98
79
  Math.round($(window).height() - 100));
99
80
  });
100
- }
81
+ },
82
+ animateOpen : { width : 'show' },
83
+ animateClose : { width : 'hide' }
101
84
  });
102
85
  }
103
86
 
104
- function flickrClick() {
105
- closerAdd(img($(this).data('zoom')));
106
- }
107
-
108
- function flickrThumb(d) {
109
- var base,
110
- owner,
111
- photo = d.photo,
112
- zoom;
113
-
114
- if (d.stat === 'ok') {
115
- base = 'http://farm' + photo.farm + '.static.flickr.com/' +
116
- photo.server + '/' + photo.id + '_';
117
- zoom = base + photo.secret + '_m.jpg';
118
-
119
- if (photo.originalsecret) {
120
- zoom = base + photo.originalsecret + '_o.' +
121
- photo.originalformat;
122
- }
123
-
124
- owner = photo.owner;
125
- return img(base + photo.secret + '_s.jpg', photo.title._content +
126
- (owner && owner.username ? ' by ' + owner.username : '')
127
- ).addClass('thumb flickr').data('zoom', zoom);
128
- }
87
+ function imgClick(event) {
88
+ closerAdd(img($(event.target).data('href')));
129
89
  }
130
90
 
131
- function imgClick() {
132
- closerAdd(img($(this).data('href')));
133
- }
134
-
135
- function imgThumb() {
136
- var i,
137
- lastIndex,
138
- urlParts = [];
139
-
140
- for (i = 0; i < arguments.length; i += 1) {
141
- urlParts.push(arguments[i]);
142
- }
143
-
144
- lastIndex = urlParts.length - 1;
145
-
146
- // if pdf the thumbnail will be .png
147
- if (urlParts[lastIndex].match(/^pdf$/i)) {
148
- urlParts.splice(lastIndex, 1, 'png');
149
- }
91
+ function vimeoClick(event) {
92
+ var iframe = makeIframe(
93
+ 'http://player.vimeo.com/video/' + $(event.target).data('id'));
150
94
 
151
- return img(urlParts.join('')).addClass('thumb');
95
+ closerAdd(iframe);
152
96
  }
153
97
 
154
- function thumbInsert(img, clickFunction, a) {
155
- if (img) {
156
- if (my.isIphone()) {
157
- a.prepend(img);
158
- } else {
159
- if (clickFunction) {
160
- img.click(clickFunction);
161
- }
162
- a.before(img);
163
- }
164
- }
165
- }
166
-
167
- function twitterAddLinks(s) {
168
- // turn urls into links and Twitter usernames into links to Twitter
169
- var result = autoLink(s);
170
-
171
- result = result.replace(
172
- /(^|[\s,(])@([0-9a-z_]+)($|[\s,.)])/gi,
173
- '$1<a href="http://twitter.com/$2">@$2</a>$3');
174
-
175
- return result;
176
- }
177
-
178
- function twitterThumb(d) {
179
- return img(d.user.profile_image_url).addClass('thumb twitter');
180
- }
181
-
182
- function vimeoClick() {
183
- closerAdd($(this).data('embedHtml'));
184
- }
185
-
186
- function vimeoThumb(d) {
187
- return img(d.thumbnail_medium, d.title).addClass('thumb vimeo');
188
- }
189
-
190
- function youtubeClick() {
191
- var movie = 'http://www.youtube.com/v/' + $(this).data('id') + '?' +
192
- $.param({
193
- fs : 1,
194
- hd : 1,
195
- hl : 'en',
196
- iv_load_policy : 3,
197
- showinfo : 0,
198
- showsearch : 0
199
- });
200
-
201
- closerAdd(objectTag(movie, 505, 640, [{
202
- name : 'movie',
203
- value : movie
204
- }]));
205
- }
98
+ function youtubeClick(event) {
99
+ var iframe = makeIframe(
100
+ 'http://www.youtube.com/embed/' + $(event.target).data('id')).attr({
101
+ 'class': 'youtube-player',
102
+ type: 'text/html'
103
+ });
206
104
 
207
- function youtubeThumb(id) {
208
- return img('http://img.youtube.com/vi/' + id + '/default.jpg',
209
- 'click to watch').addClass('thumb youtube').data('id', id);
105
+ closerAdd(iframe);
210
106
  }
211
107
 
212
108
  my.addComments = function (link, comments) {
@@ -240,111 +136,42 @@ var Murlsh = function (config, $, navigator, window) {
240
136
  var thisA = $(this),
241
137
  href = thisA.attr('href'),
242
138
  match = {},
243
- swf = 'swf/player_mp3_mini.swf',
244
- thumb;
139
+ tweetMatch,
140
+ tweetLink,
141
+ formattedTweet;
245
142
 
246
143
  $.each(hrefRes, function (x, re) {
247
144
  return !(match[x] = re.exec(href));
248
145
  });
249
146
 
250
- if (match.flickr) {
251
- $.ajax({
252
- // url : 'http://api.flickr.com/services/rest/',
253
- url : 'flickr',
254
- data : {
255
- format : 'json',
256
- method : 'flickr.photos.getinfo',
257
- photo_id : match.flickr[1]
258
- },
259
- dataType : 'jsonp',
260
- jsonp : 'jsoncallback',
261
- success : function (d) {
262
- thumbInsert(flickrThumb(d), flickrClick, $(this));
263
- },
264
- context : thisA,
265
- jsonpCallback : 'flickrCallback' + match.flickr[1]
266
- });
267
- } else if (match.imageshack) {
268
- thumbInsert(imgThumb(match.imageshack[1], 'th.',
269
- match.imageshack[2]).data('href', match.imageshack[0]),
270
- imgClick, thisA.html('imageshack.us'));
271
- } else if (match.imgur) {
272
- thumbInsert(imgThumb(match.imgur[1], match.imgur[2], 's',
273
- match.imgur[3]).data('href', match.imgur[0]), imgClick,
274
- thisA.html('imgur/' + match.imgur[2] + match.imgur[3]));
275
- } else if (match.mp3) {
276
- thisA.before(objectTag(swf, 20, 200, [
277
- { name : 'bgcolor', value : '#000000' },
278
- { name : 'FlashVars', value : 'mp3=' + href },
279
- { name : 'movie', value : swf }
280
- ]));
147
+ if (match.imageshack || match.imgur) {
148
+ setupClickHandler(thisA.siblings('img'), 'href', href, imgClick);
281
149
  } else if (match.s3) {
282
- thumb = imgThumb(match.s3[1], 'th.', match.s3[2]);
283
-
284
- if (match.s3[2].match(/^pdf$/i)) {
285
- thisA.before(thumb).html('pdf');
286
- } else {
287
- if (my.isIphone()) {
288
- thisA.html(thumb);
289
- } else {
290
- thisA.html('link');
291
- thisA.before(thumb.data('href', match.s3[0]).click(
292
- imgClick));
293
- }
150
+ if (!(match.s3[1].match(/^pdf$/i))) {
151
+ setupClickHandler(thisA.siblings('img'), 'href', href,
152
+ imgClick);
294
153
  }
295
154
  } else if (match.twitter) {
296
- $.ajax({
297
- // url : 'http://api.twitter.com/1/statuses/show/' +
298
- url : '/twitter/1/statuses/show/' +
299
- match.twitter[1] + '.json',
300
- dataType : 'jsonp',
301
- success : function (d) {
302
- var nameLink = $('<a />', {
303
- href: 'http://twitter.com/' + d.user.screen_name +
304
- '/status/' + d.id,
305
- text: '@' + d.user.screen_name
306
- }),
307
- tweet = $('<span />').addClass('tweet').append(
308
- nameLink).append(': ').append(twitterAddLinks(
309
- d.text));
310
-
311
- thumbInsert(twitterThumb(d), null, nameLink);
312
-
313
- $(this).replaceWith(tweet);
314
- },
315
- context : thisA,
316
- jsonpCallback : 'twitterCallback' + match.twitter[1]
317
- });
155
+ thisA.siblings('img').addClass('twitter');
156
+ tweetMatch = /^(@[0-9a-z_]+?): (.+)$/i.exec(thisA.text());
157
+ if (tweetMatch) {
158
+ tweetLink = $('<a />', {
159
+ href : href,
160
+ text : tweetMatch[1]
161
+ });
162
+
163
+ formattedTweet = $('<span />').addClass('tweet').append(
164
+ tweetLink).append(': ').append(
165
+ twttr.txt.autoLink(tweetMatch[2]));
166
+
167
+ thisA.replaceWith(formattedTweet);
168
+ }
318
169
  } else if (match.vimeo) {
319
- $.ajax({
320
- url : 'http://vimeo.com/api/v2/video/' + match.vimeo[1] +
321
- '.json',
322
- dataType : 'jsonp',
323
- success : function (d) {
324
- var video = d[0],
325
- movie = 'http://vimeo.com/moogaloop.swf?clip_id=' +
326
- video.id;
327
-
328
- thumbInsert(vimeoThumb(video).data('embedHtml',
329
- objectTag(movie, video.height, video.width, [
330
- { name : 'movie', value : movie }
331
- ])), vimeoClick, $(this));
332
- },
333
- context : thisA,
334
- jsonpCallback : 'vimeoCallback' + match.vimeo[1]
335
- });
170
+ setupClickHandler(thisA.siblings('img'), 'id', match.vimeo[1],
171
+ vimeoClick);
336
172
  } else if (match.youtube) {
337
- thumbInsert(youtubeThumb(match.youtube[1]), youtubeClick, thisA);
338
- } else {
339
- $.each(config.thumb_locators, function (reStr) {
340
- var re = thumbLocatorsCompiled[reStr];
341
- if (href.match(re)) {
342
- thumbInsert(img(href.replace(re,
343
- config.thumb_locators[reStr])).addClass(
344
- 'thumb locator'), null, thisA);
345
- return false;
346
- }
347
- });
173
+ setupClickHandler(thisA.siblings('img'), 'id', match.youtube[1],
174
+ youtubeClick);
348
175
  }
349
176
  };
350
177
 
@@ -425,7 +252,8 @@ $(document).ready(function () {
425
252
  urls.each(murlsh.addExtra);
426
253
 
427
254
  /*
428
- // experimental comment support, to enable uncomment and edit comments.json
255
+ // experimental comment support, to enable uncomment and edit
256
+ // comments.json
429
257
  $.getJSON('/js/comments.json', function (data) {
430
258
  urls.each(function () {
431
259
  var href = $(this).attr('href');