diff --git a/app/controllers/closet_hangers_controller.rb b/app/controllers/closet_hangers_controller.rb index b75a5a70..13f844a0 100644 --- a/app/controllers/closet_hangers_controller.rb +++ b/app/controllers/closet_hangers_controller.rb @@ -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 diff --git a/app/controllers/items_controller.rb b/app/controllers/items_controller.rb index 832353f9..6acc3610 100644 --- a/app/controllers/items_controller.rb +++ b/app/controllers/items_controller.rb @@ -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 diff --git a/app/helpers/items_helper.rb b/app/helpers/items_helper.rb index 55540a3a..30be7a78 100644 --- a/app/helpers/items_helper.rb +++ b/app/helpers/items_helper.rb @@ -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 diff --git a/app/models/closet_hanger.rb b/app/models/closet_hanger.rb index 44358a66..99fe30f8 100644 --- a/app/models/closet_hanger.rb +++ b/app/models/closet_hanger.rb @@ -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 diff --git a/app/stylesheets/closet_hangers/_index.sass b/app/stylesheets/closet_hangers/_index.sass index 18178b3f..1f9def6f 100644 --- a/app/stylesheets/closet_hangers/_index.sass +++ b/app/stylesheets/closet_hangers/_index.sass @@ -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 diff --git a/app/stylesheets/items/_show.sass b/app/stylesheets/items/_show.sass index c52f9a88..cf670d3d 100644 --- a/app/stylesheets/items/_show.sass +++ b/app/stylesheets/items/_show.sass @@ -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 diff --git a/app/views/closet_hangers/_closet_hanger.html.haml b/app/views/closet_hangers/_closet_hanger.html.haml index d63492bb..33648290 100644 --- a/app/views/closet_hangers/_closet_hanger.html.haml +++ b/app/views/closet_hangers/_closet_hanger.html.haml @@ -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 diff --git a/app/views/closet_hangers/index.html.haml b/app/views/closet_hangers/index.html.haml index d4aca491..a93a89de 100644 --- a/app/views/closet_hangers/index.html.haml +++ b/app/views/closet_hangers/index.html.haml @@ -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' diff --git a/app/views/items/show.html.haml b/app/views/items/show.html.haml index b215368e..e433ab39 100644 --- a/app/views/items/show.html.haml +++ b/app/views/items/show.html.haml @@ -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 diff --git a/config/routes.rb b/config/routes.rb index 5deb758d..9af5ccd2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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 diff --git a/public/javascripts/closet_hangers/index.js b/public/javascripts/closet_hangers/index.js index 17855fdc..fda15b2a 100644 --- a/public/javascripts/closet_hangers/index.js +++ b/public/javascripts/closet_hangers/index.js @@ -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("Add " + item.label + ""); } else if(item.list) { // these are list inserts - if(item.hangerInGroup) { - if(item.currentListId == item.list.id) { - li.append("It's in " + item.list.label + " now"); - } else { - li.append("Move to " + item.list.label + ""); - } + if(item.hasHanger) { + li.append("It's already in " + item.list.label + ""); } else { li.append("Add to " + item.list.label + ""); } li.addClass("closet-list-autocomplete-item"); } else { // these are group inserts - if(item.hangerInGroup) { - var groupName = item.group.label; - if(item.hangerInList) { - li.append("Move to " + groupName.replace(/\s+$/, '') + ", no list"); - } else { - li.append("It's in " + groupName + " now"); - } + var groupName = item.group.label; + if(!item.hasHanger) { + li.append("Add to " + groupName.replace(/\s+$/, '') + ", no list"); } else { - li.append("Add to " + item.group.label + ""); - } + li.append("It's already in " + groupName + ""); + } 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); + } }); }); diff --git a/public/javascripts/wardrobe.js b/public/javascripts/wardrobe.js index 179ae3c5..9013170b 100644 --- a/public/javascripts/wardrobe.js +++ b/public/javascripts/wardrobe.js @@ -229,7 +229,6 @@ function Wardrobe() { } }, error: function (xhr) { - console.log($.parseJSON(xhr.responseText)); try { var json = $.parseJSON(xhr.responseText); } catch(e) { diff --git a/public/stylesheets/compiled/screen.css b/public/stylesheets/compiled/screen.css index 7d32fd3f..0d3e4bc8 100644 --- a/public/stylesheets/compiled/screen.css +++ b/public/stylesheets/compiled/screen.css @@ -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; }