can now have the same item in more than one list

This commit is contained in:
Emi Matchu 2011-10-10 21:43:46 -05:00
parent 6bf926eb3b
commit 44156c5b21
13 changed files with 344 additions and 155 deletions

View file

@ -1,7 +1,7 @@
class ClosetHangersController < ApplicationController class ClosetHangersController < ApplicationController
before_filter :authorize_user!, :only => [:destroy, :create, :update, :petpage] before_filter :authorize_user!, :only => [:destroy, :create, :update, :update_quantities, :petpage]
before_filter :find_item, :only => [:destroy, :create, :update] before_filter :find_item, :only => [:destroy, :create, :update_quantities]
before_filter :find_user, :only => [:index, :petpage] before_filter :find_user, :only => [:index, :petpage, :update_quantities]
def destroy def destroy
raise ActiveRecord::RecordNotFound unless params[:closet_hanger] raise ActiveRecord::RecordNotFound unless params[:closet_hanger]
@ -39,19 +39,38 @@ class ClosetHangersController < ApplicationController
find_closet_hangers! find_closet_hangers!
end end
# Since the user does not care about the idea of a hanger, but rather the def create
# quantity of an item they own, the user would expect a create form to work @closet_hanger = current_user.closet_hangers.build(params[:closet_hanger])
# even after the record already exists, and an update form to work even after @closet_hanger.item = @item
# the record is deleted. So, create and update are aliased, and both find
# the record if it exists or create a new one if it does not. They will even if @closet_hanger.save
# delete the record if quantity is zero. respond_to do |format|
# format.html {
# This is kinda a violation of REST. It's not worth breaking user message = "Success! You #{@closet_hanger.verb(:you)} #{@closet_hanger.quantity} "
# expectations, though, and I can't really think of a genuinely RESTful way message << ((@closet_hanger.quantity > 1) ? @item.name.pluralize : @item.name)
# to pull this off. message << " in the \"#{@closet_hanger.list.name}\" list" if @closet_hanger.list
flash[:success] = "#{message}."
redirect_back!(@item)
}
format.json { render :json => true }
end
else
respond_to do |format|
format.html {
flash[:alert] = "We couldn't save how many of this item you #{@closet_hanger.verb(:you)}: #{@closet_hanger.errors.full_messages.to_sentence}"
redirect_back!(@item)
}
format.json { render :json => {:errors => @closet_hanger.errors.full_messages}, :status => :unprocessable_entity }
end
end
end
def update def update
@closet_hanger = current_user.closet_hangers.find_or_initialize_by_item_id_and_owned(@item.id, owned) @closet_hanger = current_user.closet_hangers.find(params[:id])
@closet_hanger.attributes = params[:closet_hanger] @closet_hanger.attributes = params[:closet_hanger]
@item = @closet_hanger.item
unless @closet_hanger.quantity == 0 # save the hanger, new record or not unless @closet_hanger.quantity == 0 # save the hanger, new record or not
if @closet_hanger.save if @closet_hanger.save
@ -85,8 +104,21 @@ class ClosetHangersController < ApplicationController
end end
end end
end end
alias_method :create, :update def update_quantities
begin
ClosetHanger.transaction do
params[:quantity].each do |key, quantity|
ClosetHanger.set_quantity!(quantity, :user_id => @user.id,
:item_id => @item.id, :key => key)
end
flash[:success] = "Successfully saved how many #{@item.name} you own and want."
end
rescue ActiveRecord::RecordInvalid => e
flash[:alert] = "We couldn't save those quantities. #{e.message}"
end
redirect_to @item
end
protected protected

View file

@ -54,10 +54,20 @@ class ItemsController < ApplicationController
} }
if user_signed_in? if user_signed_in?
@current_user_hangers = [true, false].map do |owned| # Empty arrays are important so that we can loop over this and still
hanger = current_user.closet_hangers.find_or_initialize_by_item_id_and_owned(@item.id, owned) # show the generic no-list case
hanger.quantity ||= 1 @current_user_lists = {true => [], false => []}
hanger current_user.closet_lists.alphabetical.each do |list|
@current_user_lists[list.hangers_owned] << list
end
@current_user_quantities = Hash.new(0) # default is zero
hangers = current_user.closet_hangers.where(:item_id => @item.id).
select([:owned, :list_id, :quantity])
hangers.each do |hanger|
key = hanger.list_id || hanger.owned
@current_user_quantities[key] = hanger.quantity
end end
end end

View file

@ -47,6 +47,10 @@ module ItemsHelper
) )
end end
end end
def closet_list_verb(owned)
ClosetHanger.verb(:you, owned)
end
def closeted_icons_for(item) def closeted_icons_for(item)
content = ''.html_safe content = ''.html_safe

