meme_captain 0.1.0 → 0.1.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,403 @@
1
+ var MEMECAPTAIN = (function (window, $, fabric) {
2
+ "use strict";
3
+
4
+ var my = {},
5
+
6
+ bingAppId = 'A120380275E87F0071F163210211F0592D0E964C',
7
+ facebookAppId = '108445492580525',
8
+ googleApiKey = 'ABQIAAAA-E0uJIHoMJX6M6atCgYANRS1DzXPXMqKnKNRJm2Z_PRWxvtqGBSOvBqyXOwxGZU5jLxExg_5ym69rw',
9
+ genUrl = '/g',
10
+ genDataType = genUrl === '/g' ? 'json' : 'jsonp',
11
+ imageMaxSide = 800;
12
+
13
+ function setSourceUrl(sourceUrl) {
14
+ $('#u').val(sourceUrl).addClass('attn');
15
+ $('#t1x,#t1y,#t1w,#t1h,#t2x,#t2y,#t2w,#t2h').val('');
16
+ $('#positionTextCanvasDiv').empty();
17
+ $('#positionTable').hide();
18
+ }
19
+
20
+ // scale width and height so that neither is larger than maxSide
21
+ function resizeToFit(width, height, maxSide) {
22
+ var result = {
23
+ width : width,
24
+ height : height
25
+ };
26
+
27
+ if (width > maxSide) {
28
+ result.width = maxSide;
29
+ result.height = Math.round(height * (maxSide / width));
30
+ }
31
+
32
+ if (result.height > maxSide) {
33
+ result.width = Math.round(
34
+ result.width * (maxSide / result.height)
35
+ );
36
+ result.height = maxSide;
37
+ }
38
+
39
+ return result;
40
+ }
41
+
42
+ // build an image search result thumbnail
43
+ function searchThumbnail(thumbnailUrl, imgUrl, imgWidth, imgHeight) {
44
+ var scaledDimensions = resizeToFit(imgWidth, imgHeight, imageMaxSide),
45
+ title = scaledDimensions.width + ' x ' + scaledDimensions.height,
46
+ thumbnailImage = $('<img />').attr({
47
+ src : thumbnailUrl,
48
+ title : title,
49
+ width : scaledDimensions.width / 4.0,
50
+ height : scaledDimensions.height / 4.0
51
+ }).addClass('thumb');
52
+
53
+ return thumbnailImage.click(function () { setSourceUrl(imgUrl); });
54
+ }
55
+
56
+ function showBingImages(resp) {
57
+ var div = $('#imageSearchResults'),
58
+ searchResponse = resp.SearchResponse,
59
+ image = searchResponse.Image;
60
+
61
+ if (image.Total > 0) {
62
+ $.each(image.Results, function (i, img) {
63
+ div.append(searchThumbnail(
64
+ img.Thumbnail.Url,
65
+ img.MediaUrl,
66
+ img.Width,
67
+ img.Height
68
+ ));
69
+ });
70
+ } else {
71
+ div.append($('<p />').append(
72
+ 'No Bing results for "' + searchResponse.Query.SearchTerms + '".'
73
+ ));
74
+ }
75
+ }
76
+
77
+ function showGoogleImages(resp) {
78
+ var div = $('#imageSearchResults'),
79
+ searchResults = resp.responseData.results;
80
+
81
+ if (searchResults.length > 0) {
82
+ $.each(searchResults, function (i, img) {
83
+ div.append(searchThumbnail(img.tbUrl, img.unescapedUrl,
84
+ parseInt(img.width, 10), parseInt(img.height, 10)));
85
+ });
86
+ } else {
87
+ div.append($('<p />').append('No Google results.'));
88
+ }
89
+ }
90
+
91
+ function loadSourceImages() {
92
+ $.get('source_images.json', function (data) {
93
+ var pos = 0,
94
+ div = $('#localSourceImages');
95
+
96
+ $.each(data.images, function (i, img) {
97
+ div.append($('<img />').attr('src',
98
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAAXNSR0IArs4c6QAAAAJiS0dEAACqjSMyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AIBAQMqmgCy0wAAAAtJREFUCNdjYGAAAAADAAEg1ZTHAAAAAElFTkSuQmCC'
99
+ ).addClass('thumb').css({
100
+ 'background-image' : 'url(' + data.thumbSpritesUrl + ')',
101
+ 'background-position' : pos + 'px 0px',
102
+ height : data.thumbHeight,
103
+ width : img.thumbWidth + 'px'
104
+ }).click(function () { setSourceUrl(img.url); }));
105
+
106
+ pos -= img.thumbWidth;
107
+ });
108
+ });
109
+ }
110
+
111
+ function imageSearch() {
112
+ var imageSearchInput = $('#imageSearch'),
113
+ imageSearchVal = imageSearchInput.val();
114
+
115
+ if (imageSearchVal.match(/[^\s]/)) {
116
+ imageSearchInput.val('');
117
+ $('#imageSearchResults').empty();
118
+
119
+ $.ajax({
120
+ type : 'GET',
121
+ url : 'http://api.bing.net/json.aspx',
122
+ data : {
123
+ AppId : bingAppId,
124
+ Query : imageSearchVal,
125
+ Sources : 'Image',
126
+ Version : '2.0',
127
+ Market : 'en-us',
128
+ 'Image.Count' : 5,
129
+ 'Image.Filters' : 'Size:Large',
130
+ 'Image.Offset' : 0,
131
+ JsonType : 'callback'
132
+ },
133
+ dataType : 'jsonp',
134
+ jsonp : 'JsonCallback',
135
+ success : showBingImages
136
+ });
137
+
138
+ $.ajax({
139
+ type : 'GET',
140
+ url : 'http://ajax.googleapis.com/ajax/services/search/images',
141
+ data : {
142
+ imgsz : 'large',
143
+ key : googleApiKey,
144
+ q : imageSearchVal,
145
+ rsz : '5',
146
+ v : '1.0'
147
+ },
148
+ dataType : 'jsonp',
149
+ success : showGoogleImages
150
+ });
151
+ }
152
+ }
153
+
154
+ function positionText() {
155
+ var imageUrl = $('#u').val(),
156
+ canvas;
157
+
158
+ if (imageUrl.length > 0) {
159
+ $('#positionTable').show();
160
+ $('#positionTextCanvasDiv').empty().append($('<canvas />').attr({
161
+ id : 'positionTextCanvas'
162
+ }));
163
+
164
+ canvas = new fabric.Canvas('positionTextCanvas');
165
+
166
+ canvas.setBackgroundImage(imageUrl, function () {
167
+ var canvasSize = resizeToFit(canvas.backgroundImage.width,
168
+ canvas.backgroundImage.height, imageMaxSide),
169
+ textWidth,
170
+ textHeight,
171
+ rect1,
172
+ rect2;
173
+
174
+ canvas.setWidth(canvasSize.width);
175
+ canvas.setHeight(canvasSize.height);
176
+
177
+ canvas.renderAll.bind(canvas);
178
+
179
+ textWidth = 0.9 * canvas.getWidth();
180
+ textHeight = 0.25 * canvas.getHeight();
181
+
182
+ rect1 = new fabric.Rect({
183
+ top : textHeight / 2.0,
184
+ left : canvas.getWidth() / 2.0,
185
+ width : textWidth,
186
+ height : textHeight,
187
+ fill : 'red'
188
+ });
189
+
190
+ rect1.name = '1';
191
+ rect1.set('lockRotation', true);
192
+
193
+ canvas.add(rect1);
194
+
195
+ rect2 = new fabric.Rect({
196
+ top : canvas.getHeight() - (textHeight / 2.0),
197
+ left : canvas.getWidth() / 2.0,
198
+ width : textWidth,
199
+ height : textHeight,
200
+ fill: 'red'
201
+ });
202
+
203
+ rect2.name = '2';
204
+ rect2.set('lockRotation', true);
205
+
206
+ canvas.add(rect2);
207
+
208
+ canvas.observe('object:moving', function (o) {
209
+ var target = o.memo.target,
210
+
211
+ halfWidth = target.getWidth() / 2,
212
+ leftSide = Math.round(target.getLeft() - halfWidth),
213
+ rightSide = Math.round(target.getLeft() + halfWidth),
214
+
215
+ halfHeight = target.getHeight() / 2,
216
+ topSide = Math.round(target.getTop() - halfHeight),
217
+ bottomSide = Math.round(target.getTop() + halfHeight);
218
+
219
+ if (leftSide < 0) {
220
+ target.setLeft(halfWidth);
221
+ }
222
+ if (rightSide > canvas.getWidth()) {
223
+ target.setLeft(canvas.getWidth() - halfWidth);
224
+ }
225
+ if (topSide < 0) {
226
+ target.setTop(halfHeight);
227
+ }
228
+ if (bottomSide > canvas.getHeight()) {
229
+ target.setTop(canvas.getHeight() - halfHeight);
230
+ }
231
+ });
232
+
233
+ canvas.observe('object:modified', function (o) {
234
+ var target = o.memo.target;
235
+
236
+ $('#t' + target.name + 'x').val(
237
+ Math.round(target.getLeft() - (target.getWidth() / 2))
238
+ );
239
+ $('#t' + target.name + 'y').val(
240
+ Math.round(target.getTop() - (target.getHeight() / 2))
241
+ );
242
+ $('#t' + target.name + 'w').val(Math.round(target.getWidth()));
243
+ $('#t' + target.name + 'h').val(Math.round(target.getHeight()));
244
+ });
245
+
246
+ canvas.fire('object:modified', { target : rect1 });
247
+ canvas.fire('object:modified', { target : rect2 });
248
+ });
249
+ }
250
+ }
251
+
252
+ function redditLink(url) {
253
+ return $('<a />').attr('href',
254
+ 'http://www.reddit.com/submit?url=' +
255
+ encodeURIComponent(url)).append('reddit');
256
+ }
257
+
258
+ // build a Twitter tweet link for a url
259
+ function tweetLink(url) {
260
+ return $('<a />').attr({
261
+ href : 'http://twitter.com/share',
262
+ 'class' : 'twitter-share-button',
263
+ 'data-count' : 'none',
264
+ 'data-text' : ' ',
265
+ 'data-url' : url
266
+ }).append('Tweet');
267
+ }
268
+
269
+ // build Twitter tweet script tag
270
+ function tweetScript() {
271
+ return $('<script />').attr(
272
+ 'src',
273
+ 'http://platform.twitter.com/widgets.js'
274
+ );
275
+ }
276
+
277
+ // build Facebook Like div for a url
278
+ function facebookLikeDiv(url) {
279
+ return $('<div />').attr('id', 'fb-root').addClass('share').append(
280
+ $('<fb:like />').attr({
281
+ href : url,
282
+ send : 'true',
283
+ width : '450',
284
+ show_faces : 'true',
285
+ font : ''
286
+ })
287
+ );
288
+ }
289
+
290
+ // Facebook asynchronous init
291
+ function fbAsyncInit() {
292
+ FB.init({
293
+ appId : facebookAppId,
294
+ status : true,
295
+ cookie : true,
296
+ xfbml : true
297
+ });
298
+ }
299
+
300
+ function facebookScript() {
301
+ return $('<script />').attr({
302
+ src : 'http://connect.facebook.net/en_US/all.js'
303
+ });
304
+ }
305
+
306
+ function createImage() {
307
+ var createdImageDiv,
308
+ uVal = $('#u').val();
309
+
310
+ if (uVal !== '') {
311
+ createdImageDiv = $('#createdImage');
312
+
313
+ $('#positionTextCanvasDiv').empty();
314
+
315
+ createdImageDiv.prepend($('<p />').append('Creating image ...'));
316
+
317
+ $.get(genUrl, {
318
+ u : uVal,
319
+ t1 : $('#t1').val(),
320
+ t2 : $('#t2').val(),
321
+
322
+ t1x : $('#t1x').val(),
323
+ t1y : $('#t1y').val(),
324
+ t1w : $('#t1w').val(),
325
+ t1h : $('#t1h').val(),
326
+
327
+ t2x : $('#t2x').val(),
328
+ t2y : $('#t2y').val(),
329
+ t2w : $('#t2w').val(),
330
+ t2h : $('#t2h').val()
331
+ }, function (data) {
332
+ var img = $('<img />').attr('src', data.imageUrl),
333
+ imgLink = $('<a />').attr('href', data.imageUrl).append(
334
+ data.imageUrl
335
+ ),
336
+ templateLink = $('<a />').attr(
337
+ 'href',
338
+ data.templateUrl
339
+ ).append(data.templateUrl);
340
+
341
+ createdImageDiv.empty().append(img).append(
342
+ $('<p />').append('Image: ').append(imgLink)
343
+ );
344
+
345
+ // template link
346
+ createdImageDiv.append(
347
+ $('<p />').append('To make more with this source image: ').
348
+ append(templateLink)
349
+ );
350
+
351
+ // reddit link
352
+ createdImageDiv.append($('<div />').addClass(
353
+ 'share'
354
+ ).append(redditLink(data.imageUrl)));
355
+
356
+ // tweet link
357
+ createdImageDiv.append($('<div />').addClass('share').append(
358
+ tweetLink(data.imageUrl)
359
+ ).append(tweetScript()));
360
+
361
+ // Facebook like
362
+ createdImageDiv.append(facebookLikeDiv(data.imageUrl));
363
+ window.fbAsyncInit = fbAsyncInit;
364
+ createdImageDiv.append(facebookScript());
365
+ }, genDataType).error(function (j) {
366
+ createdImageDiv.empty().append($('<p />').text(j.responseText));
367
+ });
368
+ }
369
+ }
370
+
371
+ my.init = function () {
372
+ loadSourceImages();
373
+
374
+ $('#imageSearch').keypress(function (event) {
375
+ if (event.which === 13) {
376
+ event.preventDefault();
377
+ imageSearch();
378
+ }
379
+ });
380
+
381
+ $('#imageSearchButton').click(imageSearch);
382
+
383
+ $('#positionTextButton').click(positionText);
384
+
385
+ $('#createImageButton').click(createImage);
386
+
387
+ $('#upload').change(function () {
388
+ $('#uploadSubmit').removeAttr('disabled');
389
+ });
390
+
391
+ // highlight input fields that have been preloaded from the query string
392
+ $(":text[value!='']").addClass('attn');
393
+
394
+ if (!window.CanvasRenderingContext2D) {
395
+ $('.hasCanvas').hide();
396
+ $('.noCanvas').show();
397
+ }
398
+ };
399
+
400
+ return my;
401
+ }(window, $, fabric));
402
+
403
+ $(MEMECAPTAIN.init);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "thumbHeight": 50,
3
- "thumbSpritesUrl": "http://memecaptain.com/thumbs.jpg",
3
+ "thumbSpritesUrl": "http://memecaptain.com/thumbs_1334973608.jpg",
4
4
  "images": [
5
5
  {
6
6
  "url": "http://memecaptain.com/all_the_things.jpg",
@@ -10,10 +10,26 @@
10
10
  "url": "http://memecaptain.com/all_the_things2.jpg",
11
11
  "thumbWidth": 67
12
12
  },
13
+ {
14
+ "url": "http://memecaptain.com/annoying_facebook_girl.jpg",
15
+ "thumbWidth": 50
16
+ },
13
17
  {
14
18
  "url": "http://memecaptain.com/aw_yeah.png",
15
19
  "thumbWidth": 50
16
20
  },
21
+ {
22
+ "url": "http://memecaptain.com/bad_joke_eel.jpg",
23
+ "thumbWidth": 67
24
+ },
25
+ {
26
+ "url": "http://memecaptain.com/bad_luck_brian.jpg",
27
+ "thumbWidth": 42
28
+ },
29
+ {
30
+ "url": "http://memecaptain.com/bad_time.jpg",
31
+ "thumbWidth": 60
32
+ },
17
33
  {
18
34
  "url": "http://memecaptain.com/bear_grylls.jpg",
19
35
  "thumbWidth": 45
@@ -26,6 +42,10 @@
26
42
  "url": "http://memecaptain.com/business_cat.jpg",
27
43
  "thumbWidth": 50
28
44
  },
45
+ {
46
+ "url": "http://memecaptain.com/conspiracy_keanu.jpg",
47
+ "thumbWidth": 50
48
+ },
29
49
  {
30
50
  "url": "http://memecaptain.com/cool_story_bro.jpg",
31
51
  "thumbWidth": 72
@@ -38,6 +58,10 @@
38
58
  "url": "http://memecaptain.com/dwight_schrute.jpg",
39
59
  "thumbWidth": 73
40
60
  },
61
+ {
62
+ "url": "http://memecaptain.com/first_world_problems.jpg",
63
+ "thumbWidth": 72
64
+ },
41
65
  {
42
66
  "url": "http://memecaptain.com/fry.png",
43
67
  "thumbWidth": 67
@@ -62,6 +86,10 @@
62
86
  "url": "http://memecaptain.com/joseph_ducreux.jpg",
63
87
  "thumbWidth": 38
64
88
  },
89
+ {
90
+ "url": "http://memecaptain.com/laundry_room_viking.jpg",
91
+ "thumbWidth": 67
92
+ },
65
93
  {
66
94
  "url": "http://memecaptain.com/me_gusta.png",
67
95
  "thumbWidth": 50
@@ -74,10 +102,22 @@
74
102
  "url": "http://memecaptain.com/ned_stark.jpg",
75
103
  "thumbWidth": 55
76
104
  },
105
+ {
106
+ "url": "http://memecaptain.com/neil_degrasse_tyson.png",
107
+ "thumbWidth": 50
108
+ },
109
+ {
110
+ "url": "http://memecaptain.com/not_bad.png",
111
+ "thumbWidth": 50
112
+ },
77
113
  {
78
114
  "url": "http://memecaptain.com/ok.png",
79
115
  "thumbWidth": 50
80
116
  },
117
+ {
118
+ "url": "http://memecaptain.com/paranoid_parrot.jpg",
119
+ "thumbWidth": 50
120
+ },
81
121
  {
82
122
  "url": "http://memecaptain.com/philosoraptor.jpg",
83
123
  "thumbWidth": 50
@@ -86,6 +126,10 @@
86
126
  "url": "http://memecaptain.com/rage.png",
87
127
  "thumbWidth": 50
88
128
  },
129
+ {
130
+ "url": "http://memecaptain.com/ron_swanson.jpg",
131
+ "thumbWidth": 73
132
+ },
89
133
  {
90
134
  "url": "http://memecaptain.com/sap.jpg",
91
135
  "thumbWidth": 50
@@ -102,10 +146,22 @@
102
146
  "url": "http://memecaptain.com/slowpoke.jpg",
103
147
  "thumbWidth": 50
104
148
  },
149
+ {
150
+ "url": "http://memecaptain.com/socially_awesome_awkward_penguin.jpg",
151
+ "thumbWidth": 50
152
+ },
105
153
  {
106
154
  "url": "http://memecaptain.com/success_kid.jpg",
107
155
  "thumbWidth": 50
108
156
  },
157
+ {
158
+ "url": "http://memecaptain.com/the_more_you_know.jpg",
159
+ "thumbWidth": 76
160
+ },
161
+ {
162
+ "url": "http://memecaptain.com/ti_duck.jpg",
163
+ "thumbWidth": 50
164
+ },
109
165
  {
110
166
  "url": "http://memecaptain.com/town_crier.jpg",
111
167
  "thumbWidth": 75
@@ -126,6 +182,10 @@
126
182
  "url": "http://memecaptain.com/tyler_durden.jpg",
127
183
  "thumbWidth": 50
128
184
  },
185
+ {
186
+ "url": "http://memecaptain.com/wonka.jpg",
187
+ "thumbWidth": 50
188
+ },
129
189
  {
130
190
  "url": "http://memecaptain.com/xzibit.jpg",
131
191
  "thumbWidth": 77
@@ -137,6 +197,10 @@
137
197
  {
138
198
  "url": "http://memecaptain.com/yao_ming.jpg",
139
199
  "thumbWidth": 42
200
+ },
201
+ {
202
+ "url": "http://memecaptain.com/you_should_feel_bad.jpg",
203
+ "thumbWidth": 67
140
204
  }
141
205
  ]
142
206
  }
