Merge branch 'hovercard'
This commit is contained in:
commit
dfd30a71bf
20 changed files with 313 additions and 61 deletions
|
|
@ -6,6 +6,8 @@
|
||||||
class AspectMembershipsController < ApplicationController
|
class AspectMembershipsController < ApplicationController
|
||||||
before_filter :authenticate_user!
|
before_filter :authenticate_user!
|
||||||
|
|
||||||
|
respond_to :html, :json, :js
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
#note :id is garbage
|
#note :id is garbage
|
||||||
|
|
||||||
|
|
@ -17,14 +19,14 @@ class AspectMembershipsController < ApplicationController
|
||||||
|
|
||||||
if membership && membership.destroy
|
if membership && membership.destroy
|
||||||
@aspect = membership.aspect
|
@aspect = membership.aspect
|
||||||
|
|
||||||
flash.now[:notice] = I18n.t 'aspect_memberships.destroy.success'
|
flash.now[:notice] = I18n.t 'aspect_memberships.destroy.success'
|
||||||
|
|
||||||
respond_to do |format|
|
respond_with do |format|
|
||||||
format.js { }
|
format.all{ }
|
||||||
format.html{
|
format.json{ render :json => {
|
||||||
redirect_to :back
|
:person_id => @person_id,
|
||||||
}
|
:aspect_ids => @contact.aspects.map{|a| a.id}
|
||||||
|
} }
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
@ -44,12 +46,12 @@ class AspectMembershipsController < ApplicationController
|
||||||
@aspect = current_user.aspects.where(:id => params[:aspect_id]).first
|
@aspect = current_user.aspects.where(:id => params[:aspect_id]).first
|
||||||
|
|
||||||
if @contact = current_user.share_with(@person, @aspect)
|
if @contact = current_user.share_with(@person, @aspect)
|
||||||
|
|
||||||
flash.now[:notice] = I18n.t 'aspects.add_to_aspect.success'
|
flash.now[:notice] = I18n.t 'aspects.add_to_aspect.success'
|
||||||
|
respond_with AspectMembership.where(:contact_id => @contact.id, :aspect_id => @aspect.id).first
|
||||||
else
|
else
|
||||||
flash[:error] = I18n.t 'contacts.create.failure'
|
flash[:error] = I18n.t 'contacts.create.failure'
|
||||||
redirect_to :back
|
#TODO(dan) take this out once the .js template is removed
|
||||||
|
render :nothing => true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,4 +24,5 @@ class ContactsController < ApplicationController
|
||||||
@contacts = current_user.contacts.sharing.includes(:aspect_memberships)
|
@contacts = current_user.contacts.sharing.includes(:aspect_memberships)
|
||||||
render :layout => false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,12 @@ class PeopleController < ApplicationController
|
||||||
if params[:only_posts]
|
if params[:only_posts]
|
||||||
render :partial => 'shared/stream', :locals => {:posts => @posts}
|
render :partial => 'shared/stream', :locals => {:posts => @posts}
|
||||||
else
|
else
|
||||||
respond_with @person, :locals => {:post_type => :all}
|
respond_to do |format|
|
||||||
|
format.all { respond_with @person, :locals => {:post_type => :all} }
|
||||||
|
format.json {
|
||||||
|
render :json => @person.to_json
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
@ -115,6 +120,7 @@ class PeopleController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def retrieve_remote
|
def retrieve_remote
|
||||||
if params[:diaspora_handle]
|
if params[:diaspora_handle]
|
||||||
webfinger(params[:diaspora_handle], :single_aspect_form => true)
|
webfinger(params[:diaspora_handle], :single_aspect_form => true)
|
||||||
|
|
@ -131,7 +137,6 @@ class PeopleController < ApplicationController
|
||||||
@aspect = :profile
|
@aspect = :profile
|
||||||
@contacts_of_contact = @contact.contacts.paginate(:page => params[:page], :per_page => (params[:limit] || 15))
|
@contacts_of_contact = @contact.contacts.paginate(:page => params[:page], :per_page => (params[:limit] || 15))
|
||||||
@hashes = hashes_for_people @contacts_of_contact, @aspects
|
@hashes = hashes_for_people @contacts_of_contact, @aspects
|
||||||
@contact = current_user.contact_for(@person)
|
|
||||||
@aspects_with_person = @contact.aspects
|
@aspects_with_person = @contact.aspects
|
||||||
@aspect_ids = @aspects_with_person.map(&:id)
|
@aspect_ids = @aspects_with_person.map(&:id)
|
||||||
else
|
else
|
||||||
|
|
@ -139,8 +144,16 @@ class PeopleController < ApplicationController
|
||||||
redirect_to people_path
|
redirect_to people_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def aspect_membership_dropdown
|
||||||
|
@person = Person.find(params[:id])
|
||||||
|
@contact = current_user.contact_for(@person) || Contact.new
|
||||||
|
render :partial => 'aspect_memberships/aspect_dropdown', :locals => {:contact => @contact, :person => @person, :hang => 'left'}
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def webfinger(account, opts = {})
|
def webfinger(account, opts = {})
|
||||||
Resque.enqueue(Job::SocketWebfinger, current_user.id, account, opts)
|
Resque.enqueue(Job::SocketWebfinger, current_user.id, account, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,8 @@ module ApplicationHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def person_link(person, opts={})
|
def person_link(person, opts={})
|
||||||
|
opts[:class] ||= ""
|
||||||
|
opts[:class] << " self" if current_user.person == person
|
||||||
"<a href='/people/#{person.id}' class='#{opts[:class]}'>
|
"<a href='/people/#{person.id}' class='#{opts[:class]}'>
|
||||||
#{h(person.name)}
|
#{h(person.name)}
|
||||||
</a>".html_safe
|
</a>".html_safe
|
||||||
|
|
|
||||||
|
|
@ -68,16 +68,12 @@ module AspectGlobalHelper
|
||||||
def aspect_dropdown_list_item(aspect, contact, person)
|
def aspect_dropdown_list_item(aspect, contact, person)
|
||||||
checked = (contact.persisted? && contact.aspect_memberships.detect{ |am| am.aspect_id == aspect.id})
|
checked = (contact.persisted? && contact.aspect_memberships.detect{ |am| am.aspect_id == aspect.id})
|
||||||
klass = checked ? "selected" : ""
|
klass = checked ? "selected" : ""
|
||||||
hidden = !checked ? "hidden" : ""
|
|
||||||
|
|
||||||
str = <<LISTITEM
|
str = <<LISTITEM
|
||||||
<li data-aspect_id=#{aspect.id} class='#{klass}'>
|
<li data-aspect_id=#{aspect.id} class='#{klass}'>
|
||||||
<img src='/images/icons/check_yes_ok.png' width=18 height=18 class='check #{hidden}'/>
|
<img src='/images/icons/check_yes_ok.png' width=18 height=18 class='check'/>
|
||||||
<img src='/images/icons/check_yes_ok_white.png' width=18 height=18 class='checkWhite'/>
|
<img src='/images/icons/check_yes_ok_white.png' width=18 height=18 class='checkWhite'/>
|
||||||
#{aspect.name}
|
#{aspect.name}
|
||||||
<div class=\"hidden\">
|
|
||||||
#{aspect_membership_button(aspect, contact, person)}
|
|
||||||
</div>
|
|
||||||
</li>
|
</li>
|
||||||
LISTITEM
|
LISTITEM
|
||||||
str.html_safe
|
str.html_safe
|
||||||
|
|
|
||||||
|
|
@ -16,4 +16,12 @@ class AspectMembership < ActiveRecord::Base
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def as_json(opts={})
|
||||||
|
{
|
||||||
|
:id => self.id,
|
||||||
|
:person_id => self.person.id,
|
||||||
|
:contact_id => self.contact.id,
|
||||||
|
:aspect_ids => self.contact.aspects.map{|a| a.id}
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -220,12 +220,13 @@ class Person < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def as_json(opts={})
|
def as_json(opts={})
|
||||||
{
|
json = {
|
||||||
:id => self.guid,
|
:id => self.id,
|
||||||
:name => self.name,
|
:name => self.name,
|
||||||
:avatar => self.profile.image_url(:thumb_small),
|
:avatar => self.profile.image_url(:thumb_small),
|
||||||
:handle => self.diaspora_handle,
|
:handle => self.diaspora_handle,
|
||||||
:url => "/people/#{self.id}"
|
:url => "/people/#{self.id}",
|
||||||
|
:hashtags => self.profile.tags.map{|t| "##{t.name}"}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,4 +19,3 @@ if($('#aspects_list').length == 1) {
|
||||||
};
|
};
|
||||||
|
|
||||||
element.fadeTo(200,1);
|
element.fadeTo(200,1);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
= person_image_link(comment.author)
|
= person_image_link(comment.author)
|
||||||
.content
|
.content
|
||||||
%span.from
|
%span.from
|
||||||
= person_link(comment.author)
|
= person_link(comment.author, :class => "hovercardable")
|
||||||
|
|
||||||
%span{:class => direction_for(comment.text)}
|
%span{:class => direction_for(comment.text)}
|
||||||
= markdownify(comment.text, :youtube_maps => comment.youtube_titles)
|
= markdownify(comment.text, :youtube_maps => comment.youtube_titles)
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,17 @@
|
||||||
.ajax_loader
|
.ajax_loader
|
||||||
= image_tag("ajax-loader.gif")
|
= image_tag("ajax-loader.gif")
|
||||||
|
|
||||||
|
#hovercard_container
|
||||||
|
#hovercard
|
||||||
|
%img.avatar
|
||||||
|
%h4
|
||||||
|
%a.person
|
||||||
|
#hovercard_dropdown_container
|
||||||
|
|
||||||
|
.hovercard_footer
|
||||||
|
.footer_container
|
||||||
|
.hashtags
|
||||||
|
|
||||||
%ul#user_menu.dropdown
|
%ul#user_menu.dropdown
|
||||||
%li
|
%li
|
||||||
.right
|
.right
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,9 @@
|
||||||
- if current_user
|
- if current_user
|
||||||
- contact = current_user.contacts.find_by_person_id(person.id)
|
- contact = current_user.contacts.find_by_person_id(person.id)
|
||||||
- contact ||= Contact.new(:person => person)
|
- contact ||= Contact.new(:person => person)
|
||||||
- unless person == current_user.person
|
|
||||||
.right
|
|
||||||
= render 'aspect_memberships/aspect_dropdown', :contact => contact, :person => person, :hang => 'left'
|
|
||||||
|
|
||||||
.content
|
.content
|
||||||
%span.from
|
%span.from
|
||||||
=person_link(person)
|
=person_link(person, :class => "hovercardable")
|
||||||
.info
|
|
||||||
= person.profile.format_tags(person.profile.tag_string)
|
|
||||||
= will_paginate people, :params => {:controller => 'people', :action => 'tag_index'}
|
= will_paginate people, :params => {:controller => 'people', :action => 'tag_index'}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
.content
|
.content
|
||||||
%div.post_initial_info
|
%div.post_initial_info
|
||||||
%span.from
|
%span.from
|
||||||
= person_link(post.author, :class => 'author')
|
= person_link(post.author, :class => 'hovercardable')
|
||||||
%time.time.timeago{:datetime => post.created_at, :integer => time_for_sort(post).to_i}
|
%time.time.timeago{:datetime => post.created_at, :integer => time_for_sort(post).to_i}
|
||||||
%span.details
|
%span.details
|
||||||
\-
|
\-
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,10 @@ javascripts:
|
||||||
- public/javascripts/widgets/infinite-scroll.js
|
- public/javascripts/widgets/infinite-scroll.js
|
||||||
- public/javascripts/widgets/directionDetector.js
|
- public/javascripts/widgets/directionDetector.js
|
||||||
- public/javascripts/widgets/notifications.js
|
- public/javascripts/widgets/notifications.js
|
||||||
- public/javascripts/widgets/notifications-badge.js
|
|
||||||
- public/javascripts/widgets/flashes.js
|
- public/javascripts/widgets/flashes.js
|
||||||
- public/javascripts/widgets/post.js
|
- public/javascripts/widgets/post.js
|
||||||
|
- public/javascripts/widgets/hovercard.js
|
||||||
|
- public/javascripts/widgets/notifications-badge.js
|
||||||
- public/javascripts/view.js
|
- public/javascripts/view.js
|
||||||
- public/javascripts/stream.js
|
- public/javascripts/stream.js
|
||||||
- public/javascripts/content-updater.js
|
- public/javascripts/content-updater.js
|
||||||
|
|
@ -45,18 +46,27 @@ javascripts:
|
||||||
- public/javascripts/login.js
|
- public/javascripts/login.js
|
||||||
mobile:
|
mobile:
|
||||||
- public/javascripts/vendor/jquery152.min.js
|
- public/javascripts/vendor/jquery152.min.js
|
||||||
- public/javascripts/custom-mobile-scripting.js
|
|
||||||
- public/javascripts/vendor/jquery.mobile-1.0a4.js
|
- public/javascripts/vendor/underscore.js
|
||||||
- public/javascripts/jquery.infinitescroll-custom.js
|
- public/javascripts/vendor/backbone-min.js
|
||||||
|
|
||||||
|
- public/javascripts/vendor/Mustache.js
|
||||||
|
|
||||||
|
- public/javascripts/mobile/app.js
|
||||||
|
- public/javascripts/mobile/helpers.js
|
||||||
|
- public/javascripts/mobile/models/*
|
||||||
|
- public/javascripts/mobile/collections/*
|
||||||
|
- public/javascripts/mobile/controllers/*
|
||||||
|
- public/javascripts/mobile/views/*
|
||||||
|
|
||||||
- public/javascripts/diaspora.js
|
- public/javascripts/diaspora.js
|
||||||
- public/javascripts/widgets/i18n.js
|
- public/javascripts/widgets/i18n.js
|
||||||
- public/javascripts/widgets/infinite-scroll.js
|
|
||||||
- public/javascripts/rails.js
|
|
||||||
mailchimp:
|
mailchimp:
|
||||||
- public/javascripts/vendor/mailchimp/jquery.form.js
|
- public/javascripts/vendor/mailchimp/jquery.form.js
|
||||||
- public/javascripts/vendor/mailchimp/jquery.validate.js
|
- public/javascripts/vendor/mailchimp/jquery.validate.js
|
||||||
- public/javascripts/vendor/mailchimp/jquery126.min.js
|
- public/javascripts/vendor/mailchimp/jquery126.min.js
|
||||||
aspects:
|
aspects:
|
||||||
|
- public/javascripts/aspect-edit.js
|
||||||
- public/javascripts/contact-list.js
|
- public/javascripts/contact-list.js
|
||||||
finder:
|
finder:
|
||||||
- public/javascripts/friend-finder.js
|
- public/javascripts/friend-finder.js
|
||||||
|
|
@ -86,12 +96,9 @@ stylesheets:
|
||||||
- public/stylesheets/vendor/fileuploader.css
|
- public/stylesheets/vendor/fileuploader.css
|
||||||
- public/stylesheets/vendor/tipsy.css
|
- public/stylesheets/vendor/tipsy.css
|
||||||
- public/stylesheets/vendor/autoSuggest.css
|
- public/stylesheets/vendor/autoSuggest.css
|
||||||
|
|
||||||
popup:
|
|
||||||
- public/stylesheets/application.css
|
|
||||||
- public/stylesheets/popup.css
|
|
||||||
- public/stylesheets/ui.css
|
|
||||||
|
|
||||||
rtl:
|
rtl:
|
||||||
- public/stylesheets/rtl.css
|
- public/stylesheets/rtl.css
|
||||||
|
|
||||||
|
mobile:
|
||||||
|
- public/stylesheets/mobile.css
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,8 @@ Diaspora::Application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
get "people/:id/aspect_membership_button" => "people#aspect_membership_dropdown", :as => "aspect_membership_button"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,9 @@ var ContactEdit = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
updateNumber: function(personId){
|
updateNumber: function(dropdown, personId, number){
|
||||||
var dropdown = $(".dropdown_list[data-person_id=" + personId.toString() +"]"),
|
var button = dropdown.parents(".dropdown").children('.button.toggle'),
|
||||||
number = dropdown.find(".selected").length,
|
replacement;
|
||||||
button = dropdown.parents(".dropdown").children('.button.toggle');
|
|
||||||
|
|
||||||
var replacement;
|
|
||||||
|
|
||||||
if (number == 0) {
|
if (number == 0) {
|
||||||
button.removeClass("in_aspects");
|
button.removeClass("in_aspects");
|
||||||
|
|
@ -27,7 +24,7 @@ var ContactEdit = {
|
||||||
}else if (number > 3) {
|
}else if (number > 3) {
|
||||||
replacement = Diaspora.widgets.i18n.t('aspect_dropdown.toggle.many', { count: number.toString()})
|
replacement = Diaspora.widgets.i18n.t('aspect_dropdown.toggle.many', { count: number.toString()})
|
||||||
}else {
|
}else {
|
||||||
//the above one are a totalogy, but I want to have them here once for once we figure out a neat way i18n them
|
//the above one are a tautology, but I want to have them here once for once we figure out a neat way i18n them
|
||||||
replacement = Diaspora.widgets.i18n.t('aspect_dropdown.toggle.other', { count: number.toString()})
|
replacement = Diaspora.widgets.i18n.t('aspect_dropdown.toggle.other', { count: number.toString()})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,7 +33,6 @@ var ContactEdit = {
|
||||||
|
|
||||||
toggleCheckbox:
|
toggleCheckbox:
|
||||||
function(check){
|
function(check){
|
||||||
check.toggleClass('hidden');
|
|
||||||
check.parent('li').toggleClass('selected');
|
check.parent('li').toggleClass('selected');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -44,10 +40,20 @@ var ContactEdit = {
|
||||||
var button = li.find('.button');
|
var button = li.find('.button');
|
||||||
if(button.hasClass('disabled') || li.hasClass('newItem')){ return; }
|
if(button.hasClass('disabled') || li.hasClass('newItem')){ return; }
|
||||||
|
|
||||||
var checkbox = li.find('img.check');
|
var checkbox = li.find('img.check'),
|
||||||
ContactEdit.toggleCheckbox(checkbox);
|
selected = li.hasClass("selected"),
|
||||||
|
routedId = selected ? "/42" : "";
|
||||||
|
|
||||||
$.fn.callRemote.apply(button);
|
$.post("/aspect_memberships" + routedId + ".json", {
|
||||||
|
"aspect_id": li.data("aspect_id"),
|
||||||
|
"person_id": li.parent().data("person_id"),
|
||||||
|
"_method": (selected) ? "DELETE" : "POST"
|
||||||
|
}, function(aspectMembership) {
|
||||||
|
ContactEdit.toggleCheckbox(checkbox);
|
||||||
|
ContactEdit.updateNumber(li.closest(".dropdown_list"), li.parent().data("person_id"), aspectMembership.aspect_ids.length);
|
||||||
|
|
||||||
|
Diaspora.widgets.publish("aspectDropdown/updated", [li.parent().data("person_id"), li.parents(".dropdown").get(0).outerHTML]);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,11 +39,14 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
Diaspora.WidgetCollection.prototype.subscribe = function(id, callback, context) {
|
Diaspora.WidgetCollection.prototype.subscribe = function(id, callback, context) {
|
||||||
this.eventsContainer.bind(id, $.proxy(callback, context));
|
var ids = id.split(" ");
|
||||||
|
for(var id in ids) {
|
||||||
|
this.eventsContainer.bind(ids[id], $.proxy(callback, context));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Diaspora.WidgetCollection.prototype.publish = function(id) {
|
Diaspora.WidgetCollection.prototype.publish = function(id, args) {
|
||||||
this.eventsContainer.trigger(id);
|
this.eventsContainer.trigger(id, args);
|
||||||
};
|
};
|
||||||
|
|
||||||
Diaspora.widgets = new Diaspora.WidgetCollection();
|
Diaspora.widgets = new Diaspora.WidgetCollection();
|
||||||
|
|
|
||||||
136
public/javascripts/widgets/hovercard.js
Normal file
136
public/javascripts/widgets/hovercard.js
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
(function() {
|
||||||
|
var HoverCard = function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.jXHRs = [];
|
||||||
|
|
||||||
|
this.start = function() {
|
||||||
|
self.personCache = new this.Cache();
|
||||||
|
self.dropdownCache = new this.Cache();
|
||||||
|
|
||||||
|
var card = $("#hovercard");
|
||||||
|
self.hoverCard = {
|
||||||
|
tip: $("#hovercard_container"),
|
||||||
|
dropdownContainer: $("#hovercard_dropdown_container"),
|
||||||
|
offset: {
|
||||||
|
left: -10,
|
||||||
|
top: 13
|
||||||
|
},
|
||||||
|
personLink: card.find("a.person"),
|
||||||
|
avatar: card.find(".avatar"),
|
||||||
|
dropdown: card.find(".dropdown_list"),
|
||||||
|
hashtags: card.find(".hashtags"),
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document.body).delegate("a.hovercardable:not(.self)", "hover", self.handleHoverEvent);
|
||||||
|
self.hoverCard.tip.hover(self.hoverCardHover, self.clearTimeout);
|
||||||
|
|
||||||
|
Diaspora.widgets.subscribe("aspectDropdown/updated aspectDropdown/blurred", function(evt, personId, dropdownHtml) {
|
||||||
|
self.dropdownCache.cache["/people/" + personId + "/aspect_membership_button"] = $(dropdownHtml).removeClass("active").get(0).outerHTML;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.handleHoverEvent = function(evt) {
|
||||||
|
self.target = $(evt.target);
|
||||||
|
|
||||||
|
if(evt.type === "mouseenter") {
|
||||||
|
self.startHover();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.clearTimeout(evt);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.startHover = function(evt) {
|
||||||
|
if(!self.hoverCardTimeout) {
|
||||||
|
self.clearTimeout(false);
|
||||||
|
}
|
||||||
|
self.timeout = setTimeout(self.showHoverCard, 600);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.showHoverCard = function() {
|
||||||
|
self.hoverCard.tip.hide();
|
||||||
|
self.hoverCard.tip.prependTo(self.target.parent());
|
||||||
|
|
||||||
|
self.personCache.get(self.target.attr("href") + ".json", function(person) {
|
||||||
|
self.populateHovercard(person);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.populateHovercard = function(person) {
|
||||||
|
var position = self.target.position();
|
||||||
|
self.hoverCard.tip.css({
|
||||||
|
left: position.left + self.hoverCard.offset.left,
|
||||||
|
top: position.top + self.hoverCard.offset.top
|
||||||
|
});
|
||||||
|
|
||||||
|
self.hoverCard.avatar.attr("src", person.avatar);
|
||||||
|
self.hoverCard.personLink.attr("href", person.url);
|
||||||
|
self.hoverCard.personLink.text(person.name);
|
||||||
|
self.hoverCard.dropdown.attr("data-person-id", person.id);
|
||||||
|
|
||||||
|
$.each(person.hashtags, function(index, hashtag) {
|
||||||
|
self.hoverCard.hashtags.append(
|
||||||
|
$("<a/>", {
|
||||||
|
href: "/tags/" + hashtag.substring(1)
|
||||||
|
}).text(hashtag)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.dropdownCache.get(self.target.attr("href") + "/aspect_membership_button", function(dropdown) {
|
||||||
|
self.hoverCard.dropdownContainer.html(dropdown);
|
||||||
|
self.hoverCard.tip.fadeIn(140);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.clearTimeout = function(delayed) {
|
||||||
|
self.personCache.clearjXHRs();
|
||||||
|
self.dropdownCache.clearjXHRs();
|
||||||
|
|
||||||
|
function callback() {
|
||||||
|
self.timeout = clearTimeout(self.timeout);
|
||||||
|
self.hoverCard.tip.hide();
|
||||||
|
self.hoverCard.dropdownContainer.html("");
|
||||||
|
};
|
||||||
|
|
||||||
|
if((typeof delayed === "boolean" && delayed) || (typeof delayed === "object" && delayed.type === "mouseleave")) {
|
||||||
|
self.hoverCardTimeout = setTimeout(callback, 200);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.hoverCardHover = function() {
|
||||||
|
self.hoverCardTimeout = clearTimeout(self.hoverCardTimeout);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Cache = function() {
|
||||||
|
var self = this;
|
||||||
|
this.cache = {};
|
||||||
|
this.jXHRs = [];
|
||||||
|
|
||||||
|
this.get = function(key, callback) {
|
||||||
|
if(typeof self.cache[key] === "undefined") {
|
||||||
|
self.jXHRs.push($.get(key, function(response) {
|
||||||
|
self.cache[key] = response;
|
||||||
|
callback(response);
|
||||||
|
self.jXHRs.shift();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callback(self.cache[key]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.clearjXHRs = function() {
|
||||||
|
$.each(self.jXHRs, function(index, jXHR) {
|
||||||
|
jXHR.abort();
|
||||||
|
});
|
||||||
|
self.jXHRs = [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Diaspora.widgets.add("hoverCard", HoverCard);
|
||||||
|
})();
|
||||||
|
|
@ -3066,6 +3066,62 @@ ul.left_nav
|
||||||
:margin
|
:margin
|
||||||
:top 30px
|
:top 30px
|
||||||
|
|
||||||
.tags_people
|
#hovercard
|
||||||
.dropdown
|
@include border-radius(2px)
|
||||||
:display none
|
@include box-shadow(0,0,5px,#666)
|
||||||
|
|
||||||
|
:position relative
|
||||||
|
|
||||||
|
.avatar
|
||||||
|
:position relative
|
||||||
|
:height 70px
|
||||||
|
:width 70px
|
||||||
|
:margin
|
||||||
|
:right 10px
|
||||||
|
|
||||||
|
:background
|
||||||
|
:color $background
|
||||||
|
|
||||||
|
:padding 5px
|
||||||
|
:bottom 55px
|
||||||
|
|
||||||
|
:border 1px solid #999
|
||||||
|
|
||||||
|
:width 220px
|
||||||
|
|
||||||
|
.hovercard_footer
|
||||||
|
:position absolute
|
||||||
|
:bottom 0
|
||||||
|
:left 0
|
||||||
|
:background
|
||||||
|
:color #eee
|
||||||
|
:width 100%
|
||||||
|
|
||||||
|
:height 20px
|
||||||
|
:min-height 20px
|
||||||
|
|
||||||
|
:font
|
||||||
|
:size smaller
|
||||||
|
|
||||||
|
:border
|
||||||
|
:top 1px solid #ccc
|
||||||
|
|
||||||
|
.footer_container
|
||||||
|
:padding 2px 5px
|
||||||
|
|
||||||
|
.hashtags
|
||||||
|
:overflow hidden
|
||||||
|
:white-space nowrap
|
||||||
|
:text-overflow ellipsis
|
||||||
|
|
||||||
|
a
|
||||||
|
:color #999
|
||||||
|
:margin
|
||||||
|
:right 4px
|
||||||
|
|
||||||
|
#hovercard_container
|
||||||
|
:padding 10px
|
||||||
|
:top 5px
|
||||||
|
:position absolute
|
||||||
|
:display none
|
||||||
|
:z-index 10
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,10 @@
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
:text-decoration none
|
:text-decoration none
|
||||||
|
|
||||||
|
&:not(.selected)
|
||||||
|
.check
|
||||||
|
:display none
|
||||||
|
|
||||||
&.hang_right
|
&.hang_right
|
||||||
.wrapper
|
.wrapper
|
||||||
|
|
@ -170,7 +173,7 @@
|
||||||
@include border-radius(3px, 3px, 0, 0)
|
@include border-radius(3px, 3px, 0, 0)
|
||||||
:border 1px solid #444
|
:border 1px solid #444
|
||||||
:bottom none
|
:bottom none
|
||||||
|
|
||||||
.selected
|
.selected
|
||||||
:font-weight bold
|
:font-weight bold
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@ describe AspectMembershipsController do
|
||||||
}.should change{
|
}.should change{
|
||||||
alice.contact_for(bob.person).aspect_memberships.count
|
alice.contact_for(bob.person).aspect_memberships.count
|
||||||
}.by(1)
|
}.by(1)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a contact' do
|
it 'creates a contact' do
|
||||||
|
|
@ -62,8 +61,19 @@ describe AspectMembershipsController do
|
||||||
:aspect_id => @aspect0.id
|
:aspect_id => @aspect0.id
|
||||||
flash[:error].should_not be_empty
|
flash[:error].should_not be_empty
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
|
context 'json' do
|
||||||
|
it 'returns a list of aspect ids for the person' do
|
||||||
|
post :create,
|
||||||
|
:format => 'json',
|
||||||
|
:person_id => @person.id,
|
||||||
|
:aspect_id => @aspect0.id
|
||||||
|
|
||||||
|
contact = @controller.current_user.contact_for(@person)
|
||||||
|
response.body.should == contact.aspect_memberships.first.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "#destroy" do
|
describe "#destroy" do
|
||||||
it 'removes contacts from an aspect' do
|
it 'removes contacts from an aspect' do
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue