omnom 0.0.1 → 0.0.2
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.
- data/lib/omnom/version.rb +1 -1
- data/vendor/assets/javascripts/omnom/jquery.imagesloaded.js +125 -0
- data/vendor/assets/javascripts/omnom/jquery.masonry.js +502 -0
- data/vendor/assets/javascripts/omnom/jquery.timeago.en.js +20 -0
- data/vendor/assets/javascripts/omnom/jquery.timeago.js +184 -0
- metadata +5 -1
data/lib/omnom/version.rb
CHANGED
@@ -0,0 +1,125 @@
|
|
1
|
+
/*!
|
2
|
+
* jQuery imagesLoaded plugin v2.1.1
|
3
|
+
* http://github.com/desandro/imagesloaded
|
4
|
+
*
|
5
|
+
* MIT License. by Paul Irish et al.
|
6
|
+
*/
|
7
|
+
|
8
|
+
/*jshint curly: true, eqeqeq: true, noempty: true, strict: true, undef: true, browser: true */
|
9
|
+
/*global jQuery: false */
|
10
|
+
|
11
|
+
;(function($, undefined) {
|
12
|
+
'use strict';
|
13
|
+
|
14
|
+
// blank image data-uri bypasses webkit log warning (thx doug jones)
|
15
|
+
var BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
|
16
|
+
|
17
|
+
$.fn.imagesLoaded = function( callback ) {
|
18
|
+
var $this = this,
|
19
|
+
deferred = $.isFunction($.Deferred) ? $.Deferred() : 0,
|
20
|
+
hasNotify = $.isFunction(deferred.notify),
|
21
|
+
$images = $this.find('img').add( $this.filter('img') ),
|
22
|
+
loaded = [],
|
23
|
+
proper = [],
|
24
|
+
broken = [];
|
25
|
+
|
26
|
+
// Register deferred callbacks
|
27
|
+
if ($.isPlainObject(callback)) {
|
28
|
+
$.each(callback, function (key, value) {
|
29
|
+
if (key === 'callback') {
|
30
|
+
callback = value;
|
31
|
+
} else if (deferred) {
|
32
|
+
deferred[key](value);
|
33
|
+
}
|
34
|
+
});
|
35
|
+
}
|
36
|
+
|
37
|
+
function doneLoading() {
|
38
|
+
var $proper = $(proper),
|
39
|
+
$broken = $(broken);
|
40
|
+
|
41
|
+
if ( deferred ) {
|
42
|
+
if ( broken.length ) {
|
43
|
+
deferred.reject( $images, $proper, $broken );
|
44
|
+
} else {
|
45
|
+
deferred.resolve( $images );
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
if ( $.isFunction( callback ) ) {
|
50
|
+
callback.call( $this, $images, $proper, $broken );
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
function imgLoadedHandler( event ) {
|
55
|
+
imgLoaded( event.target, event.type === 'error' );
|
56
|
+
}
|
57
|
+
|
58
|
+
function imgLoaded( img, isBroken ) {
|
59
|
+
// don't proceed if BLANK image, or image is already loaded
|
60
|
+
if ( img.src === BLANK || $.inArray( img, loaded ) !== -1 ) {
|
61
|
+
return;
|
62
|
+
}
|
63
|
+
|
64
|
+
// store element in loaded images array
|
65
|
+
loaded.push( img );
|
66
|
+
|
67
|
+
// keep track of broken and properly loaded images
|
68
|
+
if ( isBroken ) {
|
69
|
+
broken.push( img );
|
70
|
+
} else {
|
71
|
+
proper.push( img );
|
72
|
+
}
|
73
|
+
|
74
|
+
// cache image and its state for future calls
|
75
|
+
$.data( img, 'imagesLoaded', { isBroken: isBroken, src: img.src } );
|
76
|
+
|
77
|
+
// trigger deferred progress method if present
|
78
|
+
if ( hasNotify ) {
|
79
|
+
deferred.notifyWith( $(img), [ isBroken, $images, $(proper), $(broken) ] );
|
80
|
+
}
|
81
|
+
|
82
|
+
// call doneLoading and clean listeners if all images are loaded
|
83
|
+
if ( $images.length === loaded.length ) {
|
84
|
+
setTimeout( doneLoading );
|
85
|
+
$images.unbind( '.imagesLoaded', imgLoadedHandler );
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
// if no images, trigger immediately
|
90
|
+
if ( !$images.length ) {
|
91
|
+
doneLoading();
|
92
|
+
} else {
|
93
|
+
$images.bind( 'load.imagesLoaded error.imagesLoaded', imgLoadedHandler )
|
94
|
+
.each( function( i, el ) {
|
95
|
+
var src = el.src;
|
96
|
+
|
97
|
+
// find out if this image has been already checked for status
|
98
|
+
// if it was, and src has not changed, call imgLoaded on it
|
99
|
+
var cached = $.data( el, 'imagesLoaded' );
|
100
|
+
if ( cached && cached.src === src ) {
|
101
|
+
imgLoaded( el, cached.isBroken );
|
102
|
+
return;
|
103
|
+
}
|
104
|
+
|
105
|
+
// if complete is true and browser supports natural sizes, try
|
106
|
+
// to check for image status manually
|
107
|
+
if ( el.complete && el.naturalWidth !== undefined ) {
|
108
|
+
imgLoaded( el, el.naturalWidth === 0 || el.naturalHeight === 0 );
|
109
|
+
return;
|
110
|
+
}
|
111
|
+
|
112
|
+
// cached images don't fire load sometimes, so we reset src, but only when
|
113
|
+
// dealing with IE, or image is complete (loaded) and failed manual check
|
114
|
+
// webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
|
115
|
+
if ( el.readyState || el.complete ) {
|
116
|
+
el.src = BLANK;
|
117
|
+
el.src = src;
|
118
|
+
}
|
119
|
+
});
|
120
|
+
}
|
121
|
+
|
122
|
+
return deferred ? deferred.promise( $this ) : $this;
|
123
|
+
};
|
124
|
+
|
125
|
+
})(jQuery);
|
@@ -0,0 +1,502 @@
|
|
1
|
+
/**
|
2
|
+
* jQuery Masonry v2.1.08
|
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.dispatch.apply( context, args );
|
49
|
+
|
50
|
+
}, execAsap === "execAsap"? 0 : 100 );
|
51
|
+
}
|
52
|
+
};
|
53
|
+
|
54
|
+
$.fn.smartresize = function( fn ) {
|
55
|
+
return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] );
|
56
|
+
};
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
// ========================= Masonry ===============================
|
61
|
+
|
62
|
+
|
63
|
+
// our "Widget" object constructor
|
64
|
+
$.Mason = function( options, element ){
|
65
|
+
this.element = $( element );
|
66
|
+
|
67
|
+
this._create( options );
|
68
|
+
this._init();
|
69
|
+
};
|
70
|
+
|
71
|
+
$.Mason.settings = {
|
72
|
+
isResizable: true,
|
73
|
+
isAnimated: false,
|
74
|
+
animationOptions: {
|
75
|
+
queue: false,
|
76
|
+
duration: 500
|
77
|
+
},
|
78
|
+
gutterWidth: 0,
|
79
|
+
isRTL: false,
|
80
|
+
isFitWidth: false,
|
81
|
+
containerStyle: {
|
82
|
+
position: 'relative'
|
83
|
+
}
|
84
|
+
};
|
85
|
+
|
86
|
+
$.Mason.prototype = {
|
87
|
+
|
88
|
+
_filterFindBricks: function( $elems ) {
|
89
|
+
var selector = this.options.itemSelector;
|
90
|
+
// if there is a selector
|
91
|
+
// filter/find appropriate item elements
|
92
|
+
return !selector ? $elems : $elems.filter( selector ).add( $elems.find( selector ) );
|
93
|
+
},
|
94
|
+
|
95
|
+
_getBricks: function( $elems ) {
|
96
|
+
var $bricks = this._filterFindBricks( $elems )
|
97
|
+
.css({ position: 'absolute' })
|
98
|
+
.addClass('masonry-brick');
|
99
|
+
return $bricks;
|
100
|
+
},
|
101
|
+
|
102
|
+
// sets up widget
|
103
|
+
_create : function( options ) {
|
104
|
+
|
105
|
+
this.options = $.extend( true, {}, $.Mason.settings, options );
|
106
|
+
this.styleQueue = [];
|
107
|
+
|
108
|
+
// get original styles in case we re-apply them in .destroy()
|
109
|
+
var elemStyle = this.element[0].style;
|
110
|
+
this.originalStyle = {
|
111
|
+
// get height
|
112
|
+
height: elemStyle.height || ''
|
113
|
+
};
|
114
|
+
// get other styles that will be overwritten
|
115
|
+
var containerStyle = this.options.containerStyle;
|
116
|
+
for ( var prop in containerStyle ) {
|
117
|
+
this.originalStyle[ prop ] = elemStyle[ prop ] || '';
|
118
|
+
}
|
119
|
+
|
120
|
+
this.element.css( containerStyle );
|
121
|
+
|
122
|
+
this.horizontalDirection = this.options.isRTL ? 'right' : 'left';
|
123
|
+
|
124
|
+
var x = this.element.css( 'padding-' + this.horizontalDirection );
|
125
|
+
var y = this.element.css( 'padding-top' );
|
126
|
+
this.offset = {
|
127
|
+
x: x ? parseInt( x, 10 ) : 0,
|
128
|
+
y: y ? parseInt( y, 10 ) : 0
|
129
|
+
};
|
130
|
+
|
131
|
+
this.isFluid = this.options.columnWidth && typeof this.options.columnWidth === 'function';
|
132
|
+
|
133
|
+
// add masonry class first time around
|
134
|
+
var instance = this;
|
135
|
+
setTimeout( function() {
|
136
|
+
instance.element.addClass('masonry');
|
137
|
+
}, 0 );
|
138
|
+
|
139
|
+
// bind resize method
|
140
|
+
if ( this.options.isResizable ) {
|
141
|
+
$(window).bind( 'smartresize.masonry', function() {
|
142
|
+
instance.resize();
|
143
|
+
});
|
144
|
+
}
|
145
|
+
|
146
|
+
|
147
|
+
// need to get bricks
|
148
|
+
this.reloadItems();
|
149
|
+
|
150
|
+
},
|
151
|
+
|
152
|
+
// _init fires when instance is first created
|
153
|
+
// and when instance is triggered again -> $el.masonry();
|
154
|
+
_init : function( callback ) {
|
155
|
+
this._getColumns();
|
156
|
+
this._reLayout( callback );
|
157
|
+
},
|
158
|
+
|
159
|
+
option: function( key, value ){
|
160
|
+
// set options AFTER initialization:
|
161
|
+
// signature: $('#foo').bar({ cool:false });
|
162
|
+
if ( $.isPlainObject( key ) ){
|
163
|
+
this.options = $.extend(true, this.options, key);
|
164
|
+
}
|
165
|
+
},
|
166
|
+
|
167
|
+
// ====================== General Layout ======================
|
168
|
+
|
169
|
+
// used on collection of atoms (should be filtered, and sorted before )
|
170
|
+
// accepts atoms-to-be-laid-out to start with
|
171
|
+
layout : function( $bricks, callback ) {
|
172
|
+
|
173
|
+
// place each brick
|
174
|
+
for (var i=0, len = $bricks.length; i < len; i++) {
|
175
|
+
this._placeBrick( $bricks[i] );
|
176
|
+
}
|
177
|
+
|
178
|
+
// set the size of the container
|
179
|
+
var containerSize = {};
|
180
|
+
containerSize.height = Math.max.apply( Math, this.colYs );
|
181
|
+
if ( this.options.isFitWidth ) {
|
182
|
+
var unusedCols = 0;
|
183
|
+
i = this.cols;
|
184
|
+
// count unused columns
|
185
|
+
while ( --i ) {
|
186
|
+
if ( this.colYs[i] !== 0 ) {
|
187
|
+
break;
|
188
|
+
}
|
189
|
+
unusedCols++;
|
190
|
+
}
|
191
|
+
// fit container to columns that have been used;
|
192
|
+
containerSize.width = (this.cols - unusedCols) * this.columnWidth - this.options.gutterWidth;
|
193
|
+
}
|
194
|
+
this.styleQueue.push({ $el: this.element, style: containerSize });
|
195
|
+
|
196
|
+
// are we animating the layout arrangement?
|
197
|
+
// use plugin-ish syntax for css or animate
|
198
|
+
var styleFn = !this.isLaidOut ? 'css' : (
|
199
|
+
this.options.isAnimated ? 'animate' : 'css'
|
200
|
+
),
|
201
|
+
animOpts = this.options.animationOptions;
|
202
|
+
|
203
|
+
// process styleQueue
|
204
|
+
var obj;
|
205
|
+
for (i=0, len = this.styleQueue.length; i < len; i++) {
|
206
|
+
obj = this.styleQueue[i];
|
207
|
+
obj.$el[ styleFn ]( obj.style, animOpts );
|
208
|
+
}
|
209
|
+
|
210
|
+
// clear out queue for next time
|
211
|
+
this.styleQueue = [];
|
212
|
+
|
213
|
+
// provide $elems as context for the callback
|
214
|
+
if ( callback ) {
|
215
|
+
callback.call( $bricks );
|
216
|
+
}
|
217
|
+
|
218
|
+
this.isLaidOut = true;
|
219
|
+
},
|
220
|
+
|
221
|
+
// calculates number of columns
|
222
|
+
// i.e. this.columnWidth = 200
|
223
|
+
_getColumns : function() {
|
224
|
+
var container = this.options.isFitWidth ? this.element.parent() : this.element,
|
225
|
+
containerWidth = container.width();
|
226
|
+
|
227
|
+
// use fluid columnWidth function if there
|
228
|
+
this.columnWidth = this.isFluid ? this.options.columnWidth( containerWidth ) :
|
229
|
+
// if not, how about the explicitly set option?
|
230
|
+
this.options.columnWidth ||
|
231
|
+
// or use the size of the first item
|
232
|
+
this.$bricks.outerWidth(true) ||
|
233
|
+
// if there's no items, use size of container
|
234
|
+
containerWidth;
|
235
|
+
|
236
|
+
this.columnWidth += this.options.gutterWidth;
|
237
|
+
|
238
|
+
this.cols = Math.floor( ( containerWidth + this.options.gutterWidth ) / this.columnWidth );
|
239
|
+
this.cols = Math.max( this.cols, 1 );
|
240
|
+
|
241
|
+
},
|
242
|
+
|
243
|
+
// layout logic
|
244
|
+
_placeBrick: function( brick ) {
|
245
|
+
var $brick = $(brick),
|
246
|
+
colSpan, groupCount, groupY, groupColY, j;
|
247
|
+
|
248
|
+
//how many columns does this brick span
|
249
|
+
colSpan = Math.ceil( $brick.outerWidth(true) / this.columnWidth );
|
250
|
+
colSpan = Math.min( colSpan, this.cols );
|
251
|
+
|
252
|
+
if ( colSpan === 1 ) {
|
253
|
+
// if brick spans only one column, just like singleMode
|
254
|
+
groupY = this.colYs;
|
255
|
+
} else {
|
256
|
+
// brick spans more than one column
|
257
|
+
// how many different places could this brick fit horizontally
|
258
|
+
groupCount = this.cols + 1 - colSpan;
|
259
|
+
groupY = [];
|
260
|
+
|
261
|
+
// for each group potential horizontal position
|
262
|
+
for ( j=0; j < groupCount; j++ ) {
|
263
|
+
// make an array of colY values for that one group
|
264
|
+
groupColY = this.colYs.slice( j, j+colSpan );
|
265
|
+
// and get the max value of the array
|
266
|
+
groupY[j] = Math.max.apply( Math, groupColY );
|
267
|
+
}
|
268
|
+
|
269
|
+
}
|
270
|
+
|
271
|
+
// get the minimum Y value from the columns
|
272
|
+
var minimumY = Math.min.apply( Math, groupY ),
|
273
|
+
shortCol = 0;
|
274
|
+
|
275
|
+
// Find index of short column, the first from the left
|
276
|
+
for (var i=0, len = groupY.length; i < len; i++) {
|
277
|
+
if ( groupY[i] === minimumY ) {
|
278
|
+
shortCol = i;
|
279
|
+
break;
|
280
|
+
}
|
281
|
+
}
|
282
|
+
|
283
|
+
// position the brick
|
284
|
+
var position = {
|
285
|
+
top: minimumY + this.offset.y
|
286
|
+
};
|
287
|
+
// position.left or position.right
|
288
|
+
position[ this.horizontalDirection ] = this.columnWidth * shortCol + this.offset.x;
|
289
|
+
this.styleQueue.push({ $el: $brick, style: position });
|
290
|
+
|
291
|
+
// apply setHeight to necessary columns
|
292
|
+
var setHeight = minimumY + $brick.outerHeight(true),
|
293
|
+
setSpan = this.cols + 1 - len;
|
294
|
+
for ( i=0; i < setSpan; i++ ) {
|
295
|
+
this.colYs[ shortCol + i ] = setHeight;
|
296
|
+
}
|
297
|
+
|
298
|
+
},
|
299
|
+
|
300
|
+
|
301
|
+
resize: function() {
|
302
|
+
var prevColCount = this.cols;
|
303
|
+
// get updated colCount
|
304
|
+
this._getColumns();
|
305
|
+
if ( this.isFluid || this.cols !== prevColCount ) {
|
306
|
+
// if column count has changed, trigger new layout
|
307
|
+
this._reLayout();
|
308
|
+
}
|
309
|
+
},
|
310
|
+
|
311
|
+
|
312
|
+
_reLayout : function( callback ) {
|
313
|
+
// reset columns
|
314
|
+
var i = this.cols;
|
315
|
+
this.colYs = [];
|
316
|
+
while (i--) {
|
317
|
+
this.colYs.push( 0 );
|
318
|
+
}
|
319
|
+
// apply layout logic to all bricks
|
320
|
+
this.layout( this.$bricks, callback );
|
321
|
+
},
|
322
|
+
|
323
|
+
// ====================== Convenience methods ======================
|
324
|
+
|
325
|
+
// goes through all children again and gets bricks in proper order
|
326
|
+
reloadItems : function() {
|
327
|
+
this.$bricks = this._getBricks( this.element.children() );
|
328
|
+
},
|
329
|
+
|
330
|
+
|
331
|
+
reload : function( callback ) {
|
332
|
+
this.reloadItems();
|
333
|
+
this._init( callback );
|
334
|
+
},
|
335
|
+
|
336
|
+
|
337
|
+
// convienence method for working with Infinite Scroll
|
338
|
+
appended : function( $content, isAnimatedFromBottom, callback ) {
|
339
|
+
if ( isAnimatedFromBottom ) {
|
340
|
+
// set new stuff to the bottom
|
341
|
+
this._filterFindBricks( $content ).css({ top: this.element.height() });
|
342
|
+
var instance = this;
|
343
|
+
setTimeout( function(){
|
344
|
+
instance._appended( $content, callback );
|
345
|
+
}, 1 );
|
346
|
+
} else {
|
347
|
+
this._appended( $content, callback );
|
348
|
+
}
|
349
|
+
},
|
350
|
+
|
351
|
+
_appended : function( $content, callback ) {
|
352
|
+
var $newBricks = this._getBricks( $content );
|
353
|
+
// add new bricks to brick pool
|
354
|
+
this.$bricks = this.$bricks.add( $newBricks );
|
355
|
+
this.layout( $newBricks, callback );
|
356
|
+
},
|
357
|
+
|
358
|
+
// removes elements from Masonry widget
|
359
|
+
remove : function( $content ) {
|
360
|
+
this.$bricks = this.$bricks.not( $content );
|
361
|
+
$content.remove();
|
362
|
+
},
|
363
|
+
|
364
|
+
// destroys widget, returns elements and container back (close) to original style
|
365
|
+
destroy : function() {
|
366
|
+
|
367
|
+
this.$bricks
|
368
|
+
.removeClass('masonry-brick')
|
369
|
+
.each(function(){
|
370
|
+
this.style.position = '';
|
371
|
+
this.style.top = '';
|
372
|
+
this.style.left = '';
|
373
|
+
});
|
374
|
+
|
375
|
+
// re-apply saved container styles
|
376
|
+
var elemStyle = this.element[0].style;
|
377
|
+
for ( var prop in this.originalStyle ) {
|
378
|
+
elemStyle[ prop ] = this.originalStyle[ prop ];
|
379
|
+
}
|
380
|
+
|
381
|
+
this.element
|
382
|
+
.unbind('.masonry')
|
383
|
+
.removeClass('masonry')
|
384
|
+
.removeData('masonry');
|
385
|
+
|
386
|
+
$(window).unbind('.masonry');
|
387
|
+
|
388
|
+
}
|
389
|
+
|
390
|
+
};
|
391
|
+
|
392
|
+
|
393
|
+
// ======================= imagesLoaded Plugin ===============================
|
394
|
+
/*!
|
395
|
+
* jQuery imagesLoaded plugin v1.1.0
|
396
|
+
* http://github.com/desandro/imagesloaded
|
397
|
+
*
|
398
|
+
* MIT License. by Paul Irish et al.
|
399
|
+
*/
|
400
|
+
|
401
|
+
|
402
|
+
// $('#my-container').imagesLoaded(myFunction)
|
403
|
+
// or
|
404
|
+
// $('img').imagesLoaded(myFunction)
|
405
|
+
|
406
|
+
// execute a callback when all images have loaded.
|
407
|
+
// needed because .load() doesn't work on cached images
|
408
|
+
|
409
|
+
// callback function gets image collection as argument
|
410
|
+
// `this` is the container
|
411
|
+
|
412
|
+
$.fn.imagesLoaded = function( callback ) {
|
413
|
+
var $this = this,
|
414
|
+
$images = $this.find('img').add( $this.filter('img') ),
|
415
|
+
len = $images.length,
|
416
|
+
blank = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==',
|
417
|
+
loaded = [];
|
418
|
+
|
419
|
+
function triggerCallback() {
|
420
|
+
callback.call( $this, $images );
|
421
|
+
}
|
422
|
+
|
423
|
+
function imgLoaded( event ) {
|
424
|
+
var img = event.target;
|
425
|
+
if ( img.src !== blank && $.inArray( img, loaded ) === -1 ){
|
426
|
+
loaded.push( img );
|
427
|
+
if ( --len <= 0 ){
|
428
|
+
setTimeout( triggerCallback );
|
429
|
+
$images.unbind( '.imagesLoaded', imgLoaded );
|
430
|
+
}
|
431
|
+
}
|
432
|
+
}
|
433
|
+
|
434
|
+
// if no images, trigger immediately
|
435
|
+
if ( !len ) {
|
436
|
+
triggerCallback();
|
437
|
+
}
|
438
|
+
|
439
|
+
$images.bind( 'load.imagesLoaded error.imagesLoaded', imgLoaded ).each( function() {
|
440
|
+
// cached images don't fire load sometimes, so we reset src.
|
441
|
+
var src = this.src;
|
442
|
+
// webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
|
443
|
+
// data uri bypasses webkit log warning (thx doug jones)
|
444
|
+
this.src = blank;
|
445
|
+
this.src = src;
|
446
|
+
});
|
447
|
+
|
448
|
+
return $this;
|
449
|
+
};
|
450
|
+
|
451
|
+
|
452
|
+
// helper function for logging errors
|
453
|
+
// $.error breaks jQuery chaining
|
454
|
+
var logError = function( message ) {
|
455
|
+
if ( window.console ) {
|
456
|
+
window.console.error( message );
|
457
|
+
}
|
458
|
+
};
|
459
|
+
|
460
|
+
// ======================= Plugin bridge ===============================
|
461
|
+
// leverages data method to either create or return $.Mason constructor
|
462
|
+
// A bit from jQuery UI
|
463
|
+
// https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js
|
464
|
+
// A bit from jcarousel
|
465
|
+
// https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js
|
466
|
+
|
467
|
+
$.fn.masonry = function( options ) {
|
468
|
+
if ( typeof options === 'string' ) {
|
469
|
+
// call method
|
470
|
+
var args = Array.prototype.slice.call( arguments, 1 );
|
471
|
+
|
472
|
+
this.each(function(){
|
473
|
+
var instance = $.data( this, 'masonry' );
|
474
|
+
if ( !instance ) {
|
475
|
+
logError( "cannot call methods on masonry prior to initialization; " +
|
476
|
+
"attempted to call method '" + options + "'" );
|
477
|
+
return;
|
478
|
+
}
|
479
|
+
if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
|
480
|
+
logError( "no such method '" + options + "' for masonry instance" );
|
481
|
+
return;
|
482
|
+
}
|
483
|
+
// apply method
|
484
|
+
instance[ options ].apply( instance, args );
|
485
|
+
});
|
486
|
+
} else {
|
487
|
+
this.each(function() {
|
488
|
+
var instance = $.data( this, 'masonry' );
|
489
|
+
if ( instance ) {
|
490
|
+
// apply options & init
|
491
|
+
instance.option( options || {} );
|
492
|
+
instance._init();
|
493
|
+
} else {
|
494
|
+
// initialize new instance
|
495
|
+
$.data( this, 'masonry', new $.Mason( options, this ) );
|
496
|
+
}
|
497
|
+
});
|
498
|
+
}
|
499
|
+
return this;
|
500
|
+
};
|
501
|
+
|
502
|
+
})( window, jQuery );
|
@@ -0,0 +1,20 @@
|
|
1
|
+
// English shortened
|
2
|
+
jQuery.timeago.settings.strings = {
|
3
|
+
prefixAgo: null,
|
4
|
+
prefixFromNow: null,
|
5
|
+
suffixAgo: "",
|
6
|
+
suffixFromNow: "",
|
7
|
+
seconds: "1m",
|
8
|
+
minute: "1m",
|
9
|
+
minutes: "%dm",
|
10
|
+
hour: "1h",
|
11
|
+
hours: "%dh",
|
12
|
+
day: "1d",
|
13
|
+
days: "%dd",
|
14
|
+
month: "1mo",
|
15
|
+
months: "%dmo",
|
16
|
+
year: "1yr",
|
17
|
+
years: "%dyr",
|
18
|
+
wordSeparator: " ",
|
19
|
+
numbers: []
|
20
|
+
};
|
@@ -0,0 +1,184 @@
|
|
1
|
+
/**
|
2
|
+
* Timeago is a jQuery plugin that makes it easy to support automatically
|
3
|
+
* updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
|
4
|
+
*
|
5
|
+
* @name timeago
|
6
|
+
* @version 1.1.0
|
7
|
+
* @requires jQuery v1.2.3+
|
8
|
+
* @author Ryan McGeary
|
9
|
+
* @license MIT License - http://www.opensource.org/licenses/mit-license.php
|
10
|
+
*
|
11
|
+
* For usage and examples, visit:
|
12
|
+
* http://timeago.yarp.com/
|
13
|
+
*
|
14
|
+
* Copyright (c) 2008-2013, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
|
15
|
+
*/
|
16
|
+
|
17
|
+
(function (factory) {
|
18
|
+
if (typeof define === 'function' && define.amd) {
|
19
|
+
// AMD. Register as an anonymous module.
|
20
|
+
define(['jquery'], factory);
|
21
|
+
} else {
|
22
|
+
// Browser globals
|
23
|
+
factory(jQuery);
|
24
|
+
}
|
25
|
+
}(function ($) {
|
26
|
+
$.timeago = function(timestamp) {
|
27
|
+
if (timestamp instanceof Date) {
|
28
|
+
return inWords(timestamp);
|
29
|
+
} else if (typeof timestamp === "string") {
|
30
|
+
return inWords($.timeago.parse(timestamp));
|
31
|
+
} else if (typeof timestamp === "number") {
|
32
|
+
return inWords(new Date(timestamp));
|
33
|
+
} else {
|
34
|
+
return inWords($.timeago.datetime(timestamp));
|
35
|
+
}
|
36
|
+
};
|
37
|
+
var $t = $.timeago;
|
38
|
+
|
39
|
+
$.extend($.timeago, {
|
40
|
+
settings: {
|
41
|
+
refreshMillis: 60000,
|
42
|
+
allowFuture: false,
|
43
|
+
localeTitle: false,
|
44
|
+
strings: {
|
45
|
+
prefixAgo: null,
|
46
|
+
prefixFromNow: null,
|
47
|
+
suffixAgo: "ago",
|
48
|
+
suffixFromNow: "from now",
|
49
|
+
seconds: "less than a minute",
|
50
|
+
minute: "about a minute",
|
51
|
+
minutes: "%d minutes",
|
52
|
+
hour: "about an hour",
|
53
|
+
hours: "about %d hours",
|
54
|
+
day: "a day",
|
55
|
+
days: "%d days",
|
56
|
+
month: "about a month",
|
57
|
+
months: "%d months",
|
58
|
+
year: "about a year",
|
59
|
+
years: "%d years",
|
60
|
+
wordSeparator: " ",
|
61
|
+
numbers: []
|
62
|
+
}
|
63
|
+
},
|
64
|
+
inWords: function(distanceMillis) {
|
65
|
+
var $l = this.settings.strings;
|
66
|
+
var prefix = $l.prefixAgo;
|
67
|
+
var suffix = $l.suffixAgo;
|
68
|
+
if (this.settings.allowFuture) {
|
69
|
+
if (distanceMillis < 0) {
|
70
|
+
prefix = $l.prefixFromNow;
|
71
|
+
suffix = $l.suffixFromNow;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
var seconds = Math.abs(distanceMillis) / 1000;
|
76
|
+
var minutes = seconds / 60;
|
77
|
+
var hours = minutes / 60;
|
78
|
+
var days = hours / 24;
|
79
|
+
var years = days / 365;
|
80
|
+
|
81
|
+
function substitute(stringOrFunction, number) {
|
82
|
+
var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
|
83
|
+
var value = ($l.numbers && $l.numbers[number]) || number;
|
84
|
+
return string.replace(/%d/i, value);
|
85
|
+
}
|
86
|
+
|
87
|
+
var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
|
88
|
+
seconds < 90 && substitute($l.minute, 1) ||
|
89
|
+
minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
|
90
|
+
minutes < 90 && substitute($l.hour, 1) ||
|
91
|
+
hours < 24 && substitute($l.hours, Math.round(hours)) ||
|
92
|
+
hours < 42 && substitute($l.day, 1) ||
|
93
|
+
days < 30 && substitute($l.days, Math.round(days)) ||
|
94
|
+
days < 45 && substitute($l.month, 1) ||
|
95
|
+
days < 365 && substitute($l.months, Math.round(days / 30)) ||
|
96
|
+
years < 1.5 && substitute($l.year, 1) ||
|
97
|
+
substitute($l.years, Math.round(years));
|
98
|
+
|
99
|
+
var separator = $l.wordSeparator || "";
|
100
|
+
if ($l.wordSeparator === undefined) { separator = " "; }
|
101
|
+
return $.trim([prefix, words, suffix].join(separator));
|
102
|
+
},
|
103
|
+
parse: function(iso8601) {
|
104
|
+
var s = $.trim(iso8601);
|
105
|
+
s = s.replace(/\.\d+/,""); // remove milliseconds
|
106
|
+
s = s.replace(/-/,"/").replace(/-/,"/");
|
107
|
+
s = s.replace(/T/," ").replace(/Z/," UTC");
|
108
|
+
s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
|
109
|
+
return new Date(s);
|
110
|
+
},
|
111
|
+
datetime: function(elem) {
|
112
|
+
var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
|
113
|
+
return $t.parse(iso8601);
|
114
|
+
},
|
115
|
+
isTime: function(elem) {
|
116
|
+
// jQuery's `is()` doesn't play well with HTML5 in IE
|
117
|
+
return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
|
118
|
+
}
|
119
|
+
});
|
120
|
+
|
121
|
+
// functions that can be called via $(el).timeago('action')
|
122
|
+
// init is default when no action is given
|
123
|
+
// functions are called with context of a single element
|
124
|
+
var functions = {
|
125
|
+
init: function(){
|
126
|
+
var refresh_el = $.proxy(refresh, this);
|
127
|
+
refresh_el();
|
128
|
+
var $s = $t.settings;
|
129
|
+
if ($s.refreshMillis > 0) {
|
130
|
+
setInterval(refresh_el, $s.refreshMillis);
|
131
|
+
}
|
132
|
+
},
|
133
|
+
update: function(time){
|
134
|
+
$(this).data('timeago', { datetime: $t.parse(time) });
|
135
|
+
refresh.apply(this);
|
136
|
+
}
|
137
|
+
};
|
138
|
+
|
139
|
+
$.fn.timeago = function(action, options) {
|
140
|
+
var fn = action ? functions[action] : functions.init;
|
141
|
+
if(!fn){
|
142
|
+
throw new Error("Unknown function name '"+ action +"' for timeago");
|
143
|
+
}
|
144
|
+
// each over objects here and call the requested function
|
145
|
+
this.each(function(){
|
146
|
+
fn.call(this, options);
|
147
|
+
});
|
148
|
+
return this;
|
149
|
+
};
|
150
|
+
|
151
|
+
function refresh() {
|
152
|
+
var data = prepareData(this);
|
153
|
+
if (!isNaN(data.datetime)) {
|
154
|
+
$(this).text(inWords(data.datetime));
|
155
|
+
}
|
156
|
+
return this;
|
157
|
+
}
|
158
|
+
|
159
|
+
function prepareData(element) {
|
160
|
+
element = $(element);
|
161
|
+
if (!element.data("timeago")) {
|
162
|
+
element.data("timeago", { datetime: $t.datetime(element) });
|
163
|
+
var text = $.trim(element.text());
|
164
|
+
if ($t.settings.localeTitle) {
|
165
|
+
element.attr("title", element.data('timeago').datetime.toLocaleString());
|
166
|
+
} else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
|
167
|
+
element.attr("title", text);
|
168
|
+
}
|
169
|
+
}
|
170
|
+
return element.data("timeago");
|
171
|
+
}
|
172
|
+
|
173
|
+
function inWords(date) {
|
174
|
+
return $t.inWords(distance(date));
|
175
|
+
}
|
176
|
+
|
177
|
+
function distance(date) {
|
178
|
+
return (new Date().getTime() - date.getTime());
|
179
|
+
}
|
180
|
+
|
181
|
+
// fix for IE6 suckage
|
182
|
+
document.createElement("abbr");
|
183
|
+
document.createElement("time");
|
184
|
+
}));
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omnom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -445,6 +445,10 @@ files:
|
|
445
445
|
- lib/omnom.rb
|
446
446
|
- lib/tasks/omnom/heroku.rake
|
447
447
|
- lib/tasks/omnom.rake
|
448
|
+
- vendor/assets/javascripts/omnom/jquery.imagesloaded.js
|
449
|
+
- vendor/assets/javascripts/omnom/jquery.masonry.js
|
450
|
+
- vendor/assets/javascripts/omnom/jquery.timeago.en.js
|
451
|
+
- vendor/assets/javascripts/omnom/jquery.timeago.js
|
448
452
|
- MIT-LICENSE
|
449
453
|
- Rakefile
|
450
454
|
- README.md
|