data/public/thumbs.jpg CHANGED
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,33 +1,76 @@
1
- require 'json'
2
- require 'RMagick'
1
+ #!/usr/bin/env ruby
3
2
 
4
3
  # make a combined thumbnail image from a list of images for use in CSS sprites
5
4
  # generate source images description json
6
5
 
7
- url_prefix = 'http://memecaptain.com/'
6
+ require 'json'
7
+ require 'optparse'
8
+
9
+ require 'RMagick'
10
+
11
+ OPTIONS = {
12
+ :json_file => 'source_images.json',
13
+ :source_url_prefix => '',
14
+ :thumb_file => "thumbs_#{Time.now.to_i}.jpg",
15
+ :thumb_height => 50,
16
+ :url_root => 'http://memecaptain.com/',
17
+ }
18
+
19
+ OptionParser.new do |opts|
20
+ opts.banner = 'usage: thumb_sprites.rb [options] FILE...'
21
+
22
+ opts.on('-j', '--json-file JSON_FILE', 'output JSON file path') do |j|
23
+ OPTIONS[:json_file] = j
24
+ end
25
+
26
+ opts.on('-s', '--source-url-prefix SOURCE_URL_PREFIX',
27
+ 'source image URL prefix') do |s|
28
+ OPTIONS[:source_url_prefix] = s
29
+ end
30
+
31
+ opts.on('-t', '--thumb-height THUMB_HEIGHT',
32
+ 'thumbnail height in pixels') do |t|
33
+ OPTIONS[:thumb_height] = t
34
+ end
8
35
 
