corkboard 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. data/.env.template +1 -0
  2. data/.gitignore +15 -0
  3. data/.rspec.template +2 -0
  4. data/.rvmrc.template +4 -0
  5. data/.wiprc +0 -0
  6. data/.yardopts +1 -0
  7. data/Gemfile +15 -0
  8. data/Gemfile.lock +223 -0
  9. data/app/assets/images/corkboard/.gitkeep +0 -0
  10. data/corkboard.gemspec +41 -0
  11. data/lib/corkboard/version.rb +1 -1
  12. data/script/rails +8 -0
  13. data/spec/controllers/corkboard/application_controller_spec.rb +7 -0
  14. data/spec/controllers/corkboard/authorizations_controller_spec.rb +26 -0
  15. data/spec/controllers/corkboard/board_controller_spec.rb +29 -0
  16. data/spec/controllers/corkboard/posts_controller_spec.rb +7 -0
  17. data/spec/corkboard/client_spec.rb +52 -0
  18. data/spec/corkboard/clients/instagram_spec.rb +30 -0
  19. data/spec/corkboard/engine_spec.rb +11 -0
  20. data/spec/corkboard/provider_spec.rb +28 -0
  21. data/spec/corkboard/providers/instagram_spec.rb +44 -0
  22. data/spec/corkboard/publishers/mock_spec.rb +12 -0
  23. data/spec/corkboard/publishers/pusher_spec.rb +12 -0
  24. data/spec/corkboard/service/config_spec.rb +12 -0
  25. data/spec/corkboard_spec.rb +136 -0
  26. data/spec/dummy/README.rdoc +261 -0
  27. data/spec/dummy/Rakefile +7 -0
  28. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  29. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  30. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  31. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  32. data/spec/dummy/app/mailers/.gitkeep +0 -0
  33. data/spec/dummy/app/models/.gitkeep +0 -0
  34. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  35. data/spec/dummy/config.ru +4 -0
  36. data/spec/dummy/config/application.rb +65 -0
  37. data/spec/dummy/config/boot.rb +10 -0
  38. data/spec/dummy/config/database.yml +25 -0
  39. data/spec/dummy/config/environment.rb +5 -0
  40. data/spec/dummy/config/environments/development.rb +37 -0
  41. data/spec/dummy/config/environments/production.rb +67 -0
  42. data/spec/dummy/config/environments/test.rb +37 -0
  43. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  44. data/spec/dummy/config/initializers/corkboard.rb +45 -0
  45. data/spec/dummy/config/initializers/inflections.rb +15 -0
  46. data/spec/dummy/config/initializers/jasmine.rb +5 -0
  47. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  48. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  49. data/spec/dummy/config/initializers/session_store.rb +8 -0
  50. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  51. data/spec/dummy/config/locales/en.yml +5 -0
  52. data/spec/dummy/config/routes.rb +3 -0
  53. data/spec/dummy/db/.gitkeep +0 -0
  54. data/spec/dummy/db/migrate/20121212222912_create_corkboard_authorizations.corkboard.rb +18 -0
  55. data/spec/dummy/db/schema.rb +31 -0
  56. data/spec/dummy/lib/assets/.gitkeep +0 -0
  57. data/spec/dummy/log/.gitkeep +0 -0
  58. data/spec/dummy/public/404.html +26 -0
  59. data/spec/dummy/public/422.html +26 -0
  60. data/spec/dummy/public/500.html +25 -0
  61. data/spec/dummy/public/favicon.ico +0 -0
  62. data/spec/dummy/script/rails +6 -0
  63. data/spec/helpers/corkboard/application_helper_spec.rb +7 -0
  64. data/spec/javascripts/fixtures/board.html +3 -0
  65. data/spec/javascripts/helpers/jasmine-jquery.js +340 -0
  66. data/spec/javascripts/spec.css +3 -0
  67. data/spec/javascripts/spec.js +5 -0
  68. data/spec/javascripts/spec_helper.js +41 -0
  69. data/spec/javascripts/specs/corkboard/app/board_spec.js +68 -0
  70. data/spec/javascripts/specs/corkboard/base_spec.js +17 -0
  71. data/spec/javascripts/specs/corkboard/lib/publisher_spec.js +101 -0
  72. data/spec/javascripts/specs/corkboard/lib/weighted_randomizer_spec.js +23 -0
  73. data/spec/javascripts/specs/corkboard_spec.js +45 -0
  74. data/spec/javascripts/support/jasmine.yml +75 -0
  75. data/spec/javascripts/support/jasmine_config.rb +1 -0
  76. data/spec/models/corkboard/authorization_spec.rb +7 -0
  77. data/spec/models/corkboard/post_spec.rb +7 -0
  78. data/spec/models/corkboard/subscription_spec.rb +7 -0
  79. data/spec/requests/authorizations_spec.rb +39 -0
  80. data/spec/requests/board_spec.rb +70 -0
  81. data/spec/routing/authorizations_routing_spec.rb +25 -0
  82. data/spec/routing/board_routing_spec.rb +7 -0
  83. data/spec/routing/posts_routing_spec.rb +15 -0
  84. data/spec/spec_helper.rb +51 -0
  85. data/spec/support/helpers/config_helpers.rb +26 -0
  86. data/spec/support/helpers/controller_helpers.rb +12 -0
  87. data/spec/support/helpers/post_helpers.rb +119 -0
  88. data/vendor/assets/javascripts/jquery.masonry-extensions.js +203 -0
  89. data/vendor/assets/javascripts/jquery.masonry.js +499 -0
  90. data/vendor/assets/javascripts/modernizr.js +821 -0
  91. data/vendor/assets/javascripts/pusher.js +48 -0
  92. metadata +176 -11
