Skip to content

Commit

Permalink
Made All JS Unobtrusive and added option to enabled / disable it on a…
Browse files Browse the repository at this point in the history
… per-site basis.
  • Loading branch information
Sutto committed Aug 3, 2008
1 parent d5009d6 commit ce4a9aa
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 41 deletions.
2 changes: 1 addition & 1 deletion app/controllers/items_controller.rb
Expand Up @@ -161,7 +161,7 @@ def category

def recently
@last_checked_at = current_user.last_checked_at
conditions = ['items.updated_at > ? or comments.created_at > ?', @last_checked_at, @last_checked_at]
conditions = ['items.updated_at > ? or comments.created_at > ?', @last_checked_at, @last_checked_at]
@items_count = current_user.starred_items.count(:conditions => conditions, :include => :comments)
@items = current_user.starred_items.find(:all, :conditions => conditions, :include => :comments)
current_user.update_attribute :last_checked_at, Time.now
Expand Down
45 changes: 26 additions & 19 deletions app/controllers/stars_controller.rb
@@ -1,25 +1,32 @@
class StarsController < ApplicationController

skip_before_filter :verify_authenticity_token

def new
@star = Star.new
@star.user_id = params[:user_id]
@star.item_id = params[:item_id]

if(@star.save)
@star.reload
plural = @star.item.stars.size == 1 ? "star" : "stars"
render :text => "#{@star.item.stars.size} #{plural}"
end
end
before_filter :login_required

def destroy
@star = Star.find_by_user_id_and_item_id(params[:user_id],params[:item_id])
@count = @star.item.stars.size - 1
if(@star.destroy)
plural = @count == 1 ? "star" : "stars"
render :text => "#{@count} #{plural}"
end
def add
@item = Item.find(params[:item_id])
@star = Star.new(:item => @item, :user => current_user)
saved = @star.save
respond_to do |wants|
wants.html { redirect_to @item }
wants.js do
stars_count = @item.stars.size + 1
saved ? render(:text => "#{stars_count} #{stars_count == 1 ? "star" : "stars"}") : head(:unprocessable_entity)
end
end
end

def remove
@item = Item.find(params[:item_id])
@star = @item.stars.find(:first, :conditions => ["user_id = ?", current_user.id])
destroyed = @star ? @star.destroy : false # If the star doesn't exist, add it
respond_to do |wants|
wants.html { redirect_to @item }
wants.js do
stars_count = @item.stars.size - 1
destroyed ? render(:text => "#{stars_count} #{stars_count == 1 ? "star" : "stars"}") : head(:unprocessable_entity)
end
end
end

end
6 changes: 6 additions & 0 deletions app/helpers/items_helper.rb
Expand Up @@ -7,4 +7,10 @@ def user_link(item)
item.byline
end
end

def star_link(item)
starred = item.is_starred_by_user(current_user)
path = starred ? item_remove_star_path(item) : item_add_star_path(item)
return content_tag(:span, link_to("#{starred ? 'unstar' : 'star'} this post", path, :class => item.starred_class(current_user)), :class => "star")
end
end
3 changes: 3 additions & 0 deletions app/models/star.rb
Expand Up @@ -3,4 +3,7 @@ class Star < ActiveRecord::Base
belongs_to :user
belongs_to :item, :counter_cache => true

# Prevent people starring something multiple times.
validates_uniqueness_of :item_id, :scope => :user_id, :on => :create, :message => "must be unique"

end
9 changes: 3 additions & 6 deletions app/views/items/_item.html.erb
@@ -1,4 +1,4 @@
<div class="post" style="position: relative;" onmouseover="$('<%= "#" + dom_id(item) + ".admin-actions" %>').show();" onmouseout="$('<%= "#" + dom_id(item) + ".admin-actions"%>').hide();" id="item_wrapper_for_<%= item.id %>">
<div class="post" style="position: relative;" id="item_wrapper_for_<%= item.id %>">