View file

@ -5,7 +5,7 @@ class ClosetHanger < ActiveRecord::Base
attr_accessible :list_id, :owned, :quantity attr_accessible :list_id, :owned, :quantity
validates :item_id, :uniqueness => {:scope => [:user_id, :owned]} validates :item_id, :uniqueness => {:scope => [:user_id, :owned, :list_id]}
validates :quantity, :numericality => {:greater_than => 0} validates :quantity, :numericality => {:greater_than => 0}
validates_presence_of :item, :user validates_presence_of :item, :user
@ -28,7 +28,7 @@ class ClosetHanger < ActiveRecord::Base
)) ))
end end
before_validation :set_owned_by_list before_validation :merge_quantities, :set_owned_by_list
def verb(subject=:someone) def verb(subject=:someone)
self.class.verb(subject, owned?) self.class.verb(subject, owned?)
@ -39,6 +39,41 @@ class ClosetHanger < ActiveRecord::Base
base << 's' if positive && subject != :you && subject != :i base << 's' if positive && subject != :you && subject != :i
base base
end end
def self.set_quantity!(quantity, options)
quantity = quantity.to_i
conditions = {:user_id => options[:user_id].to_i,
:item_id => options[:item_id].to_i}
if options[:key] == "true"
conditions[:owned] = true
conditions[:list_id] = nil
elsif options[:key] == "false"
conditions[:owned] = false
conditions[:list_id] = nil
else
conditions[:list_id] = options[:key].to_i
end
hanger = self.where(conditions).first
unless hanger
hanger = self.new
hanger.user_id = conditions[:user_id]
hanger.item_id = conditions[:item_id]
# One of the following will be nil, and that's okay. If owned is nil,
# we'll cover for it before validation, as always.
hanger.owned = conditions[:owned]
hanger.list_id = conditions[:list_id]
end
unless quantity == 0
Rails.logger.debug("Logging to #{hanger.id} quantity #{quantity}")
hanger.quantity = quantity
hanger.save!
else
hanger.destroy if hanger
end
end
protected protected
@ -51,6 +86,23 @@ class ClosetHanger < ActiveRecord::Base
end end
end end
end end
def merge_quantities
# Find a hanger that conflicts: for the same item, in the same user's
# closet, same owned status, same list. It also must not be the current
# hanger.
conflicting_hanger = self.class.select([:id, :quantity]).
where(:user_id => user_id, :item_id => item_id, :owned => owned,
:list_id => list_id).where(['id != ?', self.id]).first
# If there is such a hanger, remove it and merge its quantity into this one.
if conflicting_hanger
self.quantity += conflicting_hanger.quantity
conflicting_hanger.destroy
end
true
end
def set_owned_by_list def set_owned_by_list
self.owned = list.hangers_owned if list self.owned = list.hangers_owned if list

View file

@ -295,6 +295,7 @@ body.closet_hangers-index
.closet-hangers-group-autocomplete-item, .closet-list-autocomplete-item .closet-hangers-group-autocomplete-item, .closet-list-autocomplete-item
span span
+opacity(.5)
font-style: italic font-style: italic
padding: .2em .4em padding: .2em .4em

View file

@ -112,14 +112,33 @@ body.items-show
font-size: 85% font-size: 85%
margin-left: 1em margin-left: 1em
padding: 1em padding: 1em
width: 21em width: 30em
// compete with #trade-hangers
position: relative
z-index: 2
label, header h3
display: block font-size: 150%
font-weight: bold font-weight: bold
margin-bottom: .25em
header
font-size: 125% #closet-hangers-ownership-groups
+clearfix
margin-bottom: .5em
div
float: left
margin: 0 5%
text-align: left
width: 40%
li
list-style: none
word-wrap: break-word
&.unlisted
font-style: italic
form form
padding: .5em 0 padding: .5em 0
@ -128,7 +147,8 @@ body.items-show
width: 9em width: 9em
input[type=number] input[type=number]
width: 4em margin-right: .5em
width: 3em
&.js &.js
#trade-hangers #trade-hangers

View file

@ -4,7 +4,7 @@
.quantity{:class => "quantity-#{closet_hanger.quantity}"} .quantity{:class => "quantity-#{closet_hanger.quantity}"}
%span= closet_hanger.quantity %span= closet_hanger.quantity
- if show_controls - if show_controls
= form_for closet_hanger, :url => user_item_closet_hanger_path(current_user, closet_hanger.item), :html => {:class => 'closet-hanger-update'} do |f| = form_for [current_user, closet_hanger], :html => {:class => 'closet-hanger-update'} do |f|
= return_to_field_tag = return_to_field_tag
= f.hidden_field :list_id = f.hidden_field :list_id
= f.hidden_field :owned = f.hidden_field :owned