@@ -0,0 +1,499 @@
1
+ /**
2
+ * jQuery Masonry v2.1.05
3
+ * A dynamic layout plugin for jQuery
4
+ * The flip-side of CSS Floats
5
+ * http://masonry.desandro.com
6
+ *
7
+ * Licensed under the MIT license.
8
+ * Copyright 2012 David DeSandro
9
+ */
10
+
11
+ /*jshint browser: true, curly: true, eqeqeq: true, forin: false, immed: false, newcap: true, noempty: true, strict: true, undef: true */
12
+ /*global jQuery: false */
13
+
14
+ (function( window, $, undefined ){
15
+
16
+ 'use strict';
17
+
18
+ /*
19
+ * smartresize: debounced resize event for jQuery
20
+ *
21
+ * latest version and complete README available on Github:
22
+ * https://github.com/louisremi/jquery.smartresize.js
23
+ *
24
+ * Copyright 2011 @louis_remi
25
+ * Licensed under the MIT license.
26
+ */
27
+
28
+ var $event = $.event,
29
+ resizeTimeout;
30
+
31
+ $event.special.smartresize = {
32
+ setup: function() {
33
+ $(this).bind( "resize", $event.special.smartresize.handler );
34
+ },
35
+ teardown: function() {
36
+ $(this).unbind( "resize", $event.special.smartresize.handler );
37
+ },
38
+ handler: function( event, execAsap ) {
39
+ // Save the context
40
+ var context = this,
41
+ args = arguments;
42
+
43
+ // set correct event type
44
+ event.type = "smartresize";
45
+
46
+ if ( resizeTimeout ) { clearTimeout( resizeTimeout ); }
47
+ resizeTimeout = setTimeout(function() {
48
+ $.event.handle.apply( context, args );
49
+ }, execAsap === "execAsap"? 0 : 100 );
50
+ }
51
+ };
52
+
53
+ $.fn.smartresize = function( fn ) {
54
+ return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] );
55
+ };
56
+
57
+
58
+
59
+ // ========================= Masonry ===============================
60
+
61
+
62
+ // our "Widget" object constructor
63
+ $.Mason = function( options, element ){
64
+ this.element = $( element );
65
+
66
+ this._create( options );
67
+ this._init();
68
+ };
69
+
70
+ $.Mason.settings = {
71
+ isResizable: true,
72
+ isAnimated: false,
73
+ animationOptions: {
74
+ queue: false,
75
+ duration: 500
76
+ },
77
+ gutterWidth: 0,
78
+ isRTL: false,
79
+ isFitWidth: false,
80
+ containerStyle: {
81
+ position: 'relative'
82
+ }
83
+ };
84
+
85
+ $.Mason.prototype = {
86
+
87
+ _filterFindBricks: function( $elems ) {
88
+ var selector = this.options.itemSelector;
89
+ // if there is a selector
90
+ // filter/find appropriate item elements
91
+ return !selector ? $elems : $elems.filter( selector ).add( $elems.find( selector ) );
92
+ },
93
+
94
+ _getBricks: function( $elems ) {
95
+ var $bricks = this._filterFindBricks( $elems )
96
+ .css({ position: 'absolute' })
97
+ .addClass('masonry-brick');
98
+ return $bricks;
99
+ },
100
+
101
+ // sets up widget
102
+ _create : function( options ) {
103
+
104
+ this.options = $.extend( true, {}, $.Mason.settings, options );
105
+ this.styleQueue = [];
106
+
107
+ // get original styles in case we re-apply them in .destroy()
108
+ var elemStyle = this.element[0].style;
109
+ this.originalStyle = {
110
+ // get height
111
+ height: elemStyle.height || ''
112
+ };
113
+ // get other styles that will be overwritten
114
+ var containerStyle = this.options.containerStyle;
115
+ for ( var prop in containerStyle ) {
116
+ this.originalStyle[ prop ] = elemStyle[ prop ] || '';
117
+ }
118
+
119
+ this.element.css( containerStyle );
120
+
121
+ this.horizontalDirection = this.options.isRTL ? 'right' : 'left';
122
+
123
+ this.offset = {
124
+ x: parseInt( this.element.css( 'padding-' + this.horizontalDirection ), 10 ),
125
+ y: parseInt( this.element.css( 'padding-top' ), 10 )
126
+ };
127
+
128
+ this.isFluid = this.options.columnWidth && typeof this.options.columnWidth === 'function';
129
+
130
+ // add masonry class first time around
131
+ var instance = this;
132
+ setTimeout( function() {
133
+ instance.element.addClass('masonry');
134
+ }, 0 );
135
+
136
+ // bind resize method
137
+ if ( this.options.isResizable ) {
138
+ $(window).bind( 'smartresize.masonry', function() {
139
+ instance.resize();
140
+ });
141
+ }
142
+
143
+
144
+ // need to get bricks
145
+ this.reloadItems();
146
+
147
+ },
148
+
149
+ // _init fires when instance is first created
150
+ // and when instance is triggered again -> $el.masonry();
151
+ _init : function( callback ) {
152
+ this._getColumns();
153
+ this._reLayout( callback );
154
+ },
155
+
156
+ option: function( key, value ){
157
+ // set options AFTER initialization:
158
+ // signature: $('#foo').bar({ cool:false });
159
+ if ( $.isPlainObject( key ) ){
160
+ this.options = $.extend(true, this.options, key);
161
+ }
162
+ },
163
+
164
+ // ====================== General Layout ======================
165
+
166
+ // used on collection of atoms (should be filtered, and sorted before )
167
+ // accepts atoms-to-be-laid-out to start with
168
+ layout : function( $bricks, callback ) {
169
+
170
+ // place each brick
171
+ for (var i=0, len = $bricks.length; i < len; i++) {
172
+ this._placeBrick( $bricks[i] );
173
+ }
174
+
175
+ // set the size of the container
176
+ var containerSize = {};
177
+ containerSize.height = Math.max.apply( Math, this.colYs );
178
+ if ( this.options.isFitWidth ) {
179
+ var unusedCols = 0;
180
+ i = this.cols;
181
+ // count unused columns
182
+ while ( --i ) {
183
+ if ( this.colYs[i] !== 0 ) {
184
+ break;
185
+ }
186
+ unusedCols++;
187
+ }
188
+ // fit container to columns that have been used;
189
+ containerSize.width = (this.cols - unusedCols) * this.columnWidth - this.options.gutterWidth;
190
+ }
191
+ this.styleQueue.push({ $el: this.element, style: containerSize });
192
+
193
+ // are we animating the layout arrangement?
194
+ // use plugin-ish syntax for css or animate
195
+ var styleFn = !this.isLaidOut ? 'css' : (
196
+ this.options.isAnimated ? 'animate' : 'css'
197
+ ),
198
+ animOpts = this.options.animationOptions;
199
+
200
+ // process styleQueue
201
+ var obj;
202
+ for (i=0, len = this.styleQueue.length; i < len; i++) {
203
+ obj = this.styleQueue[i];
204
+ obj.$el[ styleFn ]( obj.style, animOpts );
205
+ }
206
+
207
+ // clear out queue for next time
208
+ this.styleQueue = [];
209
+
210
+ // provide $elems as context for the callback
211
+ if ( callback ) {
212
+ callback.call( $bricks );
213
+ }
214
+
215
+ this.isLaidOut = true;
216
+ },
217
+
218
+ // calculates number of columns
219
+ // i.e. this.columnWidth = 200
220
+ _getColumns : function() {
221
+ var container = this.options.isFitWidth ? this.element.parent() : this.element,
222
+ containerWidth = container.width();
223
+
224
+ // use fluid columnWidth function if there
225
+ this.columnWidth = this.isFluid ? this.options.columnWidth( containerWidth ) :
226
+ // if not, how about the explicitly set option?
227
+ this.options.columnWidth ||
228
+ // or use the size of the first item
229
+ this.$bricks.outerWidth(true) ||
230
+ // if there's no items, use size of container
231
+ containerWidth;
232
+
233
+ this.columnWidth += this.options.gutterWidth;
234
+
235
+ this.cols = Math.floor( ( containerWidth + this.options.gutterWidth ) / this.columnWidth );
236
+ this.cols = Math.max( this.cols, 1 );
237
+
238
+ },
239
+
240
+ // layout logic
241
+ _placeBrick: function( brick ) {
242
+ var $brick = $(brick),
243
+ colSpan, groupCount, groupY, groupColY, j;
244
+
245
+ //how many columns does this brick span
246
+ colSpan = Math.ceil( $brick.outerWidth(true) / this.columnWidth );
247
+ colSpan = Math.min( colSpan, this.cols );
248
+
249
+ if ( colSpan === 1 ) {
250
+ // if brick spans only one column, just like singleMode
251
+ groupY = this.colYs;
252
+ } else {
253
+ // brick spans more than one column
254
+ // how many different places could this brick fit horizontally
255
+ groupCount = this.cols + 1 - colSpan;
256
+ groupY = [];
257
+
258
+ // for each group potential horizontal position
259
+ for ( j=0; j < groupCount; j++ ) {
260
+ // make an array of colY values for that one group
261
+ groupColY = this.colYs.slice( j, j+colSpan );
262
+ // and get the max value of the array
263
+ groupY[j] = Math.max.apply( Math, groupColY );
264
+ }
265
+
266
+ }
267
+
268
+ // get the minimum Y value from the columns
269
+ var minimumY = Math.min.apply( Math, groupY ),
270
+ shortCol = 0;
271
+
272
+ // Find index of short column, the first from the left
273
+ for (var i=0, len = groupY.length; i < len; i++) {
274
+ if ( groupY[i] === minimumY ) {
275
+ shortCol = i;
276
+ break;
277
+ }
278
+ }
279
+
280
+ // position the brick
281
+ var position = {
282
+ top: minimumY + this.offset.y
283
+ };
284
+ // position.left or position.right
285
+ position[ this.horizontalDirection ] = this.columnWidth * shortCol + this.offset.x;
286
+ this.styleQueue.push({ $el: $brick, style: position });
287
+
288
+ // apply setHeight to necessary columns
289
+ var setHeight = minimumY + $brick.outerHeight(true),
290
+ setSpan = this.cols + 1 - len;
291
+ for ( i=0; i < setSpan; i++ ) {
292
+ this.colYs[ shortCol + i ] = setHeight;
293
+ }
294
+
295
+ },
296
+
297
+
298
+ resize: function() {
299
+ var prevColCount = this.cols;
300
+ // get updated colCount
301
+ this._getColumns();
302
+ if ( this.isFluid || this.cols !== prevColCount ) {
303
+ // if column count has changed, trigger new layout
304
+ this._reLayout();
305
+ }
306
+ },
307
+
308
+
309
+ _reLayout : function( callback ) {
310
+ // reset columns
311
+ var i = this.cols;
312
+ this.colYs = [];
313
+ while (i--) {
314
+ this.colYs.push( 0 );
315
+ }
316
+ // apply layout logic to all bricks
317
+ this.layout( this.$bricks, callback );
318
+ },
319
+
320
+ // ====================== Convenience methods ======================
321
+
322
+ // goes through all children again and gets bricks in proper order
323
+ reloadItems : function() {
324
+ this.$bricks = this._getBricks( this.element.children() );
325
+ },
326
+
327
+
328
+ reload : function( callback ) {
329
+ this.reloadItems();
330
+ this._init( callback );
331
+ },
332
+
333
+
334
+ // convienence method for working with Infinite Scroll
335
+ appended : function( $content, isAnimatedFromBottom, callback ) {
336
+ if ( isAnimatedFromBottom ) {
337
+ // set new stuff to the bottom
338
+ this._filterFindBricks( $content ).css({ top: this.element.height() });
339
+ var instance = this;
340
+ setTimeout( function(){
341
+ instance._appended( $content, callback );
342
+ }, 1 );
343
+ } else {
344
+ this._appended( $content, callback );
345
+ }
346
+ },
347
+
348
+ _appended : function( $content, callback ) {
349
+ var $newBricks = this._getBricks( $content );
350
+ // add new bricks to brick pool
351
+ this.$bricks = this.$bricks.add( $newBricks );
352
+ this.layout( $newBricks, callback );
353
+ },
354
+
355
+ // removes elements from Masonry widget
356
+ remove : function( $content ) {
357
+ this.$bricks = this.$bricks.not( $content );
358
+ $content.remove();
359
+ },
360
+
361
+ // destroys widget, returns elements and container back (close) to original style
362
+ destroy : function() {
363
+
364
+ this.$bricks
365
+ .removeClass('masonry-brick')
366
+ .each(function(){
367
+ this.style.position = '';
368
+ this.style.top = '';
369
+ this.style.left = '';
370
+ });
371
+
372
+ // re-apply saved container styles
373
+ var elemStyle = this.element[0].style;
374
+ for ( var prop in this.originalStyle ) {
375
+ elemStyle[ prop ] = this.originalStyle[ prop ];
376
+ }
377
+
378
+ this.element
379
+ .unbind('.masonry')
380
+ .removeClass('masonry')
381
+ .removeData('masonry');
382
+
383
+ $(window).unbind('.masonry');
384
+
385
+ }
386
+
387
+ };
388
+
389
+
390
+ // ======================= imagesLoaded Plugin ===============================
391
+ /*!
392
+ * jQuery imagesLoaded plugin v1.1.0
393
+ * http://github.com/desandro/imagesloaded
394
+ *
395
+ * MIT License. by Paul Irish et al.
396
+ */
397
+
398
+
399
+ // $('#my-container').imagesLoaded(myFunction)
400
+ // or
401
+ // $('img').imagesLoaded(myFunction)
402
+
403
+ // execute a callback when all images have loaded.
404
+ // needed because .load() doesn't work on cached images
405
+
406
+ // callback function gets image collection as argument
407
+ // `this` is the container
408
+
409
+ $.fn.imagesLoaded = function( callback ) {
410
+ var $this = this,
411
+ $images = $this.find('img').add( $this.filter('img') ),
412
+ len = $images.length,
413
+ blank = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==',
414
+ loaded = [];
415
+
416
+ function triggerCallback() {
417
+ callback.call( $this, $images );
418
+ }
419
+
420
+ function imgLoaded( event ) {
421
+ var img = event.target;
422
+ if ( img.src !== blank && $.inArray( img, loaded ) === -1 ){
423
+ loaded.push( img );
424
+ if ( --len <= 0 ){
425
+ setTimeout( triggerCallback );
426
+ $images.unbind( '.imagesLoaded', imgLoaded );
427
+ }
428
+ }
429
+ }
430
+
431
+ // if no images, trigger immediately
432
+ if ( !len ) {
433
+ triggerCallback();
434
+ }
435
+
436
+ $images.bind( 'load.imagesLoaded error.imagesLoaded', imgLoaded ).each( function() {
437
+ // cached images don't fire load sometimes, so we reset src.
438
+ var src = this.src;
439
+ // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
440
+ // data uri bypasses webkit log warning (thx doug jones)
441
+ this.src = blank;
442
+ this.src = src;
443
+ });
444
+
445
+ return $this;
446
+ };
447
+
448
+
449
+ // helper function for logging errors
450
+ // $.error breaks jQuery chaining
451
+ var logError = function( message ) {
452
+ if ( window.console ) {
453
+ window.console.error( message );
454
+ }
455
+ };
456
+
457
+ // ======================= Plugin bridge ===============================
458
+ // leverages data method to either create or return $.Mason constructor
459
+ // A bit from jQuery UI
460
+ // https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js
461
+ // A bit from jcarousel
462
+ // https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js
463
+
464
+ $.fn.masonry = function( options ) {
465
+ if ( typeof options === 'string' ) {
466
+ // call method
467
+ var args = Array.prototype.slice.call( arguments, 1 );
468
+
469
+ this.each(function(){
470
+ var instance = $.data( this, 'masonry' );
471
+ if ( !instance ) {
472
+ logError( "cannot call methods on masonry prior to initialization; " +
473
+ "attempted to call method '" + options + "'" );
474
+ return;
475
+ }
476
+ if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
477
+ logError( "no such method '" + options + "' for masonry instance" );
478
+ return;
479
+ }
480
+ // apply method
481
+ instance[ options ].apply( instance, args );
482
+ });
483
+ } else {
484
+ this.each(function() {
485
+ var instance = $.data( this, 'masonry' );
486
+ if ( instance ) {
487
+ // apply options & init
488
+ instance.option( options || {} );
489
+ instance._init();
490
+ } else {
491
+ // initialize new instance
492
+ $.data( this, 'masonry', new $.Mason( options, this ) );
493
+ }
494
+ });
495
+ }
496
+ return this;
497
+ };
498
+
499
+ })( window, jQuery );