meme_captain 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +55 -0
- data/lib/meme_captain/caption.rb +45 -0
- data/lib/meme_captain/caption_choice.rb +33 -0
- data/lib/meme_captain/draw.rb +35 -0
- data/lib/meme_captain/filesystem_cache.rb +3 -0
- data/lib/meme_captain/meme.rb +44 -25
- data/lib/meme_captain/server.rb +22 -15
- data/lib/meme_captain/version.rb +3 -0
- data/lib/meme_captain.rb +5 -1
- data/meme_captain.gemspec +3 -0
- data/public/1.gif +0 -0
- data/public/thumbs.jpg +0 -0
- data/script/thumb_sprites.rb +20 -0
- data/views/index.erb +172 -22
- metadata +12 -5
- data/README.textile +0 -20
data/README.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
Ruby gem to create meme images (images with text added at the top and bottom).
|
2
|
+
|
3
|
+
Runs locally and has no web dependencies.
|
4
|
+
|
5
|
+
Works with animated gifs.
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
require 'open-uri'
|
9
|
+
|
10
|
+
require 'meme_captain'
|
11
|
+
|
12
|
+
open('http://memecaptain.com/troll_face.jpg', 'rb') do |f|
|
13
|
+
i = MemeCaptain.meme(f, 'test', '1 2 3')
|
14
|
+
i.display
|
15
|
+
i.write('out.jpg')
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
Also includes a Sinatra app that exposes the API over HTTP which is currently
|
20
|
+
running http://memecaptain.com/
|
21
|
+
|
22
|
+
You can use the memecaptain.com API if you prefer it to using the gem.
|
23
|
+
|
24
|
+
Simplest API:
|
25
|
+
|
26
|
+
```
|
27
|
+
http://memecaptain.com/i?u=<url encoded source image url>&tt=<url encoded top text>&tb=<url encoded bottom text>
|
28
|
+
```
|
29
|
+
|
30
|
+
Example:
|
31
|
+
|
32
|
+
```
|
33
|
+
http://memecaptain.com/i?u=http%3A%2F%2Fmemecaptain.com%2Fyao_ming.jpg&tt=sure+i%27ll+test&tb=the+api
|
34
|
+
```
|
35
|
+
|
36
|
+
![Sure I'll test the API](http://memecaptain.com/i?u=http%3A%2F%2Fmemecaptain.com%2Fyao_ming.jpg&tt=sure+i%27ll+test&tb=the+api)
|
37
|
+
|
38
|
+
If you want better error messages, use this which will return JSON:
|
39
|
+
|
40
|
+
```
|
41
|
+
http://memecaptain.com/g?u=<url encoded source image url>&tt=<url encoded top text>&tb=<url encoded bottom text>
|
42
|
+
```
|
43
|
+
|
44
|
+
Example:
|
45
|
+
|
46
|
+
```
|
47
|
+
http://memecaptain.com/g?u=http%3A%2F%2Fmemecaptain.com%2Fyao_ming.jpg&tt=sure+i%27ll+test&tb=the+api
|
48
|
+
```
|
49
|
+
|
50
|
+
```json
|
51
|
+
{
|
52
|
+
permUrl: "http://memecaptain.com/i?u=http%3A%2F%2Fmemecaptain.com%2Fyao_ming.jpg&tt=sure+i%27ll+test&tb=the+api"
|
53
|
+
tempUrl: "http://memecaptain.com/tmp/de55f7a78c6559d4a24ef3e72e2de89992b82695.jpeg"
|
54
|
+
}
|
55
|
+
```
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module MemeCaptain
|
2
|
+
|
3
|
+
class Caption < String
|
4
|
+
|
5
|
+
def initialize(s='')
|
6
|
+
super s.to_s
|
7
|
+
end
|
8
|
+
|
9
|
+
# Return the contents of the string quoted for ImageMagick annotate.
|
10
|
+
def annotate_quote
|
11
|
+
Caption.new(gsub('\\', '\\\\\\').gsub('%', '\%'))
|
12
|
+
end
|
13
|
+
|
14
|
+
# Whether the string contains any non-whitespace.
|
15
|
+
def drawable?
|
16
|
+
match(/[^\s]/) ? true : false
|
17
|
+
end
|
18
|
+
|
19
|
+
# Wrap the string of into num_lines lines.
|
20
|
+
def wrap(num_lines)
|
21
|
+
cleaned = gsub(/\s+/, ' ').strip
|
22
|
+
|
23
|
+
chars_per_line = cleaned.size / num_lines.to_f
|
24
|
+
|
25
|
+
lines = []
|
26
|
+
cleaned.split.each do |word|
|
27
|
+
if lines.empty?
|
28
|
+
lines << word
|
29
|
+
else
|
30
|
+
if (lines[-1].size + 1 + word.size) <= chars_per_line or
|
31
|
+
lines.size >= num_lines
|
32
|
+
lines[-1] << ' ' unless lines[-1].empty?
|
33
|
+
lines[-1] << word
|
34
|
+
else
|
35
|
+
lines << word
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
Caption.new(lines.join("\n"))
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module MemeCaptain
|
2
|
+
|
3
|
+
# For comparing different caption line break and pointsize choices.
|
4
|
+
class CaptionChoice
|
5
|
+
include Comparable
|
6
|
+
|
7
|
+
def initialize(fits, pointsize, text)
|
8
|
+
@fits = fits
|
9
|
+
@pointsize = pointsize
|
10
|
+
@text = text
|
11
|
+
end
|
12
|
+
|
13
|
+
def num_lines
|
14
|
+
text.count("\n") + 1
|
15
|
+
end
|
16
|
+
|
17
|
+
def fits_i
|
18
|
+
fits ? 1: 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def <=>(other)
|
22
|
+
[fits_i, pointsize, fits ? -num_lines : num_lines] <=>
|
23
|
+
[other.fits_i, other.pointsize,
|
24
|
+
other.fits ? -other.num_lines : other.num_lines]
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_accessor :fits
|
28
|
+
attr_accessor :pointsize
|
29
|
+
attr_accessor :text
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module MemeCaptain
|
2
|
+
|
3
|
+
# Mix-in for Magick::Draw
|
4
|
+
module Draw
|
5
|
+
|
6
|
+
# Calculate the largest pointsize for text that will be in a width x
|
7
|
+
# height box.
|
8
|
+
#
|
9
|
+
# Return [pointsize, fits] where pointsize is the largest pointsize and
|
10
|
+
# fits is true if that pointsize will fit in the box.
|
11
|
+
def calc_pointsize(width, height, text, min_pointsize)
|
12
|
+
current_pointsize = min_pointsize
|
13
|
+
|
14
|
+
fits = false
|
15
|
+
|
16
|
+
loop {
|
17
|
+
self.pointsize = current_pointsize
|
18
|
+
metrics = get_multiline_type_metrics(text)
|
19
|
+
if metrics.width > width or metrics.height > height
|
20
|
+
if current_pointsize > min_pointsize
|
21
|
+
current_pointsize -= 1
|
22
|
+
fits = true
|
23
|
+
end
|
24
|
+
break
|
25
|
+
else
|
26
|
+
current_pointsize += 1
|
27
|
+
end
|
28
|
+
}
|
29
|
+
|
30
|
+
[current_pointsize, fits]
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -46,6 +46,9 @@ module MemeCaptain
|
|
46
46
|
# Put data in the cache and return its path.
|
47
47
|
def put(id, data)
|
48
48
|
mime_type = MemeCaptain.mime_type(data)
|
49
|
+
unless mime_type
|
50
|
+
raise 'Data loaded from source image url is not an image'
|
51
|
+
end
|
49
52
|
file_path = id_path(id, ".#{mime_type.extensions[0]}")
|
50
53
|
|
51
54
|
open(file_path, 'w') do |f|
|
data/lib/meme_captain/meme.rb
CHANGED
@@ -6,7 +6,7 @@ module MemeCaptain
|
|
6
6
|
|
7
7
|
# Create a meme image.
|
8
8
|
# Input can be an IO object or a blob of data.
|
9
|
-
def meme(input,
|
9
|
+
def meme(input, top_text, bottom_text, options={})
|
10
10
|
img = Magick::ImageList.new
|
11
11
|
if input.respond_to?(:read)
|
12
12
|
img.from_blob(input.read)
|
@@ -14,34 +14,53 @@ module MemeCaptain
|
|
14
14
|
img.from_blob(input)
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
:fill => 'white',
|
20
|
-
:font => 'Impact-Regular',
|
21
|
-
:gravity => Magick::CenterGravity,
|
22
|
-
:size => "#{img.page.width * 1.8}x#{img.page.height / 2}",
|
23
|
-
:stroke => 'black',
|
24
|
-
:stroke_width => 2,
|
25
|
-
:background_color => 'none',
|
26
|
-
}.merge(options)
|
27
|
-
|
28
|
-
line1_caption = Magick::Image.read("caption:#{line1.to_s.upcase}") {
|
29
|
-
options.each { |k,v| self.send("#{k}=", v) }
|
30
|
-
}
|
31
|
-
line1_caption[0].resize!(0.5)
|
17
|
+
max_lines = 16
|
18
|
+
super_sample = 2.0
|
32
19
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
line2_caption[0].resize!(0.5)
|
20
|
+
min_pointsize = 12 * super_sample
|
21
|
+
text_width = img.page.width * 0.9 * super_sample
|
22
|
+
text_height = img.page.height / 4.0 * super_sample
|
37
23
|
|
38
|
-
text_layer = Magick::Image.new(
|
24
|
+
text_layer = Magick::Image.new(
|
25
|
+
img.page.width * super_sample, img.page.height * super_sample) {
|
39
26
|
self.background_color = 'none'
|
27
|
+
self.density = 144
|
40
28
|
}
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
29
|
+
|
30
|
+
draw = Magick::Draw.new.extend(Draw)
|
31
|
+
|
32
|
+
draw.fill = 'white'
|
33
|
+
draw.font = 'Impact'
|
34
|
+
|
35
|
+
[
|
36
|
+
[Caption.new(top_text), Magick::NorthGravity],
|
37
|
+
[Caption.new(bottom_text), Magick::SouthGravity],
|
38
|
+
].select { |x| x[0].drawable? }.each do |caption, gravity|
|
39
|
+
wrap_tries = (1..max_lines).map { |num_lines|
|
40
|
+
caption.wrap(num_lines).upcase.annotate_quote
|
41
|
+
}.uniq
|
42
|
+
|
43
|
+
choices = wrap_tries.map do |wrap_try|
|
44
|
+
pointsize, fits = draw.calc_pointsize(
|
45
|
+
text_width, text_height, wrap_try, min_pointsize)
|
46
|
+
|
47
|
+
CaptionChoice.new(fits, pointsize, wrap_try)
|
48
|
+
end
|
49
|
+
|
50
|
+
choice = choices.max
|
51
|
+
|
52
|
+
draw.gravity = gravity
|
53
|
+
draw.pointsize = choice.pointsize
|
54
|
+
|
55
|
+
draw.stroke = 'black'
|
56
|
+
draw.stroke_width = 8
|
57
|
+
draw.annotate text_layer, 0, 0, 0, 0, choice.text
|
58
|
+
|
59
|
+
draw.stroke = 'none'
|
60
|
+
draw.annotate text_layer, 0, 0, 0, 0, choice.text
|
61
|
+
end
|
62
|
+
|
63
|
+
text_layer.resize!(1 / super_sample)
|
45
64
|
|
46
65
|
img.each do |frame|
|
47
66
|
frame.composite!(text_layer, -frame.page.x, -frame.page.y,
|
data/lib/meme_captain/server.rb
CHANGED
@@ -13,6 +13,8 @@ module MemeCaptain
|
|
13
13
|
|
14
14
|
ImageExts = %w{.jpeg .gif .png}
|
15
15
|
|
16
|
+
set :root, File.join(File.dirname(__FILE__), '..', '..')
|
17
|
+
|
16
18
|
get '/' do
|
17
19
|
@u = params[:u]
|
18
20
|
@tt= params[:tt]
|
@@ -32,6 +34,9 @@ module MemeCaptain
|
|
32
34
|
curl = Curl::Easy.perform(params[:u]) do |c|
|
33
35
|
c.useragent = 'Meme Captain http://memecaptain.com/'
|
34
36
|
end
|
37
|
+
unless curl.response_code == 200
|
38
|
+
raise "Error loading source image url #{params[:u]}"
|
39
|
+
end
|
35
40
|
curl.body_str
|
36
41
|
}
|
37
42
|
|
@@ -49,21 +54,23 @@ module MemeCaptain
|
|
49
54
|
end
|
50
55
|
|
51
56
|
get '/g' do
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
57
|
+
begin
|
58
|
+
processed_cache_path = gen(params)
|
59
|
+
|
60
|
+
temp_url = URI(request.url)
|
61
|
+
temp_url.path = processed_cache_path.sub('public', '')
|
62
|
+
temp_url.query = nil
|
63
|
+
|
64
|
+
perm_url = URI(request.url)
|
65
|
+
perm_url.path = '/i'
|
66
|
+
|
67
|
+
[200, { 'Content-Type' => 'application/json' }, {
|
68
|
+
'tempUrl' => temp_url.to_s,
|
69
|
+
'permUrl' => perm_url.to_s,
|
70
|
+
}.to_json]
|
71
|
+
rescue => error
|
72
|
+
[500, { 'Content-Type' => 'text/plain' }, error.to_s]
|
73
|
+
end
|
67
74
|
end
|
68
75
|
|
69
76
|
get '/i' do
|
data/lib/meme_captain.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
require 'meme_captain/caption'
|
2
|
+
require 'meme_captain/caption_choice'
|
3
|
+
require 'meme_captain/draw'
|
1
4
|
require 'meme_captain/file_body'
|
5
|
+
require 'meme_captain/filesystem_cache'
|
2
6
|
require 'meme_captain/meme'
|
3
7
|
require 'meme_captain/mime_type'
|
4
8
|
require 'meme_captain/server'
|
5
|
-
require 'meme_captain/
|
9
|
+
require 'meme_captain/version'
|
data/meme_captain.gemspec
CHANGED
@@ -2,9 +2,12 @@
|
|
2
2
|
|
3
3
|
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
4
4
|
|
5
|
+
require 'meme_captain/version'
|
6
|
+
|
5
7
|
Gem::Specification.new do |s|
|
6
8
|
s.name = 'meme_captain'
|
7
9
|
s.version = '0.0.6'
|
10
|
+
s.version = MemeCaptain::VERSION
|
8
11
|
s.summary = 'create meme images'
|
9
12
|
s.description = s.summary
|
10
13
|
s.homepage = 'https://github.com/mmb/meme_captain'
|
data/public/1.gif
ADDED
Binary file
|
data/public/thumbs.jpg
ADDED
Binary file
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'RMagick'
|
2
|
+
|
3
|
+
# make a combined thumbnail image from a list of images for use in CSS sprites
|
4
|
+
|
5
|
+
puts 'images = ['
|
6
|
+
|
7
|
+
thumbs = Magick::ImageList.new
|
8
|
+
|
9
|
+
ARGV.sort.each do |file|
|
10
|
+
image = Magick::ImageList.new(file)
|
11
|
+
image.resize_to_fit!(0, 50)
|
12
|
+
image.each do |frame|
|
13
|
+
thumbs.push frame
|
14
|
+
puts " ['#{File.basename(file)}', #{frame.columns}],"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
puts ']'
|
19
|
+
|
20
|
+
thumbs.append(false).write('thumbs.jpg')
|
data/views/index.erb
CHANGED
@@ -1,16 +1,28 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
|
-
<html
|
2
|
+
<html lan="en" xmlns:fb="https://www.facebook.com/2008/fbml">
|
3
3
|
|
4
4
|
<head>
|
5
5
|
<title>Meme Captain</title>
|
6
6
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
7
|
+
<style type="text/css">
|
8
|
+
#sourceImages {
|
9
|
+
background-color : #eee;
|
10
|
+
border-top : 1px solid #ccc;
|
11
|
+
border-bottom : 1px solid #ccc;
|
12
|
+
padding : 0em 1em 0.5em 1em;
|
13
|
+
margin : 1em 0em 1em 0em;
|
14
|
+
}
|
15
|
+
|
16
|
+
img.thumb {
|
17
|
+
background-image : url('thumbs.jpg');
|
18
|
+
height : 50px;
|
19
|
+
}
|
20
|
+
</style>
|
7
21
|
</head>
|
8
22
|
|
9
23
|
<body>
|
10
24
|
|
11
|
-
<p><a href="/">Meme Captain</a
|
12
|
-
|
13
|
-
<p>Add text to images from the internet.</p>
|
25
|
+
<p><a href="/">Meme Captain</a> - add text to images from the internet</p>
|
14
26
|
|
15
27
|
<div id="img"></div>
|
16
28
|
|
@@ -42,45 +54,183 @@
|
|
42
54
|
|
43
55
|
</form>
|
44
56
|
|
45
|
-
<
|
46
|
-
|
47
|
-
<
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
57
|
+
<div id="sourceImages">
|
58
|
+
|
59
|
+
<p>Locally hosted source images or search Bing for source images (click thumbnail to use):</p>
|
60
|
+
|
61
|
+
<%
|
62
|
+
|
63
|
+
images = [
|
64
|
+
['all_the_things.jpg', 67],
|
65
|
+
['bear_grylls.jpg', 45],
|
66
|
+
['business_cat.jpg', 50],
|
67
|
+
['courage_wolf.jpg', 50],
|
68
|
+
['dwight_schrute.jpg', 73],
|
69
|
+
['fry.png', 67],
|
70
|
+
['good_guy_greg.jpg', 51],
|
71
|
+
['grandma.jpg', 68],
|
72
|
+
['insanity_wolf.jpg', 50],
|
73
|
+
['internet_husband.jpg', 45],
|
74
|
+
['joseph_ducreux.jpg', 38],
|
75
|
+
['me_gusta.png', 50],
|
76
|
+
['most_interesting.jpg', 40],
|
77
|
+
['ok.png', 50],
|
78
|
+
['philosoraptor.jpg', 50],
|
79
|
+
['rage.png', 50],
|
80
|
+
['sap.jpg', 50],
|
81
|
+
['scumbag_steve.jpg', 50],
|
82
|
+
['seriously.png', 50],
|
83
|
+
['slowpoke.jpg', 50],
|
84
|
+
['success_kid.jpg', 50],
|
85
|
+
['town_crier.jpg', 75],
|
86
|
+
['troll_face.jpg', 55],
|
87
|
+
['trolldad.png', 50],
|
88
|
+
['trolldad_dancing.png', 50],
|
89
|
+
['tyler_durden.jpg', 50],
|
90
|
+
['xzibit.jpg', 77],
|
91
|
+
['y_u_no.jpg', 67],
|
92
|
+
['yao_ming.jpg', 42],
|
93
|
+
]
|
94
|
+
|
95
|
+
pos = 0
|
96
|
+
images.each do |name, offset|
|
97
|
+
%>
|
98
|
+
<img src="1.gif" class="thumb" style="width : <%= offset %>px; background-position : <%= pos %>px 0px;" onClick="$('#u').val('http://memecaptain.com/<%= name %>'); return false;" />
|
99
|
+
<%
|
100
|
+
pos -= offset
|
101
|
+
end
|
102
|
+
%>
|
103
|
+
|
104
|
+
<div id="bingImageResults"></div>
|
54
105
|
|
55
|
-
<
|
56
|
-
|
57
|
-
<
|
106
|
+
<form action="" method="get">
|
107
|
+
<input type="text" id="bingSearch" />
|
108
|
+
<input type="button" id="bingButton" value="Bing Image Search" />
|
109
|
+
</form>
|
58
110
|
|
59
|
-
|
111
|
+
</div>
|
60
112
|
|
61
113
|
<p>by Matthew M. Boedicker <a href="mailto:matthewm@boedicker.org">matthewm@boedicker.org</a></p>
|
62
114
|
|
63
|
-
<p><a href="https://github.com/mmb/meme_captain">source code</a></p>
|
115
|
+
<p><a href="https://github.com/mmb/meme_captain">source code and API</a></p>
|
64
116
|
|
65
117
|
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js"></script>
|
66
118
|
<script>
|
119
|
+
function showBingImages(resp) {
|
120
|
+
var div = $('#bingImageResults'),
|
121
|
+
searchResponse = resp.SearchResponse,
|
122
|
+
image = searchResponse.Image;
|
123
|
+
|
124
|
+
div.empty();
|
125
|
+
|
126
|
+
if (image.Total > 0) {
|
127
|
+
$.each(image.Results, function (i, img) {
|
128
|
+
div.append($('<img />').attr('src', img.Thumbnail.Url).click(
|
129
|
+
function () { $('#u').val(img.MediaUrl); }
|
130
|
+
));
|
131
|
+
});
|
132
|
+
} else {
|
133
|
+
div.append($('<p />').append(
|
134
|
+
'No results for "' + searchResponse.Query.SearchTerms + '".'));
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
function bingImageSearch() {
|
139
|
+
var bingSearch = $('#bingSearch'),
|
140
|
+
bingSearchVal = bingSearch.val();
|
141
|
+
|
142
|
+
if (bingSearchVal.match(/[^\s]/)) {
|
143
|
+
bingSearch.val('');
|
144
|
+
|
145
|
+
$.ajax({
|
146
|
+
type : 'GET',
|
147
|
+
url : 'http://api.bing.net/json.aspx',
|
148
|
+
data : {
|
149
|
+
AppId : 'A120380275E87F0071F163210211F0592D0E964C',
|
150
|
+
Query : bingSearchVal + ' imagesize:Medium',
|
151
|
+
Sources : 'Image',
|
152
|
+
Version : '2.0',
|
153
|
+
Market : 'en-us',
|
154
|
+
'Image.Count' : 5,
|
155
|
+
'Image.Offset' : 0,
|
156
|
+
JsonType : 'callback'
|
157
|
+
},
|
158
|
+
dataType : 'jsonp',
|
159
|
+
jsonpCallback : 'showBingImages',
|
160
|
+
jsonp : 'JsonCallback'
|
161
|
+
});
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
67
165
|
$(function () {
|
166
|
+
var bingSearch,
|
167
|
+
bingSearchVal,
|
168
|
+
imgDiv;
|
169
|
+
|
68
170
|
if (window.location.search.match(/u=[^&$]/)) {
|
69
|
-
|
171
|
+
$('#tt').focus();
|
172
|
+
|
173
|
+
imgDiv = $('#img');
|
70
174
|
|
71
175
|
imgDiv.append($('<p />').append('Creating image ...'));
|
72
176
|
|
73
|
-
$.get('/g' + window.location.search, function(data) {
|
177
|
+
$.get('/g' + window.location.search, function (data) {
|
74
178
|
var img = $('<img />').attr('src', data.tempUrl),
|
75
179
|
tempLink = $('<a />').attr('href', data.tempUrl).append(data.tempUrl),
|
76
|
-
permLink = $('<a />').attr('href', data.permUrl).append(data.permUrl)
|
180
|
+
permLink = $('<a />').attr('href', data.permUrl).append(data.permUrl),
|
181
|
+
tweetLink = $('<a />').attr({
|
182
|
+
href : 'http://twitter.com/share',
|
183
|
+
'class' : 'twitter-share-button',
|
184
|
+
'data-count' : 'none',
|
185
|
+
'data-text' : ' ',
|
186
|
+
'data-url' : data.permUrl
|
187
|
+
}).append('Tweet');
|
77
188
|
|
78
189
|
imgDiv.empty().append(
|
79
190
|
img).append(
|
80
191
|
$('<p />').append('Temporary image url: ').append(tempLink)).append(
|
81
|
-
$('<p />').append('Permanent image url: ').append(permLink))
|
192
|
+
$('<p />').append('Permanent image url: ').append(permLink)).append(
|
193
|
+
$('<p />').append('Temporary url not guaranteed to work forever. Permanent url should work as long as source image exists.')).append(
|
194
|
+
$('<p />').append(tweetLink).append($('<script />').attr('src',
|
195
|
+
'http://platform.twitter.com/widgets.js')));
|
196
|
+
|
197
|
+
// Facebook like
|
198
|
+
imgDiv.append(
|
199
|
+
$('<div />').attr('id', 'fb-root')).append(
|
200
|
+
$('<fb:like />').attr({
|
201
|
+
href : data.tempUrl,
|
202
|
+
send : 'true',
|
203
|
+
width : '450',
|
204
|
+
show_faces : 'true',
|
205
|
+
font : ''
|
206
|
+
}));
|
207
|
+
|
208
|
+
window.fbAsyncInit = function () {
|
209
|
+
FB.init({
|
210
|
+
appId : '108445492580525',
|
211
|
+
status : true,
|
212
|
+
cookie : true,
|
213
|
+
xfbml : true
|
214
|
+
});
|
215
|
+
};
|
216
|
+
|
217
|
+
imgDiv.append($('<script />').attr({
|
218
|
+
src : 'http://connect.facebook.net/en_US/all.js'}));
|
219
|
+
}).error(function (j) {
|
220
|
+
imgDiv.empty().append($('<p />').text(j.responseText));
|
82
221
|
});
|
222
|
+
} else {
|
223
|
+
$('#u').focus();
|
83
224
|
}
|
225
|
+
|
226
|
+
$('#bingSearch').keypress(function (event) {
|
227
|
+
if (event.which == 13) {
|
228
|
+
event.preventDefault();
|
229
|
+
bingImageSearch();
|
230
|
+
}
|
231
|
+
});
|
232
|
+
|
233
|
+
$('#bingButton').click(bingImageSearch);
|
84
234
|
});
|
85
235
|
</script>
|
86
236
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: meme_captain
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 7
|
10
|
+
version: 0.0.7
|
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: 2011-
|
18
|
+
date: 2011-11-08 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -114,17 +114,24 @@ extra_rdoc_files: []
|
|
114
114
|
files:
|
115
115
|
- .gitignore
|
116
116
|
- COPYING
|
117
|
-
- README.
|
117
|
+
- README.md
|
118
118
|
- config.ru
|
119
119
|
- img_cache/source/.gitignore
|
120
120
|
- lib/meme_captain.rb
|
121
|
+
- lib/meme_captain/caption.rb
|
122
|
+
- lib/meme_captain/caption_choice.rb
|
123
|
+
- lib/meme_captain/draw.rb
|
121
124
|
- lib/meme_captain/file_body.rb
|
122
125
|
- lib/meme_captain/filesystem_cache.rb
|
123
126
|
- lib/meme_captain/meme.rb
|
124
127
|
- lib/meme_captain/mime_type.rb
|
125
128
|
- lib/meme_captain/server.rb
|
129
|
+
- lib/meme_captain/version.rb
|
126
130
|
- meme_captain.gemspec
|
131
|
+
- public/1.gif
|
132
|
+
- public/thumbs.jpg
|
127
133
|
- public/tmp/.gitignore
|
134
|
+
- script/thumb_sprites.rb
|
128
135
|
- views/index.erb
|
129
136
|
has_rdoc: true
|
130
137
|
homepage: https://github.com/mmb/meme_captain
|
data/README.textile
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
Ruby gem to create meme images (images with text added at the top and bottom).
|
2
|
-
|
3
|
-
Also includes Sinatra app that exposes API over HTTP which is currently
|
4
|
-
running "http://memecaptain.com/":http://memecaptain.com/
|
5
|
-
|
6
|
-
Works with animated gifs.
|
7
|
-
|
8
|
-
<pre>
|
9
|
-
<code>
|
10
|
-
require 'meme_captain'
|
11
|
-
|
12
|
-
require 'open-uri'
|
13
|
-
|
14
|
-
open('http://image_from_web_or_local_file.jpg', 'rb') do |f|
|
15
|
-
i = MemeCaptain.meme(f, 'test', '1 2 3')
|
16
|
-
i.display
|
17
|
-
i.write('out.jpg')
|
18
|
-
end
|
19
|
-
</code>
|
20
|
-
</pre>
|