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
before_filter :authorize_user!, :only => [:destroy, :create, :update, :petpage]
before_filter :find_item, :only => [:destroy, :create, :update]
before_filter :find_user, :only => [:index, :petpage]
before_filter :authorize_user!, :only => [:destroy, :create, :update, :update_quantities, :petpage]
before_filter :find_item, :only => [:destroy, :create, :update_quantities]
before_filter :find_user, :only => [:index, :petpage, :update_quantities]
def destroy
raise ActiveRecord::RecordNotFound unless params[:closet_hanger]
@ -39,19 +39,38 @@ class ClosetHangersController < ApplicationController
find_closet_hangers!
end
# Since the user does not care about the idea of a hanger, but rather the
# quantity of an item they own, the user would expect a create form to work
# even after the record already exists, and an update form to work even after
# 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
# delete the record if quantity is zero.
#
# This is kinda a violation of REST. It's not worth breaking user
# expectations, though, and I can't really think of a genuinely RESTful way
# to pull this off.
def create
@closet_hanger = current_user.closet_hangers.build(params[:closet_hanger])
@closet_hanger.item = @item
if @closet_hanger.save
respond_to do |format|
format.html {
message = "Success! You #{@closet_hanger.verb(:you)} #{@closet_hanger.quantity} "
message << ((@closet_hanger.quantity > 1) ? @item.name.pluralize : @item.name)
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
@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]
@item = @closet_hanger.item
unless @closet_hanger.quantity == 0 # save the hanger, new record or not
if @closet_hanger.save
@ -85,8 +104,21 @@ class ClosetHangersController < ApplicationController
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

View file

@ -54,10 +54,20 @@ class ItemsController < ApplicationController
}
if user_signed_in?
@current_user_hangers = [true, false].map do |owned|
hanger = current_user.closet_hangers.find_or_initialize_by_item_id_and_owned(@item.id, owned)
hanger.quantity ||= 1
hanger
# Empty arrays are important so that we can loop over this and still
# show the generic no-list case
@current_user_lists = {true => [], false => []}
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

View file

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

View file

@ -5,7 +5,7 @@ class ClosetHanger < ActiveRecord::Base
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_presence_of :item, :user
@ -28,7 +28,7 @@ class ClosetHanger < ActiveRecord::Base
))
end
before_validation :set_owned_by_list
before_validation :merge_quantities, :set_owned_by_list
def verb(subject=:someone)
self.class.verb(subject, owned?)
@ -39,6 +39,41 @@ class ClosetHanger < ActiveRecord::Base
base << 's' if positive && subject != :you && subject != :i
base
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
@ -51,6 +86,23 @@ class ClosetHanger < ActiveRecord::Base
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
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
span
+opacity(.5)
font-style: italic
padding: .2em .4em

View file

@ -112,14 +112,33 @@ body.items-show
font-size: 85%
margin-left: 1em
padding: 1em
width: 21em
width: 30em
// compete with #trade-hangers
position: relative
z-index: 2
label, header
display: block
h3
font-size: 150%
font-weight: bold
header
font-size: 125%
margin-bottom: .25em
#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
padding: .5em 0
@ -128,7 +147,8 @@ body.items-show
width: 9em
input[type=number]
width: 4em
margin-right: .5em
width: 3em
&.js
#trade-hangers

View file

@ -4,7 +4,7 @@
.quantity{:class => "quantity-#{closet_hanger.quantity}"}
%span= closet_hanger.quantity
- 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
= f.hidden_field :list_id
= f.hidden_field :owned

View file

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

View file

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

View file

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

View file

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

View file

@ -229,7 +229,6 @@ function Wardrobe() {
}
},
error: function (xhr) {
console.log($.parseJSON(xhr.responseText));
try {
var json = $.parseJSON(xhr.responseText);
} 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 */
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;
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 {
font-size: 85%;
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 {
background: #eeffee;
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 {
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 {
position: absolute;
right: 18px;
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 {
/* http://www.zurb.com/blog_uploads/0000/0617/buttons-03.html */
-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 {
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 {
-moz-opacity: 1;
-webkit-opacity: 1;
@ -1237,89 +1241,89 @@ body.closet_hangers-index.current-user #closet-hangers .object:hover .quantity {
top: 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 {
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] {
padding: 2px;
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] {
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 {
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] {
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] {
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 {
background: #eeffee;
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
display: inline-block;
}
@ -1878,35 +1882,61 @@ body.items-show #closet-hangers {
font-size: 85%;
margin-left: 1em;
padding: 1em;
width: 21em;
}
/* line 117, ../../../app/stylesheets/items/_show.sass */
body.items-show #closet-hangers label, body.items-show #closet-hangers header {
display: block;
font-weight: bold;
width: 30em;
position: relative;
z-index: 2;
}
/* line 121, ../../../app/stylesheets/items/_show.sass */
body.items-show #closet-hangers header {
font-size: 125%;
body.items-show #closet-hangers h3 {
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 {
padding: 0.5em 0;
}
/* line 127, ../../../app/stylesheets/items/_show.sass */
/* line 146, ../../../app/stylesheets/items/_show.sass */
body.items-show #closet-hangers select {
width: 9em;
}
/* line 130, ../../../app/stylesheets/items/_show.sass */
/* line 149, ../../../app/stylesheets/items/_show.sass */
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 {
max-height: 3em;
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 {
max-height: none;
}