9
- data = {
10
- :thumbHeight => 50,
11
- :thumbSpritesUrl => "#{url_prefix}thumbs.jpg",
36
+ opts.on('-u', '--url-root URL_ROOT', 'URL root') do |u|
37
+ OPTIONS[:url_root] = u
38
+ end
39
+ end.parse!
40
+
41
+ DATA = {
42
+ :thumbHeight => OPTIONS[:thumb_height],
43
+ :thumbSpritesUrl => "#{OPTIONS[:url_root]}#{OPTIONS[:thumb_file]}",
12
44
  :images => [],
13
45
  }
14
46
 
15
- thumbs = Magick::ImageList.new
47
+ puts <<eos
48
+ source URL prefix: #{OPTIONS[:source_url_prefix]}
49
+ thumbnail height: #{OPTIONS[:thumb_height]}
50
+ URL root: #{OPTIONS[:url_root]}
51
+
52
+ eos
53
+
54
+ THUMBS = Magick::ImageList.new
16
55
 
17
56
  ARGV.sort.each do |file|
57
+ puts " processing #{file}"
18
58
  image = Magick::ImageList.new(file)
19
- image.resize_to_fit!(0, data[:thumbHeight])
59
+ image.resize_to_fit!(0, DATA[:thumbHeight])
20
60
  image.each do |frame|
21
- thumbs.push frame
22
- data[:images] << {
23
- :url => "#{url_prefix}#{File.basename(file)}",
61
+ THUMBS.push frame
62
+ DATA[:images] << {
63
+ :url =>
64
+ "#{OPTIONS[:url_root]}#{OPTIONS[:source_url_prefix]}#{File.basename(file)}",
24
65
  :thumbWidth => frame.columns
25
66
  }
26
67
  end
27
68
  end
28
69
 
29
- open('source_images.json', 'w') do |f|
30
- f.write(JSON.pretty_generate(data))
70
+ open(OPTIONS[:json_file], 'w') do |f|
71
+ f.write(JSON.pretty_generate(DATA))
72
+ puts "wrote #{OPTIONS[:json_file]}"
31
73
  end
32
74
 
33
- thumbs.append(false).write('thumbs.jpg')
75
+ THUMBS.append(false).write(OPTIONS[:thumb_file])
76
+ puts "wrote #{OPTIONS[:thumb_file]}"