corkboard 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.
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 );