<div class="entry">
<%= safe(item.content) %>
Expand All @@ -8,11 +8,9 @@
<div class="metadata">
Posted by <%= user_link(item) %>
at <%= item.created_at.strftime("%R") %>
- <%= link_to pluralize(item.comments.size, 'comment'), "#{item_path(item)}/#comments" %>. <%= link_to pluralize(item.stars.size, 'star'), "#{item_path(item)}/#stars", :id => "item_#{item.id}-star-count"%>
- <%= link_to pluralize(item.comments.size, 'comment'), "#{item_path(item)}/#comments" %> &amp; <%= link_to pluralize(item.stars.size, 'star'), "#{item_path(item)}/#stars", :id => "item_#{item.id}-star-count"%>
<% if logged_in? %>
&ndash; <span class="star">
<a href="#" class="<%= item.starred_class(current_user) %>" onClick="<%= "star(#{item.id}, #{current_user.id}, #{item.is_starred_by_user(current_user)})" %>"><%= item.is_starred_by_user(current_user) ? "unstar" : "star" -%> this post</a>
</span>
&ndash; <%= star_link item %>
<% end %>
</div>

Expand All @@ -26,4 +24,3 @@
<% end %>
<% end %>
</div>

2 changes: 1 addition & 1 deletion app/views/items/recently.html.erb
Expand Up @@ -5,7 +5,7 @@
<% if @items.empty? %>
<p>Nothings happened in your flow recently - try starring some more stories for more activity.</p>
<% else %>
<br>
<br />
<% end %>
<% @items.each do |item| %>
Expand Down
3 changes: 2 additions & 1 deletion app/views/layouts/main.html.erb
Expand Up @@ -12,7 +12,7 @@
<% if @noindex %><meta name="robots" content="noindex, nofollow" /><% end -%>
<link rel="alternate" type="application/rss+xml" title="<%= APP_CONFIG[:app_title] %>" href="<%= APP_CONFIG[:rss_url] %>" />
<%= stylesheet_link_tag "main" %>
<%= javascript_include_tag :defaults, :cache => true%>
<%= javascript_include_tag :defaults, :cache => true if APP_CONFIG[:uses_javascript] %>
</head>

<body class="<%= @body_class %> <%= APP_CONFIG[:body_class] %>">
Expand All @@ -23,6 +23,7 @@
<%= link_to 'Home', root_url %> | <a href="<%= APP_CONFIG[:rss_url] %>">RSS Feed</a> |
<%= link_to 'Post Item', new_item_path, :class => 'rubyred' %> |
<% if logged_in? %>
<%= link_to 'Recently', recently_items_url -%> |
<%= link_to 'Log Out', logout_url %>
<% else %>
<%= link_to 'Sign Up', signup_url %> | <%= link_to 'Log In', login_url %>
Expand Down
1 change: 1 addition & 0 deletions config/pythonflow.yml
Expand Up @@ -7,6 +7,7 @@ rss_url: http://feeds.feedburner.com/Pythonflow
meta_keywords: "python, python news, python blog, django"
meta_author: "Peter Cooper"
body_class: pythonflow
uses_javascript: false
ga: UA-2237791-8
sidebar: |-
<h3>What?</h3><p>PythonFlow is an experimental community driven Python links site. Items are not added automatically, but chosen and summarized by community members, resulting in a better quality and context than <a href="http://python.reddit.com/">social bookmarking sites</a> can offer.</p>
Expand Down
8 changes: 5 additions & 3 deletions config/routes.rb
Expand Up @@ -3,6 +3,8 @@

map.resources :items, :collection => {:recently => :get} do |items|
items.resources :comments
items.add_star '/star/add', :controller => "stars", :action => "add"
items.remove_star '/star/remove', :controller => "stars", :action => "remove"
end

map.resources :categories
Expand All @@ -12,13 +14,13 @@


map.signup '/signup', :controller => 'users', :action => 'new'
map.login '/login', :controller => 'session', :action => 'new'
map.login '/login', :controller => 'session', :action => 'new'
map.logout '/logout', :controller => 'session', :action => 'destroy'

map.tag '/tag/:id', :controller => 'items', :action => 'list_for_tags'
map.tag '/tag/:id', :controller => 'items', :action => 'list_for_tags'
map.tags '/tags/:id', :controller => 'items', :action => 'list_for_tags'
map.tags_by_folders '/tags/*id', :controller => 'items', :action => 'list_for_tags'
map.search '/search/:id', :controller => 'items', :action => 'search'
map.search '/search/:id', :controller => 'items', :action => 'search'
map.category '/category/:id', :controller => 'items', :action => 'category'

map.connect '/page/:page', :controller => 'items', :action => 'index'
Expand Down
1 change: 1 addition & 0 deletions config/rubyflow.yml
Expand Up @@ -7,6 +7,7 @@ rss_url: http://feeds.feedburner.com/Rubyflow
meta_keywords: "ruby, ruby news, ruby blog, rails, rubyonrails"
meta_author: "Peter Cooper"
body_class: rubyflow
uses_javascript: true
# ga: UA-2237791-7
sidebar: |-
<h3>What?</h3><p>RubyFlow is a community driven Ruby links site. Items are not added automatically, but chosen and summarized by the community, resulting in a higher quality of links than <a href="http://ruby.reddit.com/">social bookmarking sites.</a></p>
Expand Down
65 changes: 56 additions & 9 deletions public/javascripts/application.js
@@ -1,22 +1,69 @@
// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults
var id_pattern = /item_wrapper_for_(\d+)/i;

function star (item_id, user_id, is_starred) {
function star(item_id, is_starred) {
if(is_starred) {
var url = "/stars/destroy";
var url = "/items/" + item_id + "/star/remove";
var jsOptions = "false";
} else {
var url = "/stars/new"
var url = "/items/" + item_id + "/star/add"
var jsOptions = "true";
}

// Send the data to the server

$.post(url, {item_id: item_id, user_id: user_id}, function(data) {
$.get(url, {}, function(data) {
$("#item_wrapper_for_" + item_id + " .star a").toggleClass("starred").html((jsOptions == "false" ? "" : "un") + "star this post");
$("#item_" + item_id + "-star-count").html(data);
$("#item_wrapper_for_" + item_id + " .star a").toggleClass("starred");
$("#item_wrapper_for_" + item_id + " .star a").attr("onClick", "star(" + item_id + ", "+ user_id +", " + jsOptions +")");
$("#item_wrapper_for_" + item_id + " .star a").html((jsOptions == "false" ? "" : "un") + "star this post");
}, "text");

}

function setupAdminAreaToggles() {
$(".admin-actions").each(function(){
var current = $(this);
var parent = current.parent();
parent.mouseover(function() {
current.show();
});
parent.mouseout(function() {
current.hide();
});
}).hide();
}

// Someone might want to refactor / rewrite this to make it nicer.
function setupItemAjaxSubmit() {
var posts = $(".post");
posts.each(function() {
var post = $(this);
setStarFor(post);
});
}

function setStarFor(post) {
var match = id_pattern.exec(post.attr("id"));
// Check if we can extract the id.
if(match != null) {
var item_id = parseInt(match[1], 10);
var star_link = post.find(".star a");
star_link.click(function(){
star(item_id, !(star_link.html().match(/un/) == null));
return false;
});
}
}

// Set jQuery to automatically use the correct
// accepts header so that we can use respond_to
// with js.
jQuery.ajaxSetup({
'beforeSend': function(xhr) {xhr.setRequestHeader("Accept",
"text/javascript")}
})


// Make all JS behaviour unobtrusive.
$(function() {
setupAdminAreaToggles();
setupItemAjaxSubmit();
});
1 change: 0 additions & 1 deletion public/stylesheets/main.css
Expand Up @@ -378,7 +378,6 @@ H1 A { color: #c00; }
position: absolute;
top: 12px;
right: 12px;
display: none;
}

/* @end */
Expand Down

0 comments on commit ce4a9aa

Please sign in to comment.