corkboard 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.md +44 -0
- data/Rakefile +29 -0
- data/app/assets/images/corkboard/image.png +0 -0
- data/app/assets/javascripts/corkboard.js.erb +45 -0
- data/app/assets/javascripts/corkboard/app/board.js +86 -0
- data/app/assets/javascripts/corkboard/base.js +9 -0
- data/app/assets/javascripts/corkboard/lib/publisher.js +53 -0
- data/app/assets/javascripts/corkboard/lib/weighted_randomizer.js +41 -0
- data/app/assets/stylesheets/corkboard/application.css +88 -0
- data/app/assets/stylesheets/corkboard/responsive.css +93 -0
- data/app/controllers/corkboard/application_controller.rb +15 -0
- data/app/controllers/corkboard/authorizations_controller.rb +65 -0
- data/app/controllers/corkboard/board_controller.rb +10 -0
- data/app/controllers/corkboard/posts_controller.rb +72 -0
- data/app/helpers/corkboard/application_helper.rb +72 -0
- data/app/models/corkboard/authorization.rb +13 -0
- data/app/models/corkboard/post.rb +19 -0
- data/app/models/corkboard/subscription.rb +34 -0
- data/app/views/corkboard/authorizations/index.html.erb +32 -0
- data/app/views/corkboard/board/_filters.html.erb +15 -0
- data/app/views/corkboard/board/_posts.html.erb +10 -0
- data/app/views/corkboard/board/show.html.erb +4 -0
- data/app/views/layouts/corkboard/application.html.erb +14 -0
- data/config/routes.rb +29 -0
- data/db/migrate/01_create_corkboard_authorizations.rb +17 -0
- data/lib/corkboard.rb +191 -0
- data/lib/corkboard/client.rb +51 -0
- data/lib/corkboard/clients/instagram.rb +26 -0
- data/lib/corkboard/engine.rb +27 -0
- data/lib/corkboard/provider.rb +21 -0
- data/lib/corkboard/providers/instagram.rb +29 -0
- data/lib/corkboard/publishers/mock.rb +14 -0
- data/lib/corkboard/publishers/pusher.rb +31 -0
- data/lib/corkboard/service/config.rb +12 -0
- data/lib/corkboard/version.rb +3 -0
- data/lib/generators/corkboard/install_generator.rb +16 -0
- data/lib/generators/corkboard/templates/README +14 -0
- data/lib/generators/corkboard/templates/initializer.rb +45 -0
- data/lib/tasks/corkboard_tasks.rake +4 -0
- metadata +412 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# Corkboard
|
2
|
+
|
3
|
+
TODO: Summary of Corkboard.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'corkboard'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install corkboard
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Developing
|
24
|
+
|
25
|
+
TODO...
|
26
|
+
|
27
|
+
### Jasmine (js) Specs
|
28
|
+
|
29
|
+
* To run from command line:
|
30
|
+
|
31
|
+
$ rake spec:javascript
|
32
|
+
|
33
|
+
* To see them in the browser:
|
34
|
+
|
35
|
+
$ cd spec/dummy && rails s
|
36
|
+
# open browser to http://localhost:3000/jasmine
|
37
|
+
|
38
|
+
## Contributing
|
39
|
+
|
40
|
+
1. Fork it
|
41
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
42
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
43
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
44
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
exit 1
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'bundler/gem_tasks'
|
10
|
+
require 'rspec/core/rake_task'
|
11
|
+
|
12
|
+
desc 'Default: run specs'
|
13
|
+
task :default => [:spec]
|
14
|
+
|
15
|
+
desc 'Run the full spec suite'
|
16
|
+
task :spec => [:'spec:ruby', :'spec:javascript']
|
17
|
+
|
18
|
+
namespace :spec do
|
19
|
+
desc 'Run the RSpec code examples'
|
20
|
+
RSpec::Core::RakeTask.new(:ruby) do |t|
|
21
|
+
t.pattern = "spec/**/*_spec.rb"
|
22
|
+
t.verbose = false
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'Run the Jasmine code examples'
|
26
|
+
task :javascript do
|
27
|
+
system 'cd spec/dummy && rake jasminerice:run'
|
28
|
+
end
|
29
|
+
end
|
Binary file
|
@@ -0,0 +1,45 @@
|
|
1
|
+
//= require modernizr
|
2
|
+
//= require jquery
|
3
|
+
//= require jquery_ujs
|
4
|
+
//= require jquery.masonry
|
5
|
+
//= require jquery.masonry-extensions
|
6
|
+
//= require pusher
|
7
|
+
|
8
|
+
//= require corkboard/base
|
9
|
+
//= require_tree ./corkboard/lib
|
10
|
+
//= require_tree ./corkboard/app
|
11
|
+
|
12
|
+
(function($, corkboard) {
|
13
|
+
// Corkboard properties pulled from the server. Client-side definitions
|
14
|
+
// should go in `corkboard/base.js`, to ensure availability and keep this
|
15
|
+
// file clean.
|
16
|
+
//
|
17
|
+
// TODO: consider something like `gon`, rather than making this file `erb`.
|
18
|
+
$.extend(corkboard, {
|
19
|
+
version : '<%= Corkboard::VERSION %>',
|
20
|
+
config : function config(key) {
|
21
|
+
return corkboard.config[key] || corkboard.defaults[key];
|
22
|
+
}
|
23
|
+
});
|
24
|
+
|
25
|
+
$.extend(corkboard.config, {
|
26
|
+
pusher : '<%= Pusher.key %>',
|
27
|
+
weights : <%= Corkboard.presentation[:weights].to_json %>
|
28
|
+
});
|
29
|
+
|
30
|
+
// Define jQuery.fn.corkboard
|
31
|
+
$.fn.extend({
|
32
|
+
// corkboard : $.corkboard.Board
|
33
|
+
corkboard : function corkboard() {
|
34
|
+
this.each(function() {
|
35
|
+
new $.corkboard.Board($(this));
|
36
|
+
});
|
37
|
+
|
38
|
+
return this;
|
39
|
+
}
|
40
|
+
});
|
41
|
+
|
42
|
+
$(function() {
|
43
|
+
$('article.corkboard').corkboard();
|
44
|
+
});
|
45
|
+
})(jQuery, jQuery.corkboard);
|
@@ -0,0 +1,86 @@
|
|
1
|
+
(function($, corkboard) {
|
2
|
+
// TODO: allow for and handle multiple matches.
|
3
|
+
function Board(board) {
|
4
|
+
if(board.length) {
|
5
|
+
this.setup(board);
|
6
|
+
}
|
7
|
+
}
|
8
|
+
|
9
|
+
// Surfaced as part of the corkboard namespace.
|
10
|
+
corkboard.Board = Board;
|
11
|
+
|
12
|
+
// TODO: consider making selectors configurable.
|
13
|
+
// TODO: make the masonry configuration configurable.
|
14
|
+
// TODO: add specs (and break up).
|
15
|
+
Board.prototype.setup = function setup(board) {
|
16
|
+
var selectors = {
|
17
|
+
posts : 'ul.corkboard.posts',
|
18
|
+
entry : 'li.entry',
|
19
|
+
filters : 'nav.corkboard.filters'
|
20
|
+
};
|
21
|
+
var posts = $(selectors.posts, board);
|
22
|
+
var filters = $(selectors.filters);
|
23
|
+
|
24
|
+
posts.imagesLoaded(function() {
|
25
|
+
this.masonry({
|
26
|
+
isAnimated : true,
|
27
|
+
isFitWidth : true,
|
28
|
+
itemSelector : selectors.entry + ':not(.hidden)',
|
29
|
+
|
30
|
+
columnWidth : function columnWidth(containerWidth) {
|
31
|
+
if(containerWidth <= 320) {
|
32
|
+
return 160;
|
33
|
+
}
|
34
|
+
if(containerWidth <= 480) {
|
35
|
+
return 182;
|
36
|
+
}
|
37
|
+
else if(containerWidth <= 768){
|
38
|
+
return 140;
|
39
|
+
}
|
40
|
+
else {
|
41
|
+
return 172;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
});
|
45
|
+
|
46
|
+
// reveal tiles in a pseudo-scattered pattern.
|
47
|
+
this.find([selectors.entry, ':nth-child(5n+1)'].join('')).fadeIn(function callback() {
|
48
|
+
var next = $(this).next();
|
49
|
+
|
50
|
+
if( ! next.is(':visible')) {
|
51
|
+
next.fadeIn(corkboard.config('fade'), callback);
|
52
|
+
};
|
53
|
+
});
|
54
|
+
});
|
55
|
+
|
56
|
+
// TODO: generalize filter placement (to other-than-FAB)
|
57
|
+
filters.on('click', 'a', function(e) {
|
58
|
+
var link = $(this);
|
59
|
+
var filters = $('li', selectors.filters);
|
60
|
+
var filter = link.parent().toggleClass('active');
|
61
|
+
var posts = $(selectors.posts);
|
62
|
+
var entries = posts.find(selectors.entry);
|
63
|
+
var href = link.attr('href');
|
64
|
+
var tag = href.replace('#', '');
|
65
|
+
|
66
|
+
filters.not(filter).removeClass('active');
|
67
|
+
|
68
|
+
if(link.hasClass('all') || ( ! filter.hasClass('active'))) {
|
69
|
+
$('a.all').parent().addClass('active');
|
70
|
+
posts.masonry('filter', entries, '*');
|
71
|
+
}
|
72
|
+
else {
|
73
|
+
posts.masonry('filter', entries, '[data-tags~="' + tag + '"]');
|
74
|
+
}
|
75
|
+
|
76
|
+
return false;
|
77
|
+
});
|
78
|
+
|
79
|
+
new corkboard.Publisher(board, this.updated);
|
80
|
+
};
|
81
|
+
|
82
|
+
Board.prototype.updated = function updated(list, added) {
|
83
|
+
list.masonry('reload');
|
84
|
+
added.fadeIn(corkboard.config('fade'));
|
85
|
+
};
|
86
|
+
})(jQuery, jQuery.corkboard);
|
@@ -0,0 +1,53 @@
|
|
1
|
+
(function($, corkboard) {
|
2
|
+
function Publisher(board, callback) {
|
3
|
+
if(board.length) {
|
4
|
+
this.board = board;
|
5
|
+
this.callback = callback;
|
6
|
+
this.subscribe();
|
7
|
+
}
|
8
|
+
}
|
9
|
+
|
10
|
+
// Surfaced as part of the corkboard namespace.
|
11
|
+
corkboard.Publisher = Publisher;
|
12
|
+
|
13
|
+
Publisher.prototype.subscribe = function subscribe() {
|
14
|
+
var self = this;
|
15
|
+
var pusher = new Pusher(corkboard.config.pusher);
|
16
|
+
var channel = pusher.subscribe('corkboard');
|
17
|
+
|
18
|
+
// pusher.bind('pusher:error', function(data) {
|
19
|
+
// alert(data);
|
20
|
+
// });
|
21
|
+
|
22
|
+
channel.bind('posts/new', function(data) {
|
23
|
+
var eid = data.eid;
|
24
|
+
|
25
|
+
if(0 === $('[data-eid="' + eid + '"]').length) {
|
26
|
+
self.publish(data);
|
27
|
+
}
|
28
|
+
});
|
29
|
+
};
|
30
|
+
|
31
|
+
// TODO: handle collections (for more efficient updates).
|
32
|
+
// TODO: include :type (e.g., 'instagram').
|
33
|
+
// TODO: use a (server-shared) template for rendering entries.
|
34
|
+
Publisher.prototype.publish = function publish(data) {
|
35
|
+
var posts = this.board.find('ul.corkboard.posts');
|
36
|
+
var weights = corkboard.config.weights;
|
37
|
+
var random = new corkboard.WeightedRandomizer(weights);
|
38
|
+
var eid = data.eid;
|
39
|
+
var klass = ['entry', random.sample()].join(' ');
|
40
|
+
var entry = $('<li class="' + klass + '" data-eid="' + data.eid + '" data-tags="' + data.tags + '"><figure><img src="' + data.image + '"><p class="caption">' + data.caption + '</p></figure></li>');
|
41
|
+
|
42
|
+
posts.prepend(entry);
|
43
|
+
|
44
|
+
if(this.callback) {
|
45
|
+
this.callback.apply(this, [posts, entry]);
|
46
|
+
}
|
47
|
+
};
|
48
|
+
|
49
|
+
// // Uncomment to debug Pusher messages.
|
50
|
+
// Pusher.log = function(data) {
|
51
|
+
// console.log('\t\t', data);
|
52
|
+
// };
|
53
|
+
})(jQuery, jQuery.corkboard);
|
@@ -0,0 +1,41 @@
|
|
1
|
+
(function($, corkboard) {
|
2
|
+
// Ported to JS from weighted_randomizer Ruby gem.
|
3
|
+
function WeightedRandomizer(setup) {
|
4
|
+
this.normalize(setup);
|
5
|
+
}
|
6
|
+
|
7
|
+
// Surfaced as part of the corkboard namespace.
|
8
|
+
corkboard.WeightedRandomizer = WeightedRandomizer;
|
9
|
+
|
10
|
+
WeightedRandomizer.prototype.normalize = function normalize(setup) {
|
11
|
+
var dict = {};
|
12
|
+
var sum = 0.0;
|
13
|
+
|
14
|
+
$.each(setup, function(key, weight) {
|
15
|
+
sum += weight;
|
16
|
+
});
|
17
|
+
|
18
|
+
$.each(setup, function(key, weight) {
|
19
|
+
dict[key] = (weight / sum);
|
20
|
+
});
|
21
|
+
|
22
|
+
this.normalized = dict;
|
23
|
+
};
|
24
|
+
|
25
|
+
WeightedRandomizer.prototype.sample = function sample() {
|
26
|
+
var pick = Math.random();
|
27
|
+
var result;
|
28
|
+
|
29
|
+
$.each(this.normalized, function(key, weight) {
|
30
|
+
result = key;
|
31
|
+
|
32
|
+
if(pick <= weight) {
|
33
|
+
return false;
|
34
|
+
}
|
35
|
+
|
36
|
+
pick -= weight;
|
37
|
+
});
|
38
|
+
|
39
|
+
return result;
|
40
|
+
};
|
41
|
+
})(jQuery, jQuery.corkboard);
|
@@ -0,0 +1,88 @@
|
|
1
|
+
/**
|
2
|
+
* Stylesheet: Corkboard
|
3
|
+
*= require_self
|
4
|
+
* -------------------:--------------------------------------------------------
|
5
|
+
*/
|
6
|
+
article#corkboard {
|
7
|
+
position : relative;
|
8
|
+
}
|
9
|
+
|
10
|
+
|
11
|
+
/**
|
12
|
+
* @section: corkboard filters
|
13
|
+
* -------------------:--------------------------------------------------------
|
14
|
+
*/
|
15
|
+
nav.corkboard.filters {
|
16
|
+
position : absolute;
|
17
|
+
top : 10px;
|
18
|
+
right : 10px;
|
19
|
+
z-index : 1;
|
20
|
+
}
|
21
|
+
|
22
|
+
nav.corkboard.filters > ul {
|
23
|
+
list-style : none;
|
24
|
+
margin : 0;
|
25
|
+
}
|
26
|
+
|
27
|
+
nav.corkboard.filters li {
|
28
|
+
display : inline-block;
|
29
|
+
}
|
30
|
+
|
31
|
+
nav.corkboard.filters li > a {
|
32
|
+
text-decoration : none;
|
33
|
+
}
|
34
|
+
|
35
|
+
|
36
|
+
/**
|
37
|
+
* @section: corkboard posts
|
38
|
+
* -------------------:--------------------------------------------------------
|
39
|
+
*/
|
40
|
+
ul.corkboard.posts {
|
41
|
+
font-family : monospace;
|
42
|
+
list-style : none;
|
43
|
+
overflow : hidden;
|
44
|
+
margin : 0 auto;
|
45
|
+
padding : 0;
|
46
|
+
}
|
47
|
+
|
48
|
+
ul.corkboard.posts > li {
|
49
|
+
display : none;
|
50
|
+
float : left;
|
51
|
+
overflow : hidden;
|
52
|
+
margin : 2px;
|
53
|
+
}
|
54
|
+
|
55
|
+
ul.corkboard.posts > li.s {
|
56
|
+
width : 168px;
|
57
|
+
height : 168px;
|
58
|
+
}
|
59
|
+
|
60
|
+
ul.corkboard.posts > li.m {
|
61
|
+
width : 340px;
|
62
|
+
height : 340px;
|
63
|
+
}
|
64
|
+
|
65
|
+
ul.corkboard.posts > li.l {
|
66
|
+
width : 512px;
|
67
|
+
height : 512px;
|
68
|
+
}
|
69
|
+
|
70
|
+
ul.corkboard.posts figure {
|
71
|
+
margin : 0;
|
72
|
+
padding : 0;
|
73
|
+
width : 100%;
|
74
|
+
height : 100%;
|
75
|
+
}
|
76
|
+
|
77
|
+
ul.corkboard.posts img {
|
78
|
+
position : absolute;
|
79
|
+
top : -5%;
|
80
|
+
left : -5%;
|
81
|
+
width : 110%;
|
82
|
+
height : 110%;
|
83
|
+
max-width : none;
|
84
|
+
}
|
85
|
+
|
86
|
+
ul.corkboard.posts p.caption {
|
87
|
+
display : none;
|
88
|
+
}
|
@@ -0,0 +1,93 @@
|
|
1
|
+
/**
|
2
|
+
* Stylesheet: Corkboard Responsive
|
3
|
+
* -------------------:--------------------------------------------------------
|
4
|
+
*/
|
5
|
+
|
6
|
+
/**
|
7
|
+
* @section: mobile portrait to landscape
|
8
|
+
* -------------------:--------------------------------------------------------
|
9
|
+
*/
|
10
|
+
@media only screen and (max-width: 320px) {
|
11
|
+
ul.corkboard.posts > li.s,
|
12
|
+
ul.corkboard.posts > li.m,
|
13
|
+
ul.corkboard.posts > li.l {
|
14
|
+
width : 156px;
|
15
|
+
height : 156px;
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
/**
|
20
|
+
* @section: mobile portrait to landscape
|
21
|
+
* -------------------:--------------------------------------------------------
|
22
|
+
*/
|
23
|
+
@media only screen and (min-width: 321px) and (max-width: 480px) {
|
24
|
+
ul.corkboard.posts > li.s,
|
25
|
+
ul.corkboard.posts > li.m,
|
26
|
+
ul.corkboard.posts > li.l {
|
27
|
+
width : 178px;
|
28
|
+
height : 178px;
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
/**
|
33
|
+
* @section: mobile landscape to tablet portrait
|
34
|
+
* -------------------:--------------------------------------------------------
|
35
|
+
*/
|
36
|
+
@media only screen and (min-width: 481px) and (max-width: 768px) {
|
37
|
+
ul.corkboard.posts > li.s {
|
38
|
+
width : 136px;
|
39
|
+
height : 136px;
|
40
|
+
}
|
41
|
+
|
42
|
+
ul.corkboard.posts > li.m,
|
43
|
+
ul.corkboard.posts > li.l {
|
44
|
+
width : 276px;
|
45
|
+
height : 276px;
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
/**
|
50
|
+
* @section: tablet portrait to standard desktop
|
51
|
+
* -------------------:--------------------------------------------------------
|
52
|
+
*/
|
53
|
+
@media only screen and (min-width: 769px) and (max-width: 980px) {
|
54
|
+
ul.corkboard.posts > li.s {
|
55
|
+
width : 168px;
|
56
|
+
height : 168px;
|
57
|
+
}
|
58
|
+
|
59
|
+
ul.corkboard.posts > li.m,
|
60
|
+
ul.corkboard.posts > li.l {
|
61
|
+
width : 340px;
|
62
|
+
height : 340px;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* @section: standard desktop and larger
|
68
|
+
* -------------------:--------------------------------------------------------
|
69
|
+
*/
|
70
|
+
@media only screen and (min-width: 981px) {
|
71
|
+
ul.corkboard.posts > li.s {
|
72
|
+
width : 168px;
|
73
|
+
height : 168px;
|
74
|
+
}
|
75
|
+
|
76
|
+
ul.corkboard.posts > li.m {
|
77
|
+
width : 340px;
|
78
|
+
height : 340px;
|
79
|
+
}
|
80
|
+
|
81
|
+
ul.corkboard.posts > li.l {
|
82
|
+
width : 512px;
|
83
|
+
height : 512px;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
/**
|
88
|
+
* @section: HD
|
89
|
+
* -------------------:--------------------------------------------------------
|
90
|
+
*/
|
91
|
+
@media only screen and (min-width: 1200px) {
|
92
|
+
|
93
|
+
}
|