corkboard 0.1.0
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/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
|
+
}
|