View file

@ -95,21 +95,22 @@
= link_to_add_closet_list 'Add new list', :owned => owned, :class => 'add-closet-list' = link_to_add_closet_list 'Add new list', :owned => owned, :class => 'add-closet-list'
.closet-hangers-group-content .closet-hangers-group-content
= render_closet_lists(@closet_lists_by_owned[owned]) = render_closet_lists(@closet_lists_by_owned[owned])
.closet-list.unlisted{'data-hangers-count' => unlisted_hangers_count(owned)} - if !public_perspective? || unlisted_hangers_count(owned) > 0
%header .closet-list.unlisted{'data-hangers-count' => unlisted_hangers_count(owned)}
- unless public_perspective? %header
= form_for @user, :html => {:class => 'visibility-form'} do |f| - unless public_perspective?
= f.select hangers_group_visibility_field_name(owned), = form_for @user, :html => {:class => 'visibility-form'} do |f|
closet_visibility_choices(:human_name) = f.select hangers_group_visibility_field_name(owned),
= f.submit "Save" closet_visibility_choices(:human_name)
= closet_visibility_descriptions = f.submit "Save"
- if has_lists?(owned) = closet_visibility_descriptions
%h4 (Not in a list) - if has_lists?(owned)
.closet-list-content %h4 (Not in a list)
.closet-list-hangers .closet-list-content
= render_unlisted_closet_hangers(owned) .closet-list-hangers
%span.empty-list = render_unlisted_closet_hangers(owned)
There aren't any items here. %span.empty-list
There aren't any items here.
- content_for :stylesheets do - content_for :stylesheets do
= stylesheet_link_tag 'south-street/jquery-ui' = stylesheet_link_tag 'south-street/jquery-ui'

View file

@ -10,25 +10,34 @@
== Rarity: #{@item.rarity_index} (#{@item.rarity}) == Rarity: #{@item.rarity_index} (#{@item.rarity})
= link_to 'NeoItems', neoitems_url_for(@item), :class => 'button' = link_to 'NeoItems', neoitems_url_for(@item), :class => 'button'
- if @current_user_hangers - if user_signed_in?
#closet-hangers #closet-hangers
%header %h3
Track this in Track this in
= link_to 'Your Items', user_closet_hangers_path(current_user) = link_to 'Your Items', user_closet_hangers_path(current_user)
- @current_user_hangers.each do |hanger| = form_tag update_quantities_user_item_closet_hangers_path(:user_id => current_user, :item_id => @item), :method => :put do
= form_for(hanger, :url => user_item_closet_hanger_path(current_user, @item)) do |f| #closet-hangers-ownership-groups
- if hanger.new_record? - @current_user_lists.each do |owned, lists|
= f.hidden_field :quantity %div
= f.hidden_field :owned %h4 Items you #{closet_list_verb(owned)}
= f.submit "I #{hanger.verb(:you)} this item!" %ul
- else - lists.each do |list|
= f.hidden_field :owned %li
= f.label :quantity, "How many of these do you #{hanger.verb(:you)}?" = number_field_tag "quantity[#{list.id}]",
= f.number_field :quantity, :min => 0, :required => true @current_user_quantities[list.id], :min => 0
- lists = current_user.closet_lists.where(:hangers_owned => hanger.owned).all = label_tag "quantity[#{list.id}]", list.name
- unless lists.empty?
= f.collection_select :list_id, lists, :id, :name, :include_blank => 'Not in a list' %li
= f.submit "Save" = number_field_tag "quantity[#{owned}]",
@current_user_quantities[owned], :min => 0
- unless lists.empty?
= label_tag "quantity[#{owned}]" do
Not in a list
- else
= label_tag "quantity[#{owned}]" do
How many?
= submit_tag 'Save to Your Items'
%p= @item.description %p= @item.description
#item-zones #item-zones

View file

@ -52,7 +52,7 @@ OpenneoImpressItems::Application.routes.draw do |map|
resources :users, :path => 'user', :only => [:index, :update] do resources :users, :path => 'user', :only => [:index, :update] do
resources :contributions, :only => [:index] resources :contributions, :only => [:index]
resources :closet_hangers, :only => [:index], :path => 'closet' do resources :closet_hangers, :only => [:index, :update], :path => 'closet' do
collection do collection do
get :petpage get :petpage
end end
@ -60,7 +60,13 @@ OpenneoImpressItems::Application.routes.draw do |map|
resources :closet_lists, :only => [:new, :create, :edit, :update, :destroy], :path => 'closet/lists' resources :closet_lists, :only => [:new, :create, :edit, :update, :destroy], :path => 'closet/lists'
resources :items, :only => [] do resources :items, :only => [] do
resource :closet_hanger, :only => [:create, :update, :destroy] resources :closet_hangers, :only => [:create] do
collection do
put :update_quantities
end
end
resource :closet_hanger, :only => [:update, :destroy]
end end
end end

View file

@ -136,11 +136,11 @@
return a.find('span.name').text().localeCompare(b.find('span.name').text()); return a.find('span.name').text().localeCompare(b.find('span.name').text());
} }
function findList(id, item) { function findList(owned, id, item) {
if(id) { if(id) {
return $('#closet-list-' + id); return $('#closet-list-' + id);
} else { } else {
return item.closest('.closet-hangers-group').find('div.closet-list.unlisted'); return $("div.closet-hangers-group[data-owned=" + owned + "] div.closet-list.unlisted");
} }
} }
@ -148,8 +148,8 @@
el.attr('data-hangers-count', el.find('div.object').length); el.attr('data-hangers-count', el.find('div.object').length);
} }
function moveItemToList(item, listId) { function moveItemToList(item, owned, listId) {
var newList = findList(listId, item); var newList = findList(owned, listId, item);
var oldList = item.closest('div.closet-list'); var oldList = item.closest('div.closet-list');
var hangersWrapper = newList.find('div.closet-list-hangers'); var hangersWrapper = newList.find('div.closet-list-hangers');
item.insertIntoSortedList(hangersWrapper, compareItemsByName); item.insertIntoSortedList(hangersWrapper, compareItemsByName);
@ -160,8 +160,9 @@
function submitUpdateForm(form) { function submitUpdateForm(form) {
if(form.data('loading')) return false; if(form.data('loading')) return false;
var quantityEl = form.children("input[name=closet_hanger\[quantity\]]"); var quantityEl = form.children("input[name=closet_hanger\[quantity\]]");
var ownedEl = form.children("input[name=closet_hanger\[owned\]]");
var listEl = form.children("input[name=closet_hanger\[list_id\]]"); var listEl = form.children("input[name=closet_hanger\[list_id\]]");
var listChanged = listEl.hasChanged(); var listChanged = ownedEl.hasChanged() || listEl.hasChanged();
if(listChanged || quantityEl.hasChanged()) { if(listChanged || quantityEl.hasChanged()) {
var objectWrapper = form.closest(".object").addClass("loading"); var objectWrapper = form.closest(".object").addClass("loading");
var newQuantity = quantityEl.val(); var newQuantity = quantityEl.val();
@ -170,7 +171,7 @@
var data = form.serialize(); // get data before disabling inputs var data = form.serialize(); // get data before disabling inputs
objectWrapper.disableForms(); objectWrapper.disableForms();
form.data('loading', true); form.data('loading', true);
if(listChanged) moveItemToList(objectWrapper, listEl.val()); if(listChanged) moveItemToList(objectWrapper, ownedEl.val(), listEl.val());
$.ajax({ $.ajax({
url: form.attr("action") + ".json", url: form.attr("action") + ".json",
type: "post", type: "post",
@ -185,13 +186,37 @@
form.data('loading', false); form.data('loading', false);
}, },
success: function () { success: function () {
// Now that the move was successful, let's merge it with any
// conflicting hangers
var id = objectWrapper.attr("data-item-id");
var conflictingHanger = findList(ownedEl.val(), listEl.val(), objectWrapper).
find("div[data-item-id=" + id + "]").not(objectWrapper);
if(conflictingHanger.length) {
var conflictingQuantity = parseInt(
conflictingHanger.attr('data-quantity'),
10
);
var currentQuantity = parseInt(newQuantity, 10);
var mergedQuantity = conflictingQuantity + currentQuantity;
quantitySpan.text(mergedQuantity);
quantityEl.val(mergedQuantity);
objectWrapper.attr('data-quantity', mergedQuantity);
conflictingHanger.remove();
}
quantityEl.storeValue(); quantityEl.storeValue();
ownedEl.storeValue();
listEl.storeValue(); listEl.storeValue();
}, },
error: function (xhr) { error: function (xhr) {
quantityEl.revertValue(); quantityEl.revertValue();
ownedEl.revertValue();
listEl.revertValue(); listEl.revertValue();
if(listChanged) moveItemToList(objectWrapper, listEl.val()); if(listChanged) moveItemToList(objectWrapper, ownedEl.val(), listEl.val());
quantitySpan.text(quantityEl.val()); quantitySpan.text(quantityEl.val());
handleSaveError(xhr, "updating the quantity"); handleSaveError(xhr, "updating the quantity");
@ -205,7 +230,13 @@
submitUpdateForm($(this)); submitUpdateForm($(this));
}); });
function editableInputs() { return $(hangersElQuery).find('input[name=closet_hanger\[quantity\]], input[name=closet_hanger\[list_id\]]') } function editableInputs() {
return $(hangersElQuery).find(
'input[name=closet_hanger\[quantity\]], ' +
'input[name=closet_hanger\[owned\]], ' +
'input[name=closet_hanger\[list_id\]]'
)
}
$(hangersElQuery + 'input[name=closet_hanger\[quantity\]]').live('change', function () { $(hangersElQuery + 'input[name=closet_hanger\[quantity\]]').live('change', function () {
submitUpdateForm($(this).parent()); submitUpdateForm($(this).parent());
@ -264,7 +295,9 @@
select: function (e, ui) { select: function (e, ui) {
if(ui.item.is_item) { if(ui.item.is_item) {
// Let the autocompleter finish up this search before starting a new one // Let the autocompleter finish up this search before starting a new one
setTimeout(function () { itemsSearchField.autocomplete("search", ui.item) }, 0); setTimeout(function () {
itemsSearchField.autocomplete("search", ui.item);
}, 0);
} else { } else {
var item = ui.item.item; var item = ui.item.item;
var group = ui.item.group; var group = ui.item.group;
@ -276,10 +309,10 @@
list_id: ui.item.list ? ui.item.list.id : '' list_id: ui.item.list ? ui.item.list.id : ''
}; };
if(!item.hangerInGroup) closetHanger.quantity = 1; if(!item.hasHanger) closetHanger.quantity = 1;
$.ajax({ $.ajax({
url: "/user/" + itemsSearchForm.data("current-user-id") + "/items/" + item.id + "/closet_hanger", url: "/user/" + itemsSearchForm.data("current-user-id") + "/items/" + item.id + "/closet_hangers",
type: "post", type: "post",
data: {closet_hanger: closetHanger, return_to: window.location.pathname + window.location.search}, data: {closet_hanger: closetHanger, return_to: window.location.pathname + window.location.search},
complete: function () { complete: function () {
@ -312,29 +345,29 @@
}); });
} else { // item was chosen, now choose a group to insert } else { // item was chosen, now choose a group to insert
var groupInserts = [], group; var groupInserts = [], group;
var item = input.term, itemEl, hangerInGroup, currentListId; var item = input.term, itemEl, occupiedGroups, hasHanger;
for(var i in hangerGroups) { for(var i in hangerGroups) {
group = hangerGroups[i]; group = hangerGroups[i];
itemEl = $('div.closet-hangers-group[data-owned=' + group.owned + '] div.object[data-item-id=' + item.id + ']'); itemEl = $('div.closet-hangers-group[data-owned=' + group.owned + '] div.object[data-item-id=' + item.id + ']');
hangerInGroup = itemEl.length > 0; occupiedGroups = itemEl.closest('.closet-list');
currentListId = itemEl.closest('.closet-list').attr('data-id'); hasHanger = occupiedGroups.filter('.unlisted').length > 0;
groupInserts[groupInserts.length] = { groupInserts[groupInserts.length] = {
group: group, group: group,
item: item, item: item,
label: item.label, label: item.label,
hangerInGroup: hangerInGroup, hasHanger: hasHanger
hangerInList: !!currentListId
} }
for(var i = 0; i < group.lists.length; i++) { for(var i = 0; i < group.lists.length; i++) {
hasHanger = occupiedGroups.
filter("[data-id=" + group.lists[i].id + "]").length > 0;
groupInserts[groupInserts.length] = { groupInserts[groupInserts.length] = {
group: group, group: group,
item: item, item: item,
label: item.label, label: item.label,
list: group.lists[i], list: group.lists[i],
hangerInGroup: hangerInGroup, hasHanger: hasHanger
currentListId: currentListId
} }
} }
} }
@ -350,27 +383,19 @@
if(item.is_item) { // these are items from the server if(item.is_item) { // these are items from the server
li.append("<a>Add <strong>" + item.label + "</strong>"); li.append("<a>Add <strong>" + item.label + "</strong>");
} else if(item.list) { // these are list inserts } else if(item.list) { // these are list inserts
if(item.hangerInGroup) { if(item.hasHanger) {
if(item.currentListId == item.list.id) { li.append("<span>It's already in <strong>" + item.list.label + "</strong>");
li.append("<span>It's in <strong>" + item.list.label + "</strong> now");
} else {
li.append("<a>Move to <strong>" + item.list.label + "</strong>");
}
} else { } else {
li.append("<a>Add to <strong>" + item.list.label + "</strong>"); li.append("<a>Add to <strong>" + item.list.label + "</strong>");
} }
li.addClass("closet-list-autocomplete-item"); li.addClass("closet-list-autocomplete-item");
} else { // these are group inserts } else { // these are group inserts
if(item.hangerInGroup) { var groupName = item.group.label;
var groupName = item.group.label; if(!item.hasHanger) {
if(item.hangerInList) { li.append("<a>Add to <strong>" + groupName.replace(/\s+$/, '') + "</strong>, no list");
li.append("<a>Move to <strong>" + groupName.replace(/\s+$/, '') + "</strong>, no list");
} else {
li.append("<span>It's in <strong>" + groupName + "</strong> now");
}
} else { } else {
li.append("<a>Add to <strong>" + item.group.label + "</strong>"); li.append("<span>It's already in <strong>" + groupName + "</strong>");
} }
li.addClass('closet-hangers-group-autocomplete-item'); li.addClass('closet-hangers-group-autocomplete-item');
} }
return li.appendTo(ul); return li.appendTo(ul);
@ -445,23 +470,23 @@
*/ */
onHangersInit(function () { onHangersInit(function () {
$('.closet-hangers-group').each(function () { $('div.closet-list').droppable({
var group = $(this); accept: 'div.object',
group.find('div.closet-list').droppable({ activate: function () {
accept: '#' + group.attr('id') + ' div.object', $(this).find('.closet-list-content').animate({opacity: 0, height: 100}, 250);
activate: function () { },
$(this).find('.closet-list-content').animate({opacity: 0, height: 100}, 250); activeClass: 'droppable-active',
}, deactivate: function () {
activeClass: 'droppable-active', $(this).find('.closet-list-content').css('height', 'auto').animate({opacity: 1}, 250);
deactivate: function () { },
$(this).find('.closet-list-content').css('height', 'auto').animate({opacity: 1}, 250); drop: function (e, ui) {
}, var form = ui.draggable.find('form.closet-hanger-update');
drop: function (e, ui) { form.find('input[name=closet_hanger\[list_id\]]').
var form = ui.draggable.find('form.closet-hanger-update'); val(this.getAttribute('data-id'));
form.find('input[name=closet_hanger\[list_id\]]').val(this.getAttribute('data-id')); form.find('input[name=closet_hanger\[owned\]]').
submitUpdateForm(form); val($(this).closest('.closet-hangers-group').attr('data-owned'));
} submitUpdateForm(form);
}); }
}); });
}); });

View file

@ -229,7 +229,6 @@ function Wardrobe() {
} }
}, },
error: function (xhr) { error: function (xhr) {
console.log($.parseJSON(xhr.responseText));
try { try {
var json = $.parseJSON(xhr.responseText); var json = $.parseJSON(xhr.responseText);
} catch(e) { } catch(e) {

View file

@ -1163,30 +1163,34 @@ body.closet_hangers-index .closet-list.droppable-active .visibility-form {
} }
/* line 297, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 297, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index .closet-hangers-group-autocomplete-item span, body.closet_hangers-index .closet-list-autocomplete-item span { body.closet_hangers-index .closet-hangers-group-autocomplete-item span, body.closet_hangers-index .closet-list-autocomplete-item span {
-moz-opacity: 0.5;
-webkit-opacity: 0.5;
-o-opacity: 0.5;
-khtml-opacity: 0.5;
font-style: italic; font-style: italic;
padding: 0.2em 0.4em; padding: 0.2em 0.4em;
} }
/* line 302, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 303, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index .closet-list-autocomplete-item a, body.closet_hangers-index .closet-list-autocomplete-item span { body.closet_hangers-index .closet-list-autocomplete-item a, body.closet_hangers-index .closet-list-autocomplete-item span {
font-size: 85%; font-size: 85%;
padding-left: 2em; padding-left: 2em;
} }
/* line 307, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 308, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index .closet-hangers-group[data-owned=true] .user-wants, body.closet_hangers-index .closet-hangers-group[data-owned=false] .user-owns { body.closet_hangers-index .closet-hangers-group[data-owned=true] .user-wants, body.closet_hangers-index .closet-hangers-group[data-owned=false] .user-owns {
background: #eeffee; background: #eeffee;
font-weight: bold; font-weight: bold;
} }
/* line 314, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 315, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user #closet-hangers .object:hover form { body.closet_hangers-index.current-user #closet-hangers .object:hover form {
display: inline; display: inline;
} }
/* line 317, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 318, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user #closet-hangers .object:hover .closet-hanger-destroy { body.closet_hangers-index.current-user #closet-hangers .object:hover .closet-hanger-destroy {
position: absolute; position: absolute;
right: 18px; right: 18px;
top: 52px; top: 52px;
} }
/* line 322, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 323, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user #closet-hangers .object:hover .closet-hanger-destroy input { body.closet_hangers-index.current-user #closet-hangers .object:hover .closet-hanger-destroy input {
/* http://www.zurb.com/blog_uploads/0000/0617/buttons-03.html */ /* http://www.zurb.com/blog_uploads/0000/0617/buttons-03.html */
-moz-border-radius: 5px; -moz-border-radius: 5px;
@ -1227,7 +1231,7 @@ body.closet_hangers-index.current-user #closet-hangers .object:hover .closet-han
body.closet_hangers-index.current-user #closet-hangers .object:hover .closet-hanger-destroy input:hover { body.closet_hangers-index.current-user #closet-hangers .object:hover .closet-hanger-destroy input:hover {
background-color: #999999; background-color: #999999;
} }
/* line 325, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 326, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user #closet-hangers .object:hover .quantity { body.closet_hangers-index.current-user #closet-hangers .object:hover .quantity {
-moz-opacity: 1; -moz-opacity: 1;
-webkit-opacity: 1; -webkit-opacity: 1;
@ -1237,89 +1241,89 @@ body.closet_hangers-index.current-user #closet-hangers .object:hover .quantity {
top: 0; top: 0;
padding: 0; padding: 0;
} }
/* line 331, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 332, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user #closet-hangers .object:hover .quantity span { body.closet_hangers-index.current-user #closet-hangers .object:hover .quantity span {
display: none; display: none;
} }
/* line 334, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 335, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user #closet-hangers .object:hover .quantity input[type=number] { body.closet_hangers-index.current-user #closet-hangers .object:hover .quantity input[type=number] {
padding: 2px; padding: 2px;
width: 2em; width: 2em;
} }
/* line 338, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 339, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user #closet-hangers .object:hover .quantity input[type=submit] { body.closet_hangers-index.current-user #closet-hangers .object:hover .quantity input[type=submit] {
font-size: 85%; font-size: 85%;
} }
/* line 343, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 344, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js #closet-hangers .object:hover .quantity { body.closet_hangers-index.current-user.js #closet-hangers .object:hover .quantity {
display: block; display: block;
} }
/* line 346, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 347, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js #closet-hangers .object:hover .quantity input[type=number] { body.closet_hangers-index.current-user.js #closet-hangers .object:hover .quantity input[type=number] {
width: 2.5em; width: 2.5em;
} }
/* line 349, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 350, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js #closet-hangers .object:hover .quantity input[type=submit] { body.closet_hangers-index.current-user.js #closet-hangers .object:hover .quantity input[type=submit] {
display: none; display: none;
} }
/* line 352, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 353, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js #closet-hangers .object.loading { body.closet_hangers-index.current-user.js #closet-hangers .object.loading {
background: #eeffee; background: #eeffee;
outline: 1px solid #006600; outline: 1px solid #006600;
} }
/* line 356, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 357, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js #closet-hangers .object.loading .quantity { body.closet_hangers-index.current-user.js #closet-hangers .object.loading .quantity {
display: block; display: block;
} }
/* line 359, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 360, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js #closet-hangers .object.loading .quantity span:after { body.closet_hangers-index.current-user.js #closet-hangers .object.loading .quantity span:after {
content: "…"; content: "…";
} }
/* line 363, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 364, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js #closet-hangers-contact form { body.closet_hangers-index.current-user.js #closet-hangers-contact form {
display: none; display: none;
} }
/* line 366, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 367, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js #closet-hangers-contact .edit-contact-link, body.closet_hangers-index.current-user.js #closet-hangers-contact #cancel-contact-link { body.closet_hangers-index.current-user.js #closet-hangers-contact .edit-contact-link, body.closet_hangers-index.current-user.js #closet-hangers-contact #cancel-contact-link {
display: inline; display: inline;
} }
/* line 370, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 371, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js #closet-hangers-contact.editing form { body.closet_hangers-index.current-user.js #closet-hangers-contact.editing form {
display: block; display: block;
} }
/* line 373, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 374, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js #closet-hangers-contact.editing .edit-contact-link { body.closet_hangers-index.current-user.js #closet-hangers-contact.editing .edit-contact-link {
display: none; display: none;
} }
/* line 378, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 379, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js .closet-hangers-group header .show, body.closet_hangers-index.current-user.js .closet-hangers-group header .hide { body.closet_hangers-index.current-user.js .closet-hangers-group header .show, body.closet_hangers-index.current-user.js .closet-hangers-group header .hide {
cursor: pointer; cursor: pointer;
} }
/* line 381, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 382, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js .closet-hangers-group header .hide { body.closet_hangers-index.current-user.js .closet-hangers-group header .hide {
display: block; display: block;
} }
/* line 385, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 386, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js .closet-hangers-group.hidden header .hide, body.closet_hangers-index.current-user.js .closet-hangers-group.hidden .closet-hangers-group-content { body.closet_hangers-index.current-user.js .closet-hangers-group.hidden header .hide, body.closet_hangers-index.current-user.js .closet-hangers-group.hidden .closet-hangers-group-content {
display: none; display: none;
} }
/* line 388, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 389, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js .closet-hangers-group.hidden header .show { body.closet_hangers-index.current-user.js .closet-hangers-group.hidden header .show {
display: block; display: block;
} }
/* line 391, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 392, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.current-user.js #toggle-help { body.closet_hangers-index.current-user.js #toggle-help {
display: inline; display: inline;
} }
/* line 395, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 396, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.js #toggle-compare { body.closet_hangers-index.js #toggle-compare {
display: inline; display: inline;
} }
/* line 399, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 400, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.js #closet-hangers.comparing .object { body.closet_hangers-index.js #closet-hangers.comparing .object {
display: none; display: none;
} }
/* line 403, ../../../app/stylesheets/closet_hangers/_index.sass */ /* line 404, ../../../app/stylesheets/closet_hangers/_index.sass */
body.closet_hangers-index.js #closet-hangers.comparing .closet-hangers-group[data-owned=true] .user-wants, body.closet_hangers-index.js #closet-hangers.comparing .closet-hangers-group[data-owned=false] .user-owns { body.closet_hangers-index.js #closet-hangers.comparing .closet-hangers-group[data-owned=true] .user-wants, body.closet_hangers-index.js #closet-hangers.comparing .closet-hangers-group[data-owned=false] .user-owns {
display: inline-block; display: inline-block;
} }
@ -1878,35 +1882,61 @@ body.items-show #closet-hangers {
font-size: 85%; font-size: 85%;
margin-left: 1em; margin-left: 1em;
padding: 1em; padding: 1em;
width: 21em; width: 30em;
} position: relative;
/* line 117, ../../../app/stylesheets/items/_show.sass */ z-index: 2;
body.items-show #closet-hangers label, body.items-show #closet-hangers header {
display: block;
font-weight: bold;
} }
/* line 121, ../../../app/stylesheets/items/_show.sass */ /* line 121, ../../../app/stylesheets/items/_show.sass */
body.items-show #closet-hangers header { body.items-show #closet-hangers h3 {
font-size: 125%; font-size: 150%;
font-weight: bold;
margin-bottom: 0.25em;
} }
/* line 124, ../../../app/stylesheets/items/_show.sass */ /* line 126, ../../../app/stylesheets/items/_show.sass */
body.items-show #closet-hangers #closet-hangers-ownership-groups {
overflow: hidden;
display: inline-block;
margin-bottom: 0.5em;
}
/* line 8, ../../../app/stylesheets/partials/clean/_mixins.sass */
body.items-show #closet-hangers #closet-hangers-ownership-groups {
display: block;
}
/* line 130, ../../../app/stylesheets/items/_show.sass */
body.items-show #closet-hangers #closet-hangers-ownership-groups div {
float: left;
margin: 0 5%;
text-align: left;
width: 40%;
}
/* line 136, ../../../app/stylesheets/items/_show.sass */
body.items-show #closet-hangers #closet-hangers-ownership-groups div li {
list-style: none;
word-wrap: break-word;
}
/* line 140, ../../../app/stylesheets/items/_show.sass */
body.items-show #closet-hangers #closet-hangers-ownership-groups div li.unlisted {
font-style: italic;
}
/* line 143, ../../../app/stylesheets/items/_show.sass */
body.items-show #closet-hangers form { body.items-show #closet-hangers form {
padding: 0.5em 0; padding: 0.5em 0;
} }
/* line 127, ../../../app/stylesheets/items/_show.sass */ /* line 146, ../../../app/stylesheets/items/_show.sass */
body.items-show #closet-hangers select { body.items-show #closet-hangers select {
width: 9em; width: 9em;
} }
/* line 130, ../../../app/stylesheets/items/_show.sass */ /* line 149, ../../../app/stylesheets/items/_show.sass */
body.items-show #closet-hangers input[type=number] { body.items-show #closet-hangers input[type=number] {
width: 4em; margin-right: 0.5em;
width: 3em;
} }
/* line 135, ../../../app/stylesheets/items/_show.sass */ /* line 155, ../../../app/stylesheets/items/_show.sass */
body.items-show.js #trade-hangers p { body.items-show.js #trade-hangers p {
max-height: 3em; max-height: 3em;
overflow: hidden; overflow: hidden;
} }
/* line 139, ../../../app/stylesheets/items/_show.sass */ /* line 159, ../../../app/stylesheets/items/_show.sass */
body.items-show.js #trade-hangers p.showing-more { body.items-show.js #trade-hangers p.showing-more {
max-height: none; max-height: none;
} }