Merge branch 'closet'
4
Gemfile
|
@ -33,6 +33,10 @@ gem 'right_aws', '~> 2.1.0'
|
|||
|
||||
gem "character-encodings", "~> 0.4.1", :platforms => :ruby_18
|
||||
|
||||
gem "nokogiri", "~> 1.5.0"
|
||||
|
||||
gem 'sanitize', '~> 2.0.3'
|
||||
|
||||
group :development_async do
|
||||
# async wrappers
|
||||
gem 'eventmachine', :git => 'git://github.com/eventmachine/eventmachine.git'
|
||||
|
|
|
@ -107,6 +107,7 @@ GEM
|
|||
mime-types (1.16)
|
||||
msgpack (0.4.4)
|
||||
mysql2 (0.2.6)
|
||||
nokogiri (1.5.0)
|
||||
openneo-auth-signatory (0.1.0)
|
||||
ruby-hmac
|
||||
polyglot (0.3.1)
|
||||
|
@ -164,9 +165,11 @@ GEM
|
|||
ruby-hmac (0.4.0)
|
||||
rufus-scheduler (2.0.9)
|
||||
tzinfo (>= 0.3.23)
|
||||
sanitize (2.0.3)
|
||||
nokogiri (< 1.6, >= 1.4.4)
|
||||
sinatra (1.2.6)
|
||||
rack (~> 1.1)
|
||||
tilt (>= 1.2.2, < 2.0)
|
||||
tilt (< 2.0, >= 1.2.2)
|
||||
swf_converter (0.0.3)
|
||||
thor (0.14.6)
|
||||
tilt (1.3.2)
|
||||
|
@ -204,6 +207,7 @@ DEPENDENCIES
|
|||
msgpack (~> 0.4.3)
|
||||
mysql2
|
||||
mysqlplus!
|
||||
nokogiri (~> 1.5.0)
|
||||
openneo-auth-signatory (~> 0.1.0)
|
||||
rack-fiber_pool
|
||||
rails (= 3.0.4)
|
||||
|
@ -213,6 +217,7 @@ DEPENDENCIES
|
|||
resque-scheduler (~> 2.0.0.d)
|
||||
right_aws (~> 2.1.0)
|
||||
rspec-rails (~> 2.0.0.beta.22)
|
||||
sanitize (~> 2.0.3)
|
||||
swf_converter (~> 0.0.3)
|
||||
whenever (~> 0.6.2)
|
||||
will_paginate (~> 3.0.pre2)
|
||||
|
|
|
@ -1,10 +1,34 @@
|
|||
class ApplicationController < ActionController::Base
|
||||
protect_from_forgery
|
||||
|
||||
helper_method :can_use_image_mode?
|
||||
helper_method :can_use_image_mode?, :user_is?
|
||||
|
||||
def authenticate_user! # too lazy to change references to login_path
|
||||
redirect_to(login_path) unless user_signed_in?
|
||||
end
|
||||
|
||||
def authorize_user!
|
||||
raise AccessDenied unless user_signed_in? && current_user.id == params[:user_id].to_i
|
||||
end
|
||||
|
||||
def can_use_image_mode?
|
||||
user_signed_in? && current_user.image_mode_tester?
|
||||
end
|
||||
|
||||
class AccessDenied < StandardError;end
|
||||
|
||||
rescue_from AccessDenied, :with => :on_access_denied
|
||||
|
||||
def on_access_denied
|
||||
render :file => 'public/403.html', :layout => false, :status => :forbidden
|
||||
end
|
||||
|
||||
def redirect_back!(default=:back)
|
||||
redirect_to(params[:return_to] || default)
|
||||
end
|
||||
|
||||
def user_is?(user)
|
||||
user_signed_in? && user == current_user
|
||||
end
|
||||
end
|
||||
|
||||
|
|
146
app/controllers/closet_hangers_controller.rb
Normal file
|
@ -0,0 +1,146 @@
|
|||
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]
|
||||
|
||||
def destroy
|
||||
raise ActiveRecord::RecordNotFound unless params[:closet_hanger]
|
||||
@closet_hanger = current_user.closet_hangers.find_by_item_id_and_owned!(@item.id, owned)
|
||||
@closet_hanger.destroy
|
||||
respond_to do |format|
|
||||
format.html { redirect_after_destroy! }
|
||||
format.json { render :json => true }
|
||||
end
|
||||
end
|
||||
|
||||
def index
|
||||
@public_perspective = params.has_key?(:public) || !user_is?(@user)
|
||||
|
||||
find_closet_hangers!
|
||||
|
||||
if @public_perspective && user_signed_in?
|
||||
items = []
|
||||
@closet_lists_by_owned.each do |owned, lists|
|
||||
lists.each do |list|
|
||||
list.hangers.each { |hanger| items << hanger.item }
|
||||
end
|
||||
end
|
||||
|
||||
@unlisted_closet_hangers_by_owned.each do |owned, hangers|
|
||||
hangers.each { |hanger| items << hanger.item }
|
||||
end
|
||||
|
||||
current_user.assign_closeted_to_items!(items)
|
||||
end
|
||||
end
|
||||
|
||||
def petpage
|
||||
@public_perspective = true
|
||||
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 update
|
||||
@closet_hanger = current_user.closet_hangers.find_or_initialize_by_item_id_and_owned(@item.id, owned)
|
||||
@closet_hanger.attributes = params[:closet_hanger]
|
||||
|
||||
unless @closet_hanger.quantity == 0 # save the hanger, new record or not
|
||||
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
|
||||
else # delete the hanger since the user doesn't want it
|
||||
@closet_hanger.destroy
|
||||
respond_to do |format|
|
||||
format.html { redirect_after_destroy! }
|
||||
|
||||
format.json { render :json => true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :create, :update
|
||||
|
||||
protected
|
||||
|
||||
def find_item
|
||||
@item = Item.find params[:item_id]
|
||||
end
|
||||
|
||||
def find_user
|
||||
if params[:user_id]
|
||||
@user = User.find params[:user_id]
|
||||
elsif user_signed_in?
|
||||
redirect_to user_closet_hangers_path(current_user)
|
||||
else
|
||||
redirect_to login_path(:return_to => request.fullpath)
|
||||
end
|
||||
end
|
||||
|
||||
def find_closet_hangers!
|
||||
@perspective_user = current_user unless @public_perspective
|
||||
|
||||
@closet_lists_by_owned = @user.closet_lists.
|
||||
alphabetical.includes(:hangers => :item)
|
||||
unless @perspective_user == @user
|
||||
# If we run this when the user matches, we'll end up with effectively:
|
||||
# WHERE belongs_to_user AND (is_public OR belongs_to_user)
|
||||
# and it's a bit silly to put the SQL server through a condition that's
|
||||
# always true.
|
||||
@closet_lists_by_owned = @closet_lists_by_owned.visible_to(@perspective_user)
|
||||
end
|
||||
@closet_lists_by_owned = @closet_lists_by_owned.group_by(&:hangers_owned)
|
||||
|
||||
visible_groups = @user.closet_hangers_groups_visible_to(@perspective_user)
|
||||
unless visible_groups.empty?
|
||||
@unlisted_closet_hangers_by_owned = @user.closet_hangers.unlisted.
|
||||
owned_before_wanted.alphabetical_by_item_name.includes(:item).
|
||||
where(:owned => [visible_groups]).group_by(&:owned)
|
||||
else
|
||||
@unlisted_closet_hangers_by_owned = {}
|
||||
end
|
||||
end
|
||||
|
||||
def owned
|
||||
owned = true
|
||||
if params[:closet_hanger]
|
||||
owned = case params[:closet_hanger][:owned]
|
||||
when 'true', '1' then true
|
||||
when 'false', '0' then false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_after_destroy!
|
||||
flash[:success] = "Success! You do not #{@closet_hanger.verb(:you)} #{@item.name}."
|
||||
redirect_back!(@item)
|
||||
end
|
||||
end
|
||||
|
49
app/controllers/closet_lists_controller.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
class ClosetListsController < ApplicationController
|
||||
before_filter :authorize_user!
|
||||
before_filter :find_closet_list, :only => [:edit, :update, :destroy]
|
||||
|
||||
def create
|
||||
@closet_list = current_user.closet_lists.build params[:closet_list]
|
||||
if @closet_list.save
|
||||
save_successful!
|
||||
else
|
||||
save_failed!
|
||||
render :action => :new
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@closet_list.destroy
|
||||
flash[:success] = "Successfully deleted \"#{@closet_list.name}\""
|
||||
redirect_to user_closet_hangers_path(current_user)
|
||||
end
|
||||
|
||||
def new
|
||||
@closet_list = current_user.closet_lists.build params[:closet_list]
|
||||
end
|
||||
|
||||
def update
|
||||
if @closet_list.update_attributes(params[:closet_list])
|
||||
save_successful!
|
||||
else
|
||||
save_failed!
|
||||
render :action => :edit
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def find_closet_list
|
||||
@closet_list = current_user.closet_lists.find params[:id]
|
||||
end
|
||||
|
||||
def save_failed!
|
||||
flash.now[:alert] = "We can't save this list because: #{@closet_list.errors.full_messages.to_sentence}"
|
||||
end
|
||||
|
||||
def save_successful!
|
||||
flash[:success] = "Successfully saved \"#{@closet_list.name}\""
|
||||
redirect_to user_closet_hangers_path(current_user)
|
||||
end
|
||||
end
|
||||
|
67
app/controllers/closet_pages_controller.rb
Normal file
|
@ -0,0 +1,67 @@
|
|||
class ClosetPagesController < ApplicationController
|
||||
include ActionView::Helpers::TextHelper
|
||||
|
||||
before_filter :authenticate_user!, :build_closet_page
|
||||
|
||||
rescue_from ClosetPage::ParseError, :with => :on_parse_error
|
||||
|
||||
def create
|
||||
if params[:closet_page] && params[:closet_page][:source]
|
||||
@closet_page.index = params[:closet_page][:index]
|
||||
@closet_page.source = params[:closet_page][:source]
|
||||
|
||||
saved_counts = @closet_page.save_hangers!
|
||||
|
||||
any_created = saved_counts[:created] > 0
|
||||
any_updated = saved_counts[:updated] > 0
|
||||
if any_created || any_updated
|
||||
message = "Page #{@closet_page.index} saved! We "
|
||||
message << "added " + pluralize(saved_counts[:created], 'item') + " to your closet" if any_created
|
||||
message << " and " if any_created && any_updated
|
||||
message << "updated the count on " + pluralize(saved_counts[:updated], 'item') if any_updated
|
||||
message << ". "
|
||||
else
|
||||
message = "Success! We checked that page, and we already had all this data recorded. "
|
||||
end
|
||||
|
||||
unless @closet_page.unknown_item_names.empty?
|
||||
message << "We also found " +
|
||||
pluralize(@closet_page.unknown_item_names.size, 'item') +
|
||||
" we didn't recognize: " +
|
||||
@closet_page.unknown_item_names.to_sentence +
|
||||
". Please put each item on your pet and type its name in on the " +
|
||||
"home page so we can have a record of it. Thanks! "
|
||||
end
|
||||
|
||||
if @closet_page.last?
|
||||
message << "That was the last page of your Neopets closet."
|
||||
destination = user_closet_hangers_path(current_user)
|
||||
else
|
||||
message << "Now the frame should contain page #{@closet_page.index + 1}. Paste that source code over, too."
|
||||
destination = {:action => :new, :index => (@closet_page.index + 1)}
|
||||
end
|
||||
|
||||
flash[:success] = message
|
||||
redirect_to destination
|
||||
else
|
||||
redirect_to :action => :new
|
||||
end
|
||||
end
|
||||
|
||||
def new
|
||||
@closet_page.index ||= 1
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def build_closet_page
|
||||
@closet_page = ClosetPage.new(current_user)
|
||||
@closet_page.index = params[:index]
|
||||
end
|
||||
|
||||
def on_parse_error
|
||||
flash[:alert] = "We had trouble reading your source code. Is it a valid HTML document? Make sure you pasted the computery-looking result of clicking View Frame Source, and not the pretty page itself."
|
||||
render :action => :new
|
||||
end
|
||||
end
|
||||
|
|
@ -10,7 +10,8 @@ class ItemsController < ApplicationController
|
|||
else
|
||||
per_page = nil
|
||||
end
|
||||
@items = Item.search(@query).alphabetize.paginate :page => params[:page], :per_page => per_page
|
||||
@items = Item.search(@query, current_user).alphabetize.paginate :page => params[:page], :per_page => per_page
|
||||
assign_closeted!
|
||||
respond_to do |format|
|
||||
format.html { render }
|
||||
format.json { render :json => {:items => @items, :total_pages => @items.total_pages} }
|
||||
|
@ -24,6 +25,7 @@ class ItemsController < ApplicationController
|
|||
end
|
||||
elsif params.has_key?(:ids) && params[:ids].is_a?(Array)
|
||||
@items = Item.find(params[:ids])
|
||||
assign_closeted!
|
||||
respond_to do |format|
|
||||
format.json { render :json => @items }
|
||||
end
|
||||
|
@ -37,6 +39,19 @@ class ItemsController < ApplicationController
|
|||
|
||||
def show
|
||||
@item = Item.find params[:id]
|
||||
|
||||
@trading_closet_hangers_by_owned = {
|
||||
true => @item.closet_hangers.owned_trading.newest.includes(:user),
|
||||
false => @item.closet_hangers.wanted_trading.newest.includes(:user)
|
||||
}
|
||||
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def needed
|
||||
|
@ -50,11 +65,16 @@ class ItemsController < ApplicationController
|
|||
raise ActiveRecord::RecordNotFound, 'Pet type not found'
|
||||
end
|
||||
@items = @pet_type.needed_items.alphabetize
|
||||
assign_closeted!
|
||||
@pet_name = params[:name]
|
||||
render :layout => 'application'
|
||||
end
|
||||
|
||||
private
|
||||
protected
|
||||
|
||||
def assign_closeted!
|
||||
current_user.assign_closeted_to_items!(@items) if user_signed_in?
|
||||
end
|
||||
|
||||
def set_query
|
||||
@query = params[:q]
|
||||
|
|
|
@ -1,5 +1,40 @@
|
|||
class UsersController < ApplicationController
|
||||
before_filter :find_and_authorize_user!, :only => [:update]
|
||||
|
||||
def top_contributors
|
||||
@users = User.top_contributors.paginate :page => params[:page], :per_page => 20
|
||||
end
|
||||
|
||||
def update
|
||||
success = @user.update_attributes params[:user]
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
if success
|
||||
flash[:success] = "Settings successfully saved"
|
||||
redirect_back! user_closet_hangers_path(@user)
|
||||
else
|
||||
flash[:alert] = "Error saving user settings: #{@user.errors.full_messages.to_sentence}"
|
||||
end
|
||||
}
|
||||
|
||||
format.json {
|
||||
if success
|
||||
render :json => true
|
||||
else
|
||||
render :json => {:errors => @user.errors.full_messages}, :status => :unprocessable_entity
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def find_and_authorize_user!
|
||||
if current_user.id == params[:id].to_i
|
||||
@user = current_user
|
||||
else
|
||||
raise AccessDenied
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -29,10 +29,22 @@ module ApplicationHelper
|
|||
content_tag(:div, html, :class => 'campaign-progress-wrapper')
|
||||
end
|
||||
|
||||
def canonical_path(resource)
|
||||
content_for :meta, tag(:link, :rel => 'canonical', :href => url_for(resource))
|
||||
end
|
||||
|
||||
def contact_email
|
||||
"webmaster@openneo.net"
|
||||
end
|
||||
|
||||
def feedback_url
|
||||
"http://openneo.uservoice.com/forums/40720-dress-to-impress"
|
||||
end
|
||||
|
||||
def flashes
|
||||
raw(flash.inject('') do |html, pair|
|
||||
key, value = pair
|
||||
html + content_tag('p', value, :class => key)
|
||||
html + content_tag('p', value, :class => "flash #{key}")
|
||||
end)
|
||||
end
|
||||
|
||||
|
@ -86,6 +98,15 @@ module ApplicationHelper
|
|||
hidden_field_tag 'origin', value, :id => nil
|
||||
end
|
||||
|
||||
def return_to_field_tag
|
||||
hidden_field_tag :return_to, request.fullpath
|
||||
end
|
||||
|
||||
def secondary_nav(&block)
|
||||
content_for :before_flashes,
|
||||
content_tag(:nav, :id => 'secondary-nav', &block)
|
||||
end
|
||||
|
||||
def show_title_header?
|
||||
params[:controller] != 'items'
|
||||
end
|
||||
|
|
110
app/helpers/closet_hangers_helper.rb
Normal file
|
@ -0,0 +1,110 @@
|
|||
require 'cgi'
|
||||
|
||||
module ClosetHangersHelper
|
||||
def closet_hangers_help_class
|
||||
'hidden' unless @user.closet_hangers.empty?
|
||||
end
|
||||
|
||||
def closet_hanger_verb(owned, positive=true)
|
||||
ClosetHanger.verb(closet_hanger_subject, owned, positive)
|
||||
end
|
||||
|
||||
def send_neomail_url(user)
|
||||
"http://www.neopets.com/neomessages.phtml?type=send&recipient=#{CGI.escape @user.neopets_username}"
|
||||
end
|
||||
|
||||
def closet_hanger_subject
|
||||
public_perspective? ? @user.name : :you
|
||||
end
|
||||
|
||||
def hangers_group_visibility_field_name(owned)
|
||||
owned ? :owned_closet_hangers_visibility : :wanted_closet_hangers_visibility
|
||||
end
|
||||
|
||||
def closet_visibility_choices(*args)
|
||||
ClosetVisibility.levels.map do |level|
|
||||
[level.send(*args), level.id]
|
||||
end
|
||||
end
|
||||
|
||||
def closet_visibility_descriptions(subject='these items')
|
||||
content = ''
|
||||
ClosetVisibility.levels.each do |level|
|
||||
content << content_tag(:li, level.description(subject), 'data-id' => level.id)
|
||||
end
|
||||
content_tag :ul, content.html_safe, :class => 'visibility-descriptions'
|
||||
end
|
||||
|
||||
# Do we have either unlisted hangers that are owned/wanted, or non-empty
|
||||
# owned/wanted lists?
|
||||
def has_hangers?(owned)
|
||||
# If we have unlisted hangers of this type, pass.
|
||||
return true if @unlisted_closet_hangers_by_owned.has_key?(owned)
|
||||
|
||||
# Additionally, if we have no lists of this type, fail.
|
||||
lists = @closet_lists_by_owned[owned]
|
||||
return false unless lists
|
||||
|
||||
# If any of those lists are non-empty, pass.
|
||||
lists.each do |list|
|
||||
return true unless list.hangers.empty?
|
||||
end
|
||||
|
||||
# Otherwise, all of the lists are empty. Fail.
|
||||
return false
|
||||
end
|
||||
|
||||
def has_lists?(owned)
|
||||
@closet_lists_by_owned.has_key?(owned)
|
||||
end
|
||||
|
||||
def link_to_add_closet_list(content, options)
|
||||
owned = options.delete(:owned)
|
||||
path = new_user_closet_list_path current_user,
|
||||
:closet_list => {:hangers_owned => owned}
|
||||
link_to(content, path, options)
|
||||
end
|
||||
|
||||
def nc_icon_url
|
||||
"http://#{request.host}#{image_path 'nc.png'}"
|
||||
end
|
||||
|
||||
def petpage_item_name(item)
|
||||
item.name.gsub(/ on/i, ' o<b></b>n')
|
||||
end
|
||||
|
||||
def public_perspective?
|
||||
@public_perspective
|
||||
end
|
||||
|
||||
PETPAGE_HANGER_BATCH_SIZE = 5
|
||||
def render_batched_petpage_hangers(hangers)
|
||||
output do |html|
|
||||
hangers.in_groups_of(PETPAGE_HANGER_BATCH_SIZE) do |batch|
|
||||
content = batch.map do |hanger|
|
||||
render 'petpage_hanger', :hanger => hanger if hanger
|
||||
end.join.html_safe
|
||||
html << content_tag(:div, content, :class => 'dti-item-row')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def render_closet_lists(lists)
|
||||
if lists
|
||||
render :partial => 'closet_lists/closet_list', :collection => lists,
|
||||
:locals => {:show_controls => !public_perspective?}
|
||||
end
|
||||
end
|
||||
|
||||
def render_unlisted_closet_hangers(owned)
|
||||
hangers_content = render :partial => 'closet_hanger',
|
||||
:collection => @unlisted_closet_hangers_by_owned[owned],
|
||||
:locals => {:show_controls => !public_perspective?}
|
||||
end
|
||||
|
||||
def unlisted_hangers_count(owned)
|
||||
hangers = @unlisted_closet_hangers_by_owned[owned]
|
||||
hangers ? hangers.size : 0
|
||||
end
|
||||
end
|
||||
|
30
app/helpers/closet_lists_helper.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
module ClosetListsHelper
|
||||
def closet_list_delete_confirmation(closet_list)
|
||||
"Are you sure you want to delete \"#{closet_list.name}\"?".tap do |msg|
|
||||
unless closet_list.hangers.empty?
|
||||
msg << " Even if you do, we'll remember that you " +
|
||||
ClosetHanger.verb(:you, closet_list.hangers_owned) +
|
||||
" these items."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def closet_list_description_format(list)
|
||||
md = RDiscount.new(list.description)
|
||||
Sanitize.clean(md.to_html, Sanitize::Config::BASIC).html_safe
|
||||
end
|
||||
|
||||
def hangers_owned_options
|
||||
@hangers_owned_options ||= [true, false].map do |owned|
|
||||
verb = ClosetHanger.verb(:i, owned)
|
||||
["items I #{verb}", owned]
|
||||
end
|
||||
end
|
||||
|
||||
def render_sorted_hangers(list, show_controls)
|
||||
render :partial => 'closet_hanger',
|
||||
:collection => list.hangers.sort { |x,y| x.item.name <=> y.item.name },
|
||||
:locals => {:show_controls => show_controls}
|
||||
end
|
||||
end
|
||||
|
10
app/helpers/closet_pages_helper.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
module ClosetPagesHelper
|
||||
def link_to_neopets_login(content)
|
||||
link_to content, neopets_login_url, :target => "_blank"
|
||||
end
|
||||
|
||||
def neopets_login_url
|
||||
"http://www.neopets.com/loginpage.phtml"
|
||||
end
|
||||
end
|
||||
|
|
@ -48,6 +48,28 @@ module ItemsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def closeted_icons_for(item)
|
||||
content = ''.html_safe
|
||||
|
||||
if item.owned?
|
||||
content << image_tag(
|
||||
'owned.png',
|
||||
:title => 'You own this',
|
||||
:alt => 'Own'
|
||||
)
|
||||
end
|
||||
|
||||
if item.wanted?
|
||||
content << image_tag(
|
||||
'wanted.png',
|
||||
:title => 'You want this',
|
||||
:alt => 'Want'
|
||||
)
|
||||
end
|
||||
|
||||
content_tag :div, content, :class => 'closeted-icons'
|
||||
end
|
||||
|
||||
def list_zones(zones, method=:label)
|
||||
zones.sort { |x,y| x.label <=> y.label }.map(&method).join(', ')
|
||||
end
|
||||
|
@ -60,6 +82,12 @@ module ItemsHelper
|
|||
sprintf(NeoitemsURLFormat, CGI::escape(item.name))
|
||||
end
|
||||
|
||||
def render_trading_closet_hangers(owned)
|
||||
@trading_closet_hangers_by_owned[owned].map do |hanger|
|
||||
link_to hanger.user.name, user_closet_hangers_path(hanger.user)
|
||||
end.to_sentence.html_safe
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_on_pet_types(species, special_color=nil, &block)
|
||||
|
|
58
app/models/closet_hanger.rb
Normal file
|
@ -0,0 +1,58 @@
|
|||
class ClosetHanger < ActiveRecord::Base
|
||||
belongs_to :item
|
||||
belongs_to :list, :class_name => 'ClosetList'
|
||||
belongs_to :user
|
||||
|
||||
attr_accessible :list_id, :owned, :quantity
|
||||
|
||||
validates :item_id, :uniqueness => {:scope => [:user_id, :owned]}
|
||||
validates :quantity, :numericality => {:greater_than => 0}
|
||||
validates_presence_of :item, :user
|
||||
|
||||
validate :list_belongs_to_user
|
||||
|
||||
scope :alphabetical_by_item_name, joins(:item).order(Item.arel_table[:name])
|
||||
scope :newest, order(arel_table[:created_at].desc)
|
||||
scope :owned_before_wanted, order(arel_table[:owned].desc)
|
||||
scope :unlisted, where(:list_id => nil)
|
||||
|
||||
{:owned => true, :wanted => false}.each do |name, owned|
|
||||
scope "#{name}_trading", joins(:user).includes(:list).
|
||||
where(:owned => owned).
|
||||
where((
|
||||
User.arel_table["#{name}_closet_hangers_visibility"].gteq(ClosetVisibility[:trading].id)
|
||||
).or(
|
||||
ClosetList.arel_table[:visibility].gteq(ClosetVisibility[:trading].id)
|
||||
))
|
||||
end
|
||||
|
||||
before_validation :set_owned_by_list
|
||||
|
||||
def verb(subject=:someone)
|
||||
self.class.verb(subject, owned?)
|
||||
end
|
||||
|
||||
def self.verb(subject, owned, positive=true)
|
||||
base = (owned) ? 'own' : 'want'
|
||||
base << 's' if positive && subject != :you && subject != :i
|
||||
base
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def list_belongs_to_user
|
||||
if list_id?
|
||||
if list
|
||||
errors.add(:list_id, "must belong to you") unless list.user_id == user_id
|
||||
else
|
||||
errors.add(:list, "must exist")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def set_owned_by_list
|
||||
self.owned = list.hangers_owned if list
|
||||
true
|
||||
end
|
||||
end
|
||||
|
31
app/models/closet_list.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
class ClosetList < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
has_many :hangers, :class_name => 'ClosetHanger', :foreign_key => 'list_id',
|
||||
:dependent => :nullify
|
||||
|
||||
attr_accessible :description, :hangers_owned, :name, :visibility
|
||||
|
||||
validates :name, :presence => true, :uniqueness => {:scope => :user_id}
|
||||
validates :user, :presence => true
|
||||
validates :hangers_owned, :inclusion => {:in => [true, false], :message => "can't be blank"}
|
||||
|
||||
scope :alphabetical, order(:name)
|
||||
scope :public, where(arel_table[:visibility].gteq(ClosetVisibility[:public].id))
|
||||
scope :visible_to, lambda { |user|
|
||||
condition = arel_table[:visibility].gteq(ClosetVisibility[:public].id)
|
||||
condition = condition.or(arel_table[:user_id].eq(user.id)) if user
|
||||
where(condition)
|
||||
}
|
||||
|
||||
after_save :sync_hangers_owned!
|
||||
|
||||
def sync_hangers_owned!
|
||||
if hangers_owned_changed?
|
||||
hangers.each do |hanger|
|
||||
hanger.owned = hangers_owned
|
||||
hanger.save!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
138
app/models/closet_page.rb
Normal file
|
@ -0,0 +1,138 @@
|
|||
require 'yaml'
|
||||
|
||||
class ClosetPage
|
||||
include ActiveModel::Conversion
|
||||
extend ActiveModel::Naming
|
||||
|
||||
SELECTORS = {
|
||||
:items => "form[action=\"process_closet.phtml\"] tr[bgcolor!=silver][bgcolor!=\"#E4E4E4\"]",
|
||||
:item_thumbnail => "img",
|
||||
:item_name => "td:nth-child(2)",
|
||||
:item_quantity => "td:nth-child(5)",
|
||||
:item_remove => "input",
|
||||
:page_select => "select[name=page]",
|
||||
:selected => "option[selected]"
|
||||
}
|
||||
|
||||
attr_accessor :index
|
||||
attr_reader :hangers, :source, :total_pages, :unknown_item_names, :user
|
||||
|
||||
def initialize(user)
|
||||
raise ArgumentError, "Expected #{user.inspect} to be a User", caller unless user.is_a?(User)
|
||||
@user = user
|
||||
end
|
||||
|
||||
def last?
|
||||
@index == @total_pages
|
||||
end
|
||||
|
||||
def persisted?
|
||||
false
|
||||
end
|
||||
|
||||
def save_hangers!
|
||||
counts = {:created => 0, :updated => 0}
|
||||
ClosetHanger.transaction do
|
||||
@hangers.each do |hanger|
|
||||
if hanger.new_record?
|
||||
counts[:created] += 1
|
||||
hanger.save!
|
||||
elsif hanger.changed?
|
||||
counts[:updated] += 1
|
||||
hanger.save!
|
||||
end
|
||||
end
|
||||
end
|
||||
counts
|
||||
end
|
||||
|
||||
def source=(source)
|
||||
@source = source
|
||||
parse_source!(source)
|
||||
end
|
||||
|
||||
def url
|
||||
"http://www.neopets.com/closet.phtml?per_page=50&page=#{@index}"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def element(selector_name, parent)
|
||||
parent.at_css(SELECTORS[selector_name]) ||
|
||||
raise(ParseError, "Closet #{selector_name} element not found in #{parent.inspect}")
|
||||
end
|
||||
|
||||
def elements(selector_name, parent)
|
||||
parent.css(SELECTORS[selector_name])
|
||||
end
|
||||
|
||||
def parse_source!(source)
|
||||
doc = Nokogiri::HTML(source)
|
||||
|
||||
page_selector = element(:page_select, doc)
|
||||
@total_pages = page_selector.children.size
|
||||
@index = element(:selected, page_selector)['value'].to_i
|
||||
|
||||
items_data = {
|
||||
:id => {},
|
||||
:thumbnail_url => {}
|
||||
}
|
||||
|
||||
# Go through the items, and find the ID/thumbnail for each and data with it
|
||||
elements(:items, doc).each do |row|
|
||||
# For normal items, the td contains essentially:
|
||||
# <b>NAME<br/><span>OPTIONAL ADJECTIVE</span></b>
|
||||
# For PB items, the td contains:
|
||||
# NAME<br/><span>OPTIONAL ADJECTIVE</span>
|
||||
# So, we want the first text node. If it's a PB item, that's the first
|
||||
# child. If it's a normal item, it's the first child <b>'s child.
|
||||
name_el = element(:item_name, row).children[0]
|
||||
name_el = name_el.children[0] if name_el.name == 'b'
|
||||
|
||||
data = {
|
||||
:name => name_el.text,
|
||||
:quantity => element(:item_quantity, row).text.to_i
|
||||
}
|
||||
|
||||
if id = element(:item_remove, row)['name']
|
||||
id = id.to_i
|
||||
items_data[:id][id] = data
|
||||
else # if this is a pb item, which does not give ID, go by thumbnail
|
||||
thumbnail_url = element(:item_thumbnail, row)['src']
|
||||
items_data[:thumbnail_url][thumbnail_url] = data
|
||||
end
|
||||
end
|
||||
|
||||
# Find items with either a matching ID or matching thumbnail URL
|
||||
# Check out that single-query beauty :)
|
||||
i = Item.arel_table
|
||||
items = Item.where(
|
||||
i[:id].in(items_data[:id].keys).
|
||||
or(
|
||||
i[:thumbnail_url].in(items_data[:thumbnail_url].keys)
|
||||
)
|
||||
)
|
||||
|
||||
# Create closet hanger from each item, and remove them from the reference
|
||||
# lists
|
||||
@hangers = items.map do |item|
|
||||
data = items_data[:id].delete(item.id) ||
|
||||
items_data[:thumbnail_url].delete(item.thumbnail_url)
|
||||
hanger = @user.closet_hangers.find_or_initialize_by_item_id(item.id)
|
||||
hanger.quantity = data[:quantity]
|
||||
hanger
|
||||
end
|
||||
|
||||
# Take the names of the items remaining in the reference lists, meaning
|
||||
# that they weren't found
|
||||
@unknown_item_names = []
|
||||
items_data.each do |type, data_by_key|
|
||||
data_by_key.each do |key, data|
|
||||
@unknown_item_names << data[:name]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ParseError < RuntimeError;end
|
||||
end
|
||||
|
58
app/models/closet_visibility.rb
Normal file
|
@ -0,0 +1,58 @@
|
|||
module ClosetVisibility
|
||||
class Level
|
||||
attr_accessor :id, :name
|
||||
attr_writer :description
|
||||
|
||||
def initialize(data)
|
||||
data.each do |key, value|
|
||||
send("#{key}=", value)
|
||||
end
|
||||
end
|
||||
|
||||
def description(subject=nil)
|
||||
if subject
|
||||
@description.sub('$SUBJECT', subject).capitalize
|
||||
else
|
||||
@description
|
||||
end
|
||||
end
|
||||
|
||||
def human_name
|
||||
name.to_s.humanize
|
||||
end
|
||||
end
|
||||
|
||||
LEVELS = [
|
||||
Level.new(
|
||||
:id => 0,
|
||||
:name => :private,
|
||||
:description => "Only you can see $SUBJECT"
|
||||
),
|
||||
Level.new(
|
||||
:id => 1,
|
||||
:name => :public,
|
||||
:description => "Anyone who visits this page can see $SUBJECT"
|
||||
),
|
||||
Level.new(
|
||||
:id => 2,
|
||||
:name => :trading,
|
||||
:description => "$SUBJECT will be publicly listed for trades"
|
||||
)
|
||||
]
|
||||
|
||||
LEVELS_BY_NAME = {}.tap do |levels_by_name|
|
||||
LEVELS.each do |level|
|
||||
levels_by_name[level.id] = level
|
||||
levels_by_name[level.name] = level
|
||||
end
|
||||
end
|
||||
|
||||
def self.[](id)
|
||||
LEVELS_BY_NAME[id]
|
||||
end
|
||||
|
||||
def self.levels
|
||||
LEVELS
|
||||
end
|
||||
end
|
||||
|
|
@ -1,15 +1,18 @@
|
|||
# requires item sweeper at bottom
|
||||
|
||||
class Item < ActiveRecord::Base
|
||||
include PrettyParam
|
||||
|
||||
SwfAssetType = 'object'
|
||||
|
||||
has_many :closet_hangers
|
||||
has_one :contribution, :as => :contributed
|
||||
has_many :parent_swf_asset_relationships, :foreign_key => 'parent_id',
|
||||
:conditions => {:swf_asset_type => SwfAssetType}
|
||||
has_many :swf_assets, :through => :parent_swf_asset_relationships, :source => :object_asset,
|
||||
:conditions => {:type => SwfAssetType}
|
||||
|
||||
attr_writer :current_body_id
|
||||
attr_writer :current_body_id, :owned, :wanted
|
||||
|
||||
NCRarities = [0, 500]
|
||||
PAINTBRUSH_SET_DESCRIPTION = 'This item is part of a deluxe paint brush set!'
|
||||
|
@ -42,12 +45,24 @@ class Item < ActiveRecord::Base
|
|||
|
||||
scope :sitemap, select([:id, :name]).order(:id).limit(49999)
|
||||
|
||||
# Not defining validations, since this app is currently read-only
|
||||
scope :with_closet_hangers, joins(:closet_hangers)
|
||||
|
||||
def closeted?
|
||||
@owned || @wanted
|
||||
end
|
||||
|
||||
def nc?
|
||||
NCRarities.include?(rarity_index)
|
||||
end
|
||||
|
||||
def owned?
|
||||
@owned
|
||||
end
|
||||
|
||||
def wanted?
|
||||
@wanted
|
||||
end
|
||||
|
||||
def restricted_zones
|
||||
unless @restricted_zones
|
||||
@restricted_zones = []
|
||||
|
@ -115,7 +130,7 @@ class Item < ActiveRecord::Base
|
|||
@supported_species ||= species_support_ids.blank? ? Species.all : species_support_ids.sort.map { |id| Species.find(id) }
|
||||
end
|
||||
|
||||
def self.search(query)
|
||||
def self.search(query, user=nil)
|
||||
raise SearchError, "Please provide a search query" unless query
|
||||
query = query.strip
|
||||
raise SearchError, "Search queries should be at least 3 characters" if query.length < 3
|
||||
|
@ -143,7 +158,7 @@ class Item < ActiveRecord::Base
|
|||
limited_filters_used << condition.filter
|
||||
end
|
||||
end
|
||||
condition.narrow(scope)
|
||||
condition.narrow(scope, user)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -154,19 +169,12 @@ class Item < ActiveRecord::Base
|
|||
:name => name,
|
||||
:thumbnail_url => thumbnail_url,
|
||||
:zones_restrict => zones_restrict,
|
||||
:rarity_index => rarity_index
|
||||
:rarity_index => rarity_index,
|
||||
:owned => owned?,
|
||||
:wanted => wanted?
|
||||
}
|
||||
end
|
||||
|
||||
URL_CHAR_BLACKLIST = /[^a-z0-9\-]/i
|
||||
def name_for_url
|
||||
name.downcase.gsub(' ', '-').gsub(URL_CHAR_BLACKLIST, '')
|
||||
end
|
||||
|
||||
def to_param
|
||||
"#{id}-#{name_for_url}"
|
||||
end
|
||||
|
||||
before_create do
|
||||
self.sold_in_mall ||= false
|
||||
true
|
||||
|
@ -631,6 +639,7 @@ class Item < ActiveRecord::Base
|
|||
name = name.to_s
|
||||
SearchFilterScopes << name
|
||||
LimitedSearchFilters << name if options[:limit]
|
||||
|
||||
(class << self; self; end).instance_eval do
|
||||
if options[:full]
|
||||
define_method "search_filter_#{name}", &options[:full]
|
||||
|
@ -648,10 +657,13 @@ class Item < ActiveRecord::Base
|
|||
search_filter name, options, &block
|
||||
end
|
||||
|
||||
def self.search_filter_block(options, positive)
|
||||
Proc.new { |str, scope|
|
||||
condition = yield(str)
|
||||
condition = "!(#{condition.to_sql})" unless positive
|
||||
def self.search_filter_block(options, positive, &block)
|
||||
Proc.new { |str, user, scope|
|
||||
condition = block.arity == 1 ? block.call(str) : block.call(str, user)
|
||||
unless positive
|
||||
condition = condition.to_sql if condition.respond_to?(:to_sql)
|
||||
condition = "!(#{condition})"
|
||||
end
|
||||
scope = scope.send(options[:scope]) if options[:scope]
|
||||
scope.where(condition)
|
||||
}
|
||||
|
@ -679,6 +691,47 @@ class Item < ActiveRecord::Base
|
|||
filter
|
||||
end
|
||||
|
||||
USER_ADJECTIVES = {
|
||||
'own' => true,
|
||||
'owns' => true,
|
||||
'owned' => true,
|
||||
'want' => false,
|
||||
'wants' => false,
|
||||
'wanted' => false,
|
||||
'all' => nil,
|
||||
'items' => nil
|
||||
}
|
||||
def self.parse_user_adjective(adjective, user)
|
||||
unless USER_ADJECTIVES.has_key?(adjective)
|
||||
raise SearchError, "We don't understand user:#{adjective}. " +
|
||||
"Find items you own with user:owns, items you want with user:wants, or " +
|
||||
"both with user:all"
|
||||
end
|
||||
|
||||
unless user
|
||||
raise SearchError, "It looks like you're not logged in, so you don't own any items."
|
||||
end
|
||||
|
||||
USER_ADJECTIVES[adjective]
|
||||
end
|
||||
|
||||
search_filter :user do |adjective, user|
|
||||
# Though joins may seem more efficient here for the positive case, we need
|
||||
# to be able to handle cases like "user:owns user:wants", which breaks on
|
||||
# the JOIN approach. Just have to look up the IDs in advance.
|
||||
|
||||
owned_value = parse_user_adjective(adjective, user)
|
||||
hangers = ClosetHanger.arel_table
|
||||
items = user.closeted_items
|
||||
items = items.where(ClosetHanger.arel_table[:owned].eq(owned_value)) unless owned_value.nil?
|
||||
item_ids = items.map(&:id)
|
||||
# Though it's best to do arel_table[:id].in(item_ids), it breaks in this
|
||||
# version of Arel, and other conditions will overwrite this one. Since IDs
|
||||
# are guaranteed to be integers, let's just build our own string condition
|
||||
# and be done with it.
|
||||
"id IN (#{item_ids.join(',')})"
|
||||
end
|
||||
|
||||
search_filter :only do |species_name|
|
||||
begin
|
||||
id = Species.require_by_name(species_name).id
|
||||
|
@ -709,7 +762,7 @@ class Item < ActiveRecord::Base
|
|||
SwfAsset.arel_table[:zone_id].in(zone_set.map(&:id))
|
||||
end
|
||||
|
||||
single_search_filter :not_type, :full => lambda { |zone_set_name, scope|
|
||||
single_search_filter :not_type, :full => lambda { |zone_set_name, user, scope|
|
||||
zone_set = Zone::ItemZoneSets[zone_set_name]
|
||||
raise SearchError, "Type \"#{zone_set_name}\" does not exist" unless zone_set
|
||||
psa = ParentSwfAssetRelationship.arel_table.alias
|
||||
|
@ -757,10 +810,10 @@ class Item < ActiveRecord::Base
|
|||
@positive = !@positive
|
||||
end
|
||||
|
||||
def narrow(scope)
|
||||
def narrow(scope, user)
|
||||
if SearchFilterScopes.include?(filter)
|
||||
polarized_filter = @positive ? filter : "not_#{filter}"
|
||||
Item.send("search_filter_#{polarized_filter}", self, scope)
|
||||
Item.send("search_filter_#{polarized_filter}", self, user, scope)
|
||||
else
|
||||
raise SearchError, "Filter #{filter} does not exist"
|
||||
end
|
||||
|
|
11
app/models/pretty_param.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
module PrettyParam
|
||||
BLACKLIST = /[^a-z0-9]/i
|
||||
def name_for_param
|
||||
name.split(BLACKLIST).select { |word| !word.blank? }.join('-')
|
||||
end
|
||||
|
||||
def to_param
|
||||
"#{id}-#{name_for_param}"
|
||||
end
|
||||
end
|
||||
|
|
@ -1,7 +1,12 @@
|
|||
class User < ActiveRecord::Base
|
||||
include PrettyParam
|
||||
|
||||
DefaultAuthServerId = 1
|
||||
PreviewTopContributorsCount = 3
|
||||
|
||||
has_many :closet_hangers
|
||||
has_many :closet_lists
|
||||
has_many :closeted_items, :through => :closet_hangers, :source => :item
|
||||
has_many :contributions
|
||||
has_many :outfits
|
||||
|
||||
|
@ -9,6 +14,9 @@ class User < ActiveRecord::Base
|
|||
|
||||
devise :rememberable
|
||||
|
||||
attr_accessible :neopets_username, :owned_closet_hangers_visibility,
|
||||
:wanted_closet_hangers_visibility
|
||||
|
||||
def contribute!(pet)
|
||||
new_contributions = []
|
||||
new_points = 0
|
||||
|
@ -39,6 +47,30 @@ class User < ActiveRecord::Base
|
|||
new_points
|
||||
end
|
||||
|
||||
def assign_closeted_to_items!(items)
|
||||
# Assigning these items to a hash by ID means that we don't have to go
|
||||
# N^2 searching the items list for items that match the given IDs or vice
|
||||
# versa, and everything stays a lovely O(n)
|
||||
items_by_id = {}
|
||||
items.each { |item| items_by_id[item.id] = item }
|
||||
closet_hangers.where(:item_id => items_by_id.keys).each do |hanger|
|
||||
item = items_by_id[hanger.item_id]
|
||||
if hanger.owned?
|
||||
item.owned = true
|
||||
else
|
||||
item.wanted = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def closet_hangers_groups_visible_to(user)
|
||||
return [true, false] if user == self
|
||||
[].tap do |groups|
|
||||
groups << true if owned_closet_hangers_visibility >= ClosetVisibility[:public].id
|
||||
groups << false if wanted_closet_hangers_visibility >= ClosetVisibility[:public].id
|
||||
end
|
||||
end
|
||||
|
||||
def self.find_or_create_from_remote_auth_data(user_data)
|
||||
user = find_or_initialize_by_remote_id_and_auth_server_id(
|
||||
user_data['id'],
|
||||
|
@ -56,3 +88,4 @@ class User < ActiveRecord::Base
|
|||
user ? user.points : 0
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ $container_width: 800px
|
|||
input, button, select, label
|
||||
cursor: pointer
|
||||
|
||||
input[type=text], input[type=password], input[type=search], select
|
||||
input[type=text], input[type=password], input[type=search], input[type=number], select, textarea
|
||||
+border-radius(3px)
|
||||
background: #fff
|
||||
border: 1px solid $input-border-color
|
||||
|
@ -85,14 +85,14 @@ input[type=text], input[type=password], input[type=search], select
|
|||
&:focus, &:active
|
||||
color: inherit
|
||||
|
||||
textarea
|
||||
font: inherit
|
||||
|
||||
a.button, input[type=submit], button
|
||||
+awesome-button
|
||||
&.loud
|
||||
+loud-awesome-button
|
||||
|
||||
a.button
|
||||
+arrowed-awesome-button
|
||||
|
||||
ul.buttons
|
||||
margin-bottom: 1em
|
||||
li
|
||||
|
@ -155,9 +155,21 @@ ul.buttons
|
|||
&:hover span
|
||||
text-decoration: none
|
||||
|
||||
#userbar-items-link
|
||||
+hover-link
|
||||
background: $module-bg-color
|
||||
padding: .25em .5em
|
||||
|
||||
&:after
|
||||
color: red
|
||||
content: "new!"
|
||||
font-size: 85%
|
||||
margin-left: .5em
|
||||
|
||||
.object
|
||||
+inline-block
|
||||
padding: .5em
|
||||
margin: $object-padding 0
|
||||
padding: 0 $object-padding
|
||||
position: relative
|
||||
text-align: center
|
||||
vertical-align: top
|
||||
|
@ -166,13 +178,38 @@ ul.buttons
|
|||
text-decoration: none
|
||||
img
|
||||
+opacity(0.75)
|
||||
&:hover img
|
||||
+opacity(1)
|
||||
img
|
||||
display: block
|
||||
height: $object-img-size
|
||||
margin: 0 auto
|
||||
width: $object-img-size
|
||||
&:hover img, a:hover img
|
||||
// behave in browsers that only respond to a:hover, but also be in the
|
||||
// hover state more often for browsers who support div:hover
|
||||
// (quantity form in user items)
|
||||
+opacity(1)
|
||||
|
||||
.nc-icon, .closeted-icons
|
||||
+opacity(1)
|
||||
background: rgba(255, 255, 255, 0.75)
|
||||
line-height: 1
|
||||
position: absolute
|
||||
top: $object-img-size - $nc-icon-size
|
||||
&:hover
|
||||
+opacity(0.5)
|
||||
background: transparent
|
||||
|
||||
.nc-icon, .closeted-icons img
|
||||
display: inline
|
||||
height: $nc-icon-size
|
||||
width: $nc-icon-size
|
||||
|
||||
.nc-icon
|
||||
right: ($object-width - $object-img-size) / 2 + $object-padding
|
||||
|
||||
$closeted-icons-left: ($object-width - $object-img-size) / 2 + $object-padding
|
||||
.closeted-icons
|
||||
left: $closeted-icons-left
|
||||
|
||||
dt
|
||||
font-weight: bold
|
||||
|
@ -202,15 +239,6 @@ dd
|
|||
.current
|
||||
font-weight: bold
|
||||
|
||||
.object .nc-icon
|
||||
height: 16px
|
||||
position: absolute
|
||||
right: ($object-width - $object-img-size) / 2 + $object-padding
|
||||
top: $object-img-size - $nc-icon-size
|
||||
width: 16px
|
||||
&:hover
|
||||
+opacity(0.5)
|
||||
|
||||
/* Fonts
|
||||
|
||||
/* A font by Jos Buivenga (exljbris) -> www.exljbris.nl
|
||||
|
|
388
app/stylesheets/closet_hangers/_index.sass
Normal file
|
@ -0,0 +1,388 @@
|
|||
@import "partials/context_button"
|
||||
@import "partials/icon"
|
||||
@import "partials/secondary_nav"
|
||||
|
||||
body.closet_hangers-index
|
||||
+secondary-nav
|
||||
|
||||
#title
|
||||
margin-bottom: 0
|
||||
|
||||
#import-link
|
||||
+awesome-button
|
||||
+loud-awesome-button-color
|
||||
|
||||
#closet-hangers-items-search
|
||||
float: right
|
||||
|
||||
input[name=q]
|
||||
&.loading
|
||||
background:
|
||||
image: url(/images/loading.gif)
|
||||
position: 2px center
|
||||
repeat: no-repeat
|
||||
padding-left: $icon-width + 4px
|
||||
|
||||
#closet-hangers-contact
|
||||
clear: both
|
||||
color: $soft-text-color
|
||||
margin-bottom: 1em
|
||||
margin-left: 2em
|
||||
min-height: image-height("neomail.png")
|
||||
|
||||
a, > span
|
||||
+hover-link
|
||||
background:
|
||||
image: image-url("neomail.png")
|
||||
position: left center
|
||||
repeat: no-repeat
|
||||
color: inherit
|
||||
float: left
|
||||
height: 100%
|
||||
padding-left: image-width("neomail.png") + 4px
|
||||
|
||||
> span
|
||||
background-image: image-url("neomail_edit.png")
|
||||
|
||||
input[type=text]
|
||||
width: 10em
|
||||
|
||||
label
|
||||
font-weight: bold
|
||||
margin-right: .5em
|
||||
|
||||
&:after
|
||||
content: ":"
|
||||
|
||||
#edit-contact-link-to-replace-form, #cancel-contact-link
|
||||
display: none
|
||||
|
||||
.edit-contact-link, #cancel-contact-link
|
||||
cursor: pointer
|
||||
text-decoration: underline
|
||||
|
||||
&:hover
|
||||
text-decoration: none
|
||||
|
||||
#edit-contact-link-to-replace-form
|
||||
#contact-link-has-value
|
||||
display: none
|
||||
|
||||
#contact-link-no-value
|
||||
display: inline
|
||||
|
||||
&.has-value
|
||||
#contact-link-has-value
|
||||
display: inline
|
||||
|
||||
#contact-link-no-value
|
||||
display: none
|
||||
|
||||
#cancel-contact-link
|
||||
margin-left: 1em
|
||||
|
||||
#toggle-help
|
||||
+awesome-button
|
||||
cursor: pointer
|
||||
display: none
|
||||
|
||||
#closet-hangers-help.hidden
|
||||
display: none
|
||||
|
||||
#closet-hangers-extras
|
||||
margin:
|
||||
bottom: 2em
|
||||
top: 2em
|
||||
text-align: center
|
||||
|
||||
a
|
||||
+awesome-button
|
||||
margin: 0 0.5em
|
||||
|
||||
#closet-hangers-share
|
||||
font-size: 85%
|
||||
margin-bottom: 1em
|
||||
|
||||
label
|
||||
font-weight: bold
|
||||
margin-right: .5em
|
||||
|
||||
input
|
||||
width: 30em
|
||||
|
||||
#closet-hangers
|
||||
clear: both
|
||||
text-align: center
|
||||
|
||||
.object
|
||||
.quantity
|
||||
+opacity(.75)
|
||||
background: white
|
||||
padding: 6px 4px 4px
|
||||
position: absolute
|
||||
left: ($object-width - $object-img-size) / 2 + $object-padding
|
||||
line-height: 1
|
||||
text-align: left
|
||||
top: 0
|
||||
|
||||
span, input[type=number]
|
||||
font-size: 16px
|
||||
font-weight: bold
|
||||
|
||||
form
|
||||
display: none
|
||||
|
||||
&[data-quantity="1"]
|
||||
.quantity
|
||||
display: none
|
||||
|
||||
.closet-hangers-group
|
||||
border-top: 1px solid $module-border-color
|
||||
margin-bottom: 2em
|
||||
padding-bottom: 1em
|
||||
|
||||
> header
|
||||
border-bottom: 1px solid $soft-border-color
|
||||
display: block
|
||||
margin-bottom: .25em
|
||||
padding: .25em 0
|
||||
position: relative
|
||||
|
||||
h3
|
||||
font-size: 250%
|
||||
margin: 0
|
||||
|
||||
.add-closet-list
|
||||
+awesome-button
|
||||
bottom: 50%
|
||||
margin-bottom: -1em
|
||||
position: absolute
|
||||
right: 1em
|
||||
|
||||
&:active
|
||||
margin-bottom: -1.1em
|
||||
top: auto
|
||||
|
||||
span.show, span.hide
|
||||
color: $soft-text-color
|
||||
display: none
|
||||
font-size: 85%
|
||||
left: 1em
|
||||
position: absolute
|
||||
top: 1em
|
||||
|
||||
&:hover
|
||||
color: inherit
|
||||
text-decoration: underline
|
||||
|
||||
.closet-list
|
||||
border-bottom: 1px solid $soft-border-color
|
||||
padding: .5em 0
|
||||
position: relative
|
||||
|
||||
.visibility-form
|
||||
font-size: 85%
|
||||
left: .5em
|
||||
position: absolute
|
||||
text-align: left
|
||||
top: .25em
|
||||
z-index: 10
|
||||
|
||||
input, select
|
||||
font-size: inherit
|
||||
margin:
|
||||
bottom: 0
|
||||
top: 0
|
||||
|
||||
select
|
||||
border-color: $background-color
|
||||
|
||||
input[type=submit]
|
||||
+context-button
|
||||
font-size: inherit
|
||||
visibility: hidden
|
||||
|
||||
&:active
|
||||
top: 1px
|
||||
|
||||
.visibility-descriptions
|
||||
+opacity(.75)
|
||||
background: $background-color
|
||||
font-style: italic
|
||||
list-style: none
|
||||
padding: 0 .5em
|
||||
|
||||
li
|
||||
display: none
|
||||
|
||||
&:hover
|
||||
.visibility-descriptions li.current
|
||||
display: block
|
||||
|
||||
header
|
||||
display: block
|
||||
position: relative
|
||||
|
||||
h4
|
||||
+header-text
|
||||
font-size: 150%
|
||||
line-height: 1
|
||||
margin: 0 auto .67em
|
||||
width: 50%
|
||||
|
||||
.empty-list
|
||||
display: none
|
||||
font-style: italic
|
||||
|
||||
.closet-list-controls
|
||||
display: none
|
||||
position: absolute
|
||||
right: 1em
|
||||
top: 0
|
||||
|
||||
a, input[type=submit]
|
||||
+context-button
|
||||
|
||||
form
|
||||
display: inline
|
||||
|
||||
&[data-hangers-count="0"]
|
||||
.empty-list
|
||||
display: block
|
||||
|
||||
&.unlisted
|
||||
h4
|
||||
font:
|
||||
size: 125%
|
||||
style: italic
|
||||
|
||||
&:hover
|
||||
.closet-list-controls
|
||||
display: block
|
||||
|
||||
.visibility-form
|
||||
input[type=submit]
|
||||
visibility: visible
|
||||
|
||||
select
|
||||
border-color: $soft-border-color
|
||||
|
||||
&:last-child
|
||||
border-bottom: 0
|
||||
|
||||
&.droppable-active
|
||||
+border-radius(1em)
|
||||
+module
|
||||
border-bottom-width: 1px
|
||||
border-style: dotted
|
||||
margin: 1em 0
|
||||
|
||||
.object
|
||||
// totally hiding these elements causes the original element to change
|
||||
// position, throwing off the drag
|
||||
+opacity(.25)
|
||||
&.ui-draggable-dragging
|
||||
+opacity(1)
|
||||
|
||||
.closet-list-controls
|
||||
display: none
|
||||
|
||||
.closet-list-hangers
|
||||
overflow: hidden
|
||||
|
||||
.visibility-form
|
||||
display: none
|
||||
|
||||
.closet-hangers-group-autocomplete-item, .closet-list-autocomplete-item
|
||||
span
|
||||
font-style: italic
|
||||
padding: .2em .4em
|
||||
|
||||
.closet-list-autocomplete-item
|
||||
a, span
|
||||
font-size: 85%
|
||||
padding-left: 2em
|
||||
|
||||
&.current-user
|
||||
#closet-hangers
|
||||
.object:hover
|
||||
form
|
||||
display: inline
|
||||
|
||||
.closet-hanger-destroy
|
||||
position: absolute
|
||||
right: ($object-width - $object-img-size) / 2 + $object-padding
|
||||
top: $object-img-size - 28px
|
||||
|
||||
input
|
||||
+context-button
|
||||
|
||||
.quantity
|
||||
+opacity(1)
|
||||
background: transparent
|
||||
top: 0
|
||||
padding: 0
|
||||
|
||||
span
|
||||
display: none
|
||||
|
||||
input[type=number]
|
||||
padding: 2px
|
||||
width: 2em
|
||||
|
||||
input[type=submit]
|
||||
font-size: 85%
|
||||
|
||||
&.js
|
||||
#closet-hangers
|
||||
.object:hover .quantity
|
||||
display: block
|
||||
|
||||
input[type=number]
|
||||
width: 2.5em
|
||||
|
||||
input[type=submit]
|
||||
display: none
|
||||
|
||||
.object.loading
|
||||
background: $module-bg-color
|
||||
outline: 1px solid $module-border-color
|
||||
|
||||
.quantity
|
||||
display: block
|
||||
|
||||
span:after
|
||||
content: "…"
|
||||
|
||||
#closet-hangers-contact
|
||||
form
|
||||
display: none
|
||||
|
||||
.edit-contact-link, #cancel-contact-link
|
||||
display: inline
|
||||
|
||||
&.editing
|
||||
form
|
||||
display: block
|
||||
|
||||
.edit-contact-link
|
||||
display: none
|
||||
|
||||
.closet-hangers-group
|
||||
header
|
||||
.show, .hide
|
||||
cursor: pointer
|
||||
|
||||
.hide
|
||||
display: block
|
||||
|
||||
&.hidden
|
||||
header .hide, .closet-hangers-group-content
|
||||
display: none
|
||||
|
||||
header .show
|
||||
display: block
|
||||
|
||||
#toggle-help
|
||||
display: inline
|
||||
|
12
app/stylesheets/closet_hangers/_petpage.sass
Normal file
|
@ -0,0 +1,12 @@
|
|||
body.closet_hangers-petpage
|
||||
+secondary-nav
|
||||
|
||||
#intro
|
||||
clear: both
|
||||
|
||||
#petpage-output
|
||||
display: block
|
||||
height: 30em
|
||||
margin: 0 auto
|
||||
width: 50%
|
||||
|
30
app/stylesheets/closet_lists/_form.sass
Normal file
|
@ -0,0 +1,30 @@
|
|||
body.closet_lists-new, body.closet_lists-create, body.closet_lists-edit, body.closet_lists-update
|
||||
+secondary-nav
|
||||
|
||||
form ul.fields
|
||||
clear: both
|
||||
list-style: none
|
||||
|
||||
label
|
||||
float: left
|
||||
font-weight: bold
|
||||
margin-right: 1em
|
||||
|
||||
li
|
||||
padding: 0.75em 0
|
||||
width: 35em
|
||||
|
||||
input, textarea, select
|
||||
clear: both
|
||||
display: block
|
||||
margin-top: .25em
|
||||
width: 80%
|
||||
|
||||
textarea
|
||||
height: 12em
|
||||
|
||||
.hint
|
||||
display: block
|
||||
font:
|
||||
size: 85%
|
||||
|
55
app/stylesheets/closet_pages/_new.sass
Normal file
|
@ -0,0 +1,55 @@
|
|||
body.closet_pages-new, body.closet_pages-create
|
||||
|
||||
#title
|
||||
float: left
|
||||
|
||||
.flash
|
||||
clear: both
|
||||
|
||||
#back-to-items
|
||||
+awesome-button
|
||||
margin:
|
||||
left: 1em
|
||||
top: .75em
|
||||
|
||||
#closet-page-form
|
||||
+clearfix
|
||||
clear: both
|
||||
margin-bottom: 1em
|
||||
|
||||
#closet-page-frame-wrapper
|
||||
float: left
|
||||
margin-right: 2%
|
||||
width: 48%
|
||||
|
||||
#closet-page-frame
|
||||
height: 19em
|
||||
width: 100%
|
||||
|
||||
#closet-page-source
|
||||
float: left
|
||||
width: 50%
|
||||
|
||||
label
|
||||
font-weight: bold
|
||||
|
||||
textarea
|
||||
height: 19em
|
||||
|
||||
|
||||
ol
|
||||
padding-left: 1em
|
||||
|
||||
> li
|
||||
margin-bottom: 1em
|
||||
|
||||
ul
|
||||
font-size: 85%
|
||||
margin:
|
||||
bottom: 1em
|
||||
top: 0
|
||||
padding-left: 1em
|
||||
|
||||
p
|
||||
margin: 0
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
body.items-show
|
||||
header
|
||||
#item-header
|
||||
border-bottom: 1px solid $module-border-color
|
||||
display: block
|
||||
margin-bottom: 1em
|
||||
|
@ -50,8 +50,51 @@ body.items-show
|
|||
font:
|
||||
family: $text-font
|
||||
size: 85%
|
||||
p:first-child
|
||||
margin-bottom: .25em
|
||||
margin-bottom: 1em
|
||||
|
||||
p
|
||||
display: inline
|
||||
|
||||
&:first-child
|
||||
margin-right: 1em
|
||||
#trade-hangers
|
||||
font-size: 85%
|
||||
text-align: left
|
||||
|
||||
p
|
||||
position: relative
|
||||
|
||||
&:first-child
|
||||
margin-bottom: .5em
|
||||
|
||||
&.overflows
|
||||
.toggle
|
||||
display: block
|
||||
|
||||
&.showing-more
|
||||
.toggle
|
||||
.less
|
||||
display: block
|
||||
|
||||
.more
|
||||
display: none
|
||||
|
||||
.toggle
|
||||
background: white
|
||||
bottom: 0
|
||||
cursor: pointer
|
||||
display: none
|
||||
font-family: $main-font
|
||||
padding: 0 1em
|
||||
position: absolute
|
||||
right: 0
|
||||
|
||||
&:hover
|
||||
text-decoration: underline
|
||||
|
||||
.less
|
||||
display: none
|
||||
|
||||
#item-preview-header
|
||||
margin-top: 3em
|
||||
h3, a
|
||||
|
@ -62,3 +105,37 @@ body.items-show
|
|||
.nc-icon
|
||||
height: 16px
|
||||
width: 16px
|
||||
|
||||
#closet-hangers
|
||||
border: 1px solid $module-border-color
|
||||
float: right
|
||||
font-size: 85%
|
||||
margin-left: 1em
|
||||
padding: 1em
|
||||
width: 21em
|
||||
|
||||
label, header
|
||||
display: block
|
||||
font-weight: bold
|
||||
|
||||
header
|
||||
font-size: 125%
|
||||
|
||||
form
|
||||
padding: .5em 0
|
||||
|
||||
select
|
||||
width: 9em
|
||||
|
||||
input[type=number]
|
||||
width: 4em
|
||||
|
||||
&.js
|
||||
#trade-hangers
|
||||
p
|
||||
max-height: 3em
|
||||
overflow: hidden
|
||||
|
||||
&.showing-more
|
||||
max-height: none
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
@import ../shared/jquery.jgrowl
|
||||
@import partials/wardrobe
|
||||
|
||||
@import "partials/context_button"
|
||||
@import "partials/icon"
|
||||
@import star
|
||||
|
||||
|
@ -229,9 +230,10 @@ body.outfits-edit
|
|||
display: none
|
||||
font-size: 85%
|
||||
margin: 0 auto
|
||||
#preview-mode-image-access-denied
|
||||
#preview-mode-note
|
||||
display: block
|
||||
font-size: 75%
|
||||
margin-top: .5em
|
||||
text-align: center
|
||||
text-decoration: none
|
||||
width: 100%
|
||||
|
@ -312,9 +314,14 @@ body.outfits-edit
|
|||
margin: 0 1em 0 0
|
||||
input
|
||||
+inline-block
|
||||
&[type=submit]
|
||||
margin-right: 2em
|
||||
.preview-search-form-your-items
|
||||
display: none
|
||||
font-size: 85%
|
||||
margin-right: 1em
|
||||
#preview-search-form-pagination
|
||||
+inline-block
|
||||
margin-left: 2em
|
||||
a, span
|
||||
margin: 0 .25em
|
||||
.current
|
||||
|
@ -400,10 +407,7 @@ body.outfits-edit
|
|||
li
|
||||
margin-bottom: .25em
|
||||
a
|
||||
+awesome-button
|
||||
+awesome-button-color(#aaaaaa)
|
||||
+opacity(0.9)
|
||||
font-size: 80%
|
||||
+context-button
|
||||
&:hover
|
||||
ul, .object-info
|
||||
display: block
|
||||
|
@ -560,6 +564,8 @@ body.outfits-edit
|
|||
display: block
|
||||
#save-outfit, #save-current-outfit, #save-outfit-copy, #current-outfit-permalink, #shared-outfit-permalink, #share-outfit, #shared-outfit-url
|
||||
display: none
|
||||
.preview-search-form-your-items
|
||||
+inline-block
|
||||
|
||||
&.user-not-signed-in
|
||||
#save-outfit-not-signed-in
|
||||
|
|
|
@ -137,3 +137,12 @@ body.outfits-new
|
|||
#read-more
|
||||
float: right
|
||||
|
||||
#your-items-module
|
||||
h3
|
||||
&:after
|
||||
color: red
|
||||
content: "new!"
|
||||
font-size: 85%
|
||||
font-weight: bold
|
||||
margin-left: .5em
|
||||
|
||||
|
|
6
app/stylesheets/partials/_context_button.sass
Normal file
|
@ -0,0 +1,6 @@
|
|||
=context-button
|
||||
+awesome-button
|
||||
+awesome-button-color(#aaaaaa)
|
||||
+opacity(0.9)
|
||||
font-size: 80%
|
||||
|
12
app/stylesheets/partials/_secondary_nav.sass
Normal file
|
@ -0,0 +1,12 @@
|
|||
=secondary-nav
|
||||
#title
|
||||
float: left
|
||||
margin-right: .5em
|
||||
|
||||
.flash
|
||||
clear: both
|
||||
|
||||
#secondary-nav
|
||||
display: block
|
||||
margin-top: .75em
|
||||
|
|
@ -24,7 +24,8 @@ $text-font: "Droid Serif", Georgia, "Times New Roman", Times, serif
|
|||
|
||||
$object-img-size: 80px
|
||||
$object-width: 100px
|
||||
$object-padding: 6px
|
||||
$object-padding: 8px
|
||||
$nc-icon-size: 16px
|
||||
|
||||
$container-top-padding: 3em
|
||||
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
|
||||
@import partials/jquery.jgrowl
|
||||
|
||||
@import closet_hangers/index
|
||||
@import closet_hangers/petpage
|
||||
@import closet_lists/form
|
||||
@import closet_pages/new
|
||||
@import contributions/index
|
||||
@import items
|
||||
@import items/index
|
||||
|
|
18
app/views/closet_hangers/_closet_hanger.html.haml
Normal file
|
@ -0,0 +1,18 @@
|
|||
- show_controls ||= false # we could do user check here, but may as well do it once
|
||||
.object{'data-item-id' => closet_hanger.item_id, 'data-quantity' => closet_hanger.quantity}
|
||||
= render :partial => 'items/item_link', :locals => {:item => closet_hanger.item}
|
||||
.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|
|
||||
= return_to_field_tag
|
||||
= f.hidden_field :list_id
|
||||
= f.hidden_field :owned
|
||||
= f.number_field :quantity, :min => 0, :required => true, :title => "You own #{pluralize closet_hanger.quantity, closet_hanger.item.name}"
|
||||
= f.submit "Save"
|
||||
- if show_controls
|
||||
= form_tag user_item_closet_hanger_path(current_user, closet_hanger.item), :method => :delete, :class => 'closet-hanger-destroy' do
|
||||
= return_to_field_tag
|
||||
= hidden_field_tag 'closet_hanger[owned]', closet_hanger.owned
|
||||
= submit_tag "Remove"
|
||||
|
99
app/views/closet_hangers/_petpage_content.html.haml
Normal file
|
@ -0,0 +1,99 @@
|
|||
%style{:type => 'text/css'}
|
||||
:plain
|
||||
.dti-item-group, .dti-item-list, .dti-unlisted-items {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dti-item-group-header, .dti-item-group, #dti-item-footer {
|
||||
color: #040;
|
||||
font-family: Helvetica, Arial, Verdana, sans-serif;
|
||||
line-height: 1.5;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
width: 560px;
|
||||
}
|
||||
|
||||
.dti-item-group-header {
|
||||
border-top: 1px solid #060;
|
||||
font-size: 175%;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.dti-item-group-header, .dti-item-list {
|
||||
border-bottom: 1px solid #ADA;
|
||||
}
|
||||
|
||||
.dti-item-group {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.dti-item-list h3 {
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
.dti-items {
|
||||
border-spacing: 10px;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.dti-item-row {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.dti-item {
|
||||
display: table-cell;
|
||||
margin: 0 10px;
|
||||
padding: 8px;
|
||||
text-align: right;
|
||||
width: 84px;
|
||||
}
|
||||
|
||||
.dti-item-thumbnail {
|
||||
height: 80px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.dti-item span {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dti-item-nc {
|
||||
margin-top: -16px;
|
||||
}
|
||||
|
||||
.dti-unlisted-items h3 {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#dti-item-footer {
|
||||
font-size: 85%;
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
- [true, false].each do |owned|
|
||||
- lists = lists_by_owned[owned]
|
||||
- if lists || unlisted_hangers_by_owned[owned]
|
||||
%h2.dti-item-group-header Items #OWNER #{ClosetHanger.verb(:someone, owned)}
|
||||
%ul.dti-item-group
|
||||
- if lists
|
||||
- lists.each do |list|
|
||||
- unless list.hangers.empty?
|
||||
%li.dti-item-list
|
||||
%h3= list.name
|
||||
- if list.description?
|
||||
.dti-item-list-desc
|
||||
= closet_list_description_format list
|
||||
%div.dti-items
|
||||
= render_batched_petpage_hangers(list.hangers.sort { |a,b| a.item.name <=> b.item.name })
|
||||
- if unlisted_hangers_by_owned[owned]
|
||||
%li.dti-unlisted-items
|
||||
- unless lists.blank?
|
||||
%h3 (Not in a list)
|
||||
%div
|
||||
= render_batched_petpage_hangers(unlisted_hangers_by_owned[owned])
|
||||
|
||||
#dti-item-footer
|
||||
I made this list on Dress to Impress. You can, too!
|
||||
|
6
app/views/closet_hangers/_petpage_hanger.html.haml
Normal file
|
@ -0,0 +1,6 @@
|
|||
%div.dti-item
|
||||
= image_tag hanger.item.thumbnail_url, :alt => nil, :class => 'dti-item-thumbnail'
|
||||
- if hanger.item.nc?
|
||||
= image_tag nc_icon_url, :alt => 'NC', :class => 'dti-item-nc', :title => 'This is an NC Mall item'
|
||||
%span= petpage_item_name hanger.item
|
||||
|
119
app/views/closet_hangers/index.html.haml
Normal file
|
@ -0,0 +1,119 @@
|
|||
- unless public_perspective?
|
||||
- title 'Your Items'
|
||||
- add_body_class 'current-user'
|
||||
- secondary_nav do
|
||||
%span#toggle-help Need help?
|
||||
= form_tag items_path, :method => :get, :id => 'closet-hangers-items-search', 'data-current-user-id' => current_user.id do
|
||||
= text_field_tag :q, nil, :placeholder => "Find items to add"
|
||||
= submit_tag 'Search', :name => nil
|
||||
- else
|
||||
- title "#{@user.name}'s Items"
|
||||
|
||||
- canonical_path user_closet_hangers_path(@user)
|
||||
|
||||
- content_for :before_flashes do
|
||||
#closet-hangers-contact
|
||||
- if public_perspective?
|
||||
- if @user.neopets_username?
|
||||
= link_to "Neomail #{@user.neopets_username}", send_neomail_url(@user)
|
||||
- else
|
||||
%span#edit-contact-link-to-replace-form.edit-contact-link{:class => @user.neopets_username? ? 'has-value' : nil}
|
||||
%span#contact-link-no-value
|
||||
Add your Neopets username
|
||||
%span#contact-link-has-value
|
||||
Edit
|
||||
= surround '"' do
|
||||
Neomail
|
||||
%span= @user.neopets_username
|
||||
= form_for @user do |f|
|
||||
= f.label :neopets_username
|
||||
= f.text_field :neopets_username
|
||||
= f.submit "Save"
|
||||
%span#cancel-contact-link cancel
|
||||
|
||||
- unless public_perspective?
|
||||
#closet-hangers-help{:class => closet_hangers_help_class}
|
||||
:markdown
|
||||
**These are your items! You can track what items you want and own, and
|
||||
share [this page](#{request.fullpath}) with the world**. Just look up an
|
||||
item in the search form above to get started.
|
||||
|
||||
**You can also sort your items into lists.**
|
||||
[Building an Up For Trade list is a good place to start][uft]. You can
|
||||
make lists for trade items with different market values, a private list of
|
||||
what you need for that next outfit, or whatever you like. You can also
|
||||
drag-and-drop items in and out of lists. It's pretty fun.
|
||||
|
||||
**Your items also have privacy settings.**
|
||||
Items can be **private**, so only you can see them. They can be **public**,
|
||||
so you can share this page with friends. They can even be **trading**,
|
||||
meaning that we'll mention on the item's [Infinite Closet][ic] page that
|
||||
you own or want that item.
|
||||
|
||||
**We try to make trading easy.** If there's some item you want, you can
|
||||
pull up that item's [Infinite Closet][ic] page to see if anyone is offering
|
||||
it, and see what *that* user wants
|
||||
in exchange. It's all pretty spiffy. Also, if you plan to trade, your should
|
||||
<span class="edit-contact-link">add your Neopets username</span> so that
|
||||
when other users come here they know how to contact you.
|
||||
|
||||
**Have fun!** If you have any [neat ideas][suggestions] or [general praise and
|
||||
bug reports][mail], we love to hear them. And, if you enjoy this feature,
|
||||
[please consider donating to keep Dress to Impress running and improving][donate].
|
||||
Thanks!
|
||||
|
||||
[donate]: #{donate_path}
|
||||
[ic]: #{items_path}
|
||||
[mail]: mailto:#{contact_email}
|
||||
[suggestions]: #{feedback_url}
|
||||
[uft]: #{new_user_closet_list_path(@user, :closet_list => {:hangers_owned => true, :name => 'Up For Trade', :visibility => ClosetVisibility[:trading].id})}
|
||||
|
||||
- unless public_perspective?
|
||||
#closet-hangers-extras
|
||||
#closet-hangers-share
|
||||
%label{:for => 'closet-hangers-share-box'} Public URL:
|
||||
%input#closet-hangers-share-box{:type => 'text', :value => user_closet_hangers_url(@user), :readonly => true}
|
||||
|
||||
= link_to "Import from Neopets closet", new_closet_page_path
|
||||
= link_to "Export to Neopets petpage", petpage_user_closet_hangers_path(@user)
|
||||
|
||||
|
||||
|
||||
#closet-hangers{:class => public_perspective? ? nil : 'current-user'}
|
||||
- [true, false].each do |owned|
|
||||
.closet-hangers-group{'data-owned' => owned.to_s, :id => "closet-hangers-group-#{owned}"}
|
||||
%header
|
||||
%h3
|
||||
Items #{closet_hanger_subject} #{closet_hanger_verb(owned)}
|
||||
%span.toggle.show show
|
||||
%span.toggle.hide hide
|
||||
- unless public_perspective?
|
||||
= 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?
|
||||
- unless has_hangers?(owned)
|
||||
%p #{@user.name} doesn't seem to #{closet_hanger_verb(owned, false)} anything.
|
||||
|
||||
- content_for :stylesheets do
|
||||
= stylesheet_link_tag 'south-street/jquery-ui'
|
||||
|
||||
- content_for :javascripts do
|
||||
= include_javascript_libraries :jquery
|
||||
= javascript_include_tag 'jquery.ui', 'jquery.jgrowl', 'placeholder', 'closet_hangers/index'
|
||||
|
22
app/views/closet_hangers/petpage.html.haml
Normal file
|
@ -0,0 +1,22 @@
|
|||
- title 'Export to petpage'
|
||||
- secondary_nav do
|
||||
= link_to 'Back to Your Items', user_closet_hangers_path(current_user), :class => 'button'
|
||||
|
||||
#intro
|
||||
%p
|
||||
We took your public lists and created a nice, simple HTML file for your
|
||||
Neopet's petpage. By default it's styled as a table, but it doesn't have to
|
||||
be. The HTML is flexible, so, if you're the artsy type, you're free to mess
|
||||
with the styles all you want!
|
||||
|
||||
%p
|
||||
Copy the HTML from the box below, then paste it into
|
||||
= succeed '.' do
|
||||
= link_to "your pet's page", 'http://www.neopets.com/edithomepage.phtml'
|
||||
Then head to the Neoboards to show off! Have fun!
|
||||
|
||||
%textarea#petpage-output
|
||||
= '' + render('petpage_content',
|
||||
:lists_by_owned => @closet_lists_by_owned,
|
||||
:unlisted_hangers_by_owned => @unlisted_closet_hangers_by_owned)
|
||||
|
22
app/views/closet_lists/_closet_list.html.haml
Normal file
|
@ -0,0 +1,22 @@
|
|||
.closet-list{'data-id' => closet_list.id, 'data-hangers-count' => closet_list.hangers.count, :id => "closet-list-#{closet_list.id}"}
|
||||
%header
|
||||
- if show_controls
|
||||
= form_for [closet_list.user, closet_list], :html => {:class => 'visibility-form'} do |f|
|
||||
= f.select :visibility, closet_visibility_choices(:human_name)
|
||||
= f.submit "Save"
|
||||
= closet_visibility_descriptions('items in this list')
|
||||
.closet-list-controls
|
||||
= link_to 'Edit', edit_user_closet_list_path(closet_list.user, closet_list)
|
||||
= form_tag user_closet_list_path(closet_list.user, closet_list), :method => 'delete' do
|
||||
= submit_tag 'Delete', :confirm => closet_list_delete_confirmation(closet_list)
|
||||
%h4= closet_list.name
|
||||
|
||||
.closet-list-content
|
||||
- if closet_list.description?
|
||||
= closet_list_description_format closet_list
|
||||
|
||||
.closet-list-hangers
|
||||
- unless closet_list.hangers.empty?
|
||||
= render_sorted_hangers(closet_list, show_controls)
|
||||
%span.empty-list This list is empty.
|
||||
|
31
app/views/closet_lists/_form.html.haml
Normal file
|
@ -0,0 +1,31 @@
|
|||
- secondary_nav do
|
||||
= link_to 'Back to Your Items', user_closet_hangers_path(current_user), :class => 'button'
|
||||
|
||||
= form_for [@closet_list.user, @closet_list] do |f|
|
||||
%ul.fields
|
||||
%li
|
||||
= f.label :name
|
||||
%span.hint Like "up for trade" or "NC wishlist"
|
||||
= f.text_field :name, :required => true
|
||||
%li
|
||||
= f.label :hangers_owned, 'This is a list for…'.html_safe
|
||||
= f.select :hangers_owned, hangers_owned_options
|
||||
%li
|
||||
= f.label :visibility, 'Who can see this list?'
|
||||
= f.select :visibility, closet_visibility_choices(:description, 'Items in this list')
|
||||
%li
|
||||
= f.label :description
|
||||
%span.hint
|
||||
Why are these items in a list? What are your terms for trading?
|
||||
Or you can leave this blank.
|
||||
= f.text_area :description
|
||||
%span.hint
|
||||
We
|
||||
= surround '_' do
|
||||
%em support
|
||||
= surround '**' do
|
||||
%strong Markdown
|
||||
and
|
||||
some HTML.
|
||||
= f.submit 'Save list'
|
||||
|
3
app/views/closet_lists/edit.html.haml
Normal file
|
@ -0,0 +1,3 @@
|
|||
- title "Editing list \"#{@closet_list.name}\""
|
||||
= render 'form'
|
||||
|
3
app/views/closet_lists/new.html.haml
Normal file
|
@ -0,0 +1,3 @@
|
|||
- title 'Create an items list'
|
||||
= render 'form'
|
||||
|
57
app/views/closet_pages/new.html.haml
Normal file
|
@ -0,0 +1,57 @@
|
|||
- title "Import from closet, Page #{@closet_page.index}"
|
||||
- content_for :before_flashes do
|
||||
= link_to 'Back to Your Items', user_closet_hangers_path(current_user), :id => 'back-to-items'
|
||||
|
||||
= form_for @closet_page, :html => {:id => 'closet-page-form'} do |f|
|
||||
= f.hidden_field :index
|
||||
#closet-page-frame-wrapper
|
||||
%span
|
||||
%strong Page #{@closet_page.index}
|
||||
of your closet
|
||||
%iframe#closet-page-frame{:src => @closet_page.url}
|
||||
#closet-page-source
|
||||
= f.label :source, "Paste source code below"
|
||||
= f.text_area :source
|
||||
= f.submit 'Add items to closet'
|
||||
|
||||
:markdown
|
||||
**Welcome to the bulk closet importer!** We're going to make it as
|
||||
easy as possible to import your Neopets.com closet data into your Dress to
|
||||
Impress closet. Here's how it works.
|
||||
|
||||
1. Check the framed Neopets.com window on the left, pointing to
|
||||
[page #{@closet_page.index} of your closet][cp].
|
||||
* **Confirm that you're logged in.** If you're logged into
|
||||
Neopets, but the above frame says that you're not, try enabling
|
||||
"third-party cookies" in your browser. (Most have that on by default.)
|
||||
* **If you haven't logged in, #{link_to_neopets_login "do so in another window"}</a>.**
|
||||
It's never a good idea to log in inside of a frame, unless you're a
|
||||
web programmer pro who can check that the frame does, in fact, point
|
||||
to Neopets.com. To be safe,
|
||||
#{link_to_neopets_login "pull up another window, check the URL, and log in safely"}.
|
||||
* **Confirm that the page is, in fact, your closet.** Similarly, don't
|
||||
just trust a website when they tell you to copy-paste the source code
|
||||
of another site. Instead, check that the page is what it is supposed to
|
||||
be and does not contain any information you didn't mean to give out.
|
||||
|
||||
2. View the frame's source code.
|
||||
* **In Google Chrome,** right-click the frame and choose **View Frame Source**.
|
||||
* **In Firefox,** right-click the frame, choose **This Frame**, then **View Frame Source**.
|
||||
* In other browsers, right-click, and look for something similar. If you're
|
||||
still having trouble, try
|
||||
#{link_to "viewing the page in a new window", @closet_page.url, :target => "_blank"},
|
||||
right-clicking, and choosing View Source.
|
||||
|
||||
3. Highlight the entire source code, and copy-paste it into the box on the right.
|
||||
* Some nifty shortcuts: Ctrl-A to select all the text, Ctrl-C to copy it,
|
||||
Ctrl-V to paste it in.
|
||||
|
||||
4. Submit!
|
||||
* We'll analyze the code you sent us, grab exclusively the identity and
|
||||
quantity of items in your closet, and add that to your Dress to Impress
|
||||
closet. I promise it's all safe, but, if you're concerned, find a
|
||||
programmer buddy and [check out the source code to be sure][source].
|
||||
|
||||
[cp]: #{@closet_page.url}
|
||||
[source]: http://github.com/matchu/openneo-impress-rails
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
- title 'Recent Contributions'
|
||||
- if @user
|
||||
- canonical_path user_contributions_path(@user)
|
||||
|
||||
%ul.buttons
|
||||
- if @user
|
||||
%li= link_to 'Recent Contributions', contributions_path, :class => 'button'
|
||||
|
@ -15,3 +18,4 @@
|
|||
= will_paginate @contributions
|
||||
%ul.contributions= render @contributions
|
||||
= will_paginate @contributions
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
.object
|
||||
= link_to item_path(item, :q => @query) do
|
||||
= image_tag item.thumbnail_url, :alt => item.description, :title => item.description
|
||||
= item.name
|
||||
= nc_icon_for(item)
|
||||
= render :partial => 'item_link', :locals => {:item => item}
|
||||
|
||||
|
|
6
app/views/items/_item_link.html.haml
Normal file
|
@ -0,0 +1,6 @@
|
|||
= link_to item_path(item, :q => @query) do
|
||||
= image_tag item.thumbnail_url, :alt => item.description, :title => item.description
|
||||
%span.name= item.name
|
||||
= nc_icon_for(item)
|
||||
= closeted_icons_for(item)
|
||||
|
|
@ -20,6 +20,11 @@
|
|||
%dd
|
||||
returns any item with the word "kreludor" and the phrase "altador cup"
|
||||
in it, but not the word "background"
|
||||
%dt hat user:owns
|
||||
%dd
|
||||
returns
|
||||
= link_to 'items that you own', your_items_path
|
||||
with the word "hat"
|
||||
%dt blue is:nc
|
||||
%dd returns any NC Mall item with the word "blue" in it
|
||||
%dt collar -is:pb
|
||||
|
|
|
@ -1,17 +1,35 @@
|
|||
- title @item.name
|
||||
- content_for :meta do
|
||||
%link{:rel => 'canonical', :href => url_for(@item)}
|
||||
- canonical_path @item
|
||||
|
||||
- cache "items_show_#{@item.id}_main_content" do
|
||||
%header
|
||||
%header#item-header
|
||||
= image_tag @item.thumbnail_url, :id => 'item-thumbnail'
|
||||
%div
|
||||
%h2#item-name= @item.name
|
||||
= nc_icon_for(@item)
|
||||
- unless @item.rarity.blank?
|
||||
== Rarity: #{@item.rarity_index} (#{@item.rarity})
|
||||
%a.button{:href => neoitems_url_for(@item)} NeoItems
|
||||
= link_to 'NeoItems', neoitems_url_for(@item), :class => 'button'
|
||||
|
||||
- if @current_user_hangers
|
||||
#closet-hangers
|
||||
%header
|
||||
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"
|
||||
%p= @item.description
|
||||
|
||||
#item-zones
|
||||
|
@ -25,6 +43,37 @@
|
|||
- else
|
||||
= list_zones @item.restricted_zones
|
||||
|
||||
#trade-hangers
|
||||
- [true, false].each do |owned|
|
||||
%p
|
||||
- unless @trading_closet_hangers_by_owned[owned].empty?
|
||||
%strong
|
||||
= pluralize @trading_closet_hangers_by_owned[owned].size, 'user'
|
||||
- if owned
|
||||
- if @trading_closet_hangers_by_owned[owned].size == 1
|
||||
has
|
||||
- else
|
||||
have
|
||||
this item up for trade:
|
||||
- else
|
||||
- if @trading_closet_hangers_by_owned[owned].size == 1
|
||||
wants
|
||||
- else
|
||||
want
|
||||
this item:
|
||||
= render_trading_closet_hangers(owned)
|
||||
- else
|
||||
%strong
|
||||
We don't know anyone who
|
||||
- if owned
|
||||
has this item up for trade.
|
||||
- else
|
||||
wants this item.
|
||||
%span.toggle
|
||||
%span.more more
|
||||
%span.less less
|
||||
|
||||
|
||||
#item-preview-header
|
||||
%h3 Preview
|
||||
%a#customize-more.button{:href => '/'} Customize more
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
Dress to Impress: Preview customized Neopets' clothing and wearables
|
||||
/[if IE]
|
||||
= include_javascript_libraries :html5
|
||||
= yield :stylesheets
|
||||
= stylesheet_link_tag "compiled/screen"
|
||||
= yield :meta
|
||||
= csrf_meta_tag
|
||||
|
@ -36,15 +37,12 @@
|
|||
|
||||
#userbar
|
||||
- if user_signed_in?
|
||||
- if can_use_image_mode?
|
||||
= link_to image_mode_path, :id => 'userbar-image-mode' do
|
||||
= image_tag 'image_mode_icon.png', :alt => 'Image Mode'
|
||||
Welcome to Image Mode!
|
||||
%span
|
||||
Hey,
|
||||
= succeed '!' do
|
||||
= link_to current_user.name, user_contributions_path(current_user)
|
||||
== You have #{current_user.points} points.
|
||||
Hey, #{current_user.name}!
|
||||
You have
|
||||
= succeed '.' do
|
||||
= link_to "#{current_user.points} points", user_contributions_path(current_user)
|
||||
= link_to 'Items', user_closet_hangers_path(current_user), :id => 'userbar-items-link'
|
||||
= link_to 'Outfits', current_user_outfits_path
|
||||
= link_to 'Settings', Openneo::Auth.remote_settings_url
|
||||
= link_to 'Log out', logout_path_with_return_to
|
||||
|
@ -69,9 +67,9 @@
|
|||
Contact:
|
||||
%ul
|
||||
%li
|
||||
%a{:href => "http://openneo.uservoice.com/forums/40720-dress-to-impress"} Suggestions
|
||||
%a{:href => feedback_url} Suggestions
|
||||
%li
|
||||
%a{:href => "mailto:webmaster@openneo.net"} Questions, comments, bugs
|
||||
%a{:href => "mailto:#{contact_email}"} Questions, comments, bugs
|
||||
%p
|
||||
Images © 2000-2010 Neopets, Inc. All Rights Reserved.
|
||||
Used With Permission
|
||||
|
|
|
@ -41,14 +41,16 @@
|
|||
%li#preview-mode-flash.active Flash
|
||||
- if can_use_image_mode?
|
||||
%li#preview-mode-image Image
|
||||
- unless can_use_image_mode?
|
||||
= link_to(donate_path, :id => 'preview-mode-image-access-denied', :target => '_blank') do
|
||||
- if can_use_image_mode?
|
||||
%button#preview-download-image Download
|
||||
= link_to 'Image mode FAQ', image_mode_path,
|
||||
:id => 'preview-mode-note', :target => '_blank'
|
||||
- else
|
||||
= link_to(donate_path, :id => 'preview-mode-note', :target => '_blank') do
|
||||
%strong Image mode
|
||||
is available for early beta testing to users who
|
||||
%em donate
|
||||
at least $5 to help upgrade the server. Thanks!
|
||||
- if can_use_image_mode?
|
||||
%button#preview-download-image Download
|
||||
#preview-sidebar
|
||||
- unless can_use_image_mode?
|
||||
#preview-sidebar-donation-request
|
||||
|
@ -91,6 +93,8 @@
|
|||
%h2 Add an item
|
||||
%input{:name => "query", :placeholder => "Search items...", :type => "search"}/
|
||||
%input{:type => "submit", :value => "Go"}/
|
||||
%a.preview-search-form-your-items{:href => '#', 'data-search-value' => 'owns'} Items you own
|
||||
%a.preview-search-form-your-items{:href => '#', 'data-search-value' => 'wants'} Items you want
|
||||
#preview-search-form-pagination
|
||||
%a#preview-search-form-clear{:href => "#"} clear
|
||||
%dl#preview-search-form-help
|
||||
|
|
|
@ -28,16 +28,16 @@
|
|||
|
||||
- cache :action_suffix => 'sections_and_description' do
|
||||
%ul#sections
|
||||
%li
|
||||
%a{:href => "http://forum.openneo.net"}
|
||||
= image_tag 'forum.png'
|
||||
%li#your-items-module
|
||||
= link_to image_tag('your_items.png'), your_items_path
|
||||
%h3
|
||||
%a{:href => "http://forum.openneo.net/"} Forum
|
||||
= link_to 'Your Items', your_items_path
|
||||
%div
|
||||
%h4 Join our community!
|
||||
%h4 Track and trade!
|
||||
%p
|
||||
Show off your designs, ask for advice, or play silly forum games
|
||||
here.
|
||||
Make lists of the items you own and want, and share them with the
|
||||
world.
|
||||
|
||||
%li
|
||||
%a{:href => items_path}
|
||||
= image_tag 'items.png'
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
OpenneoImpressItems::Application.routes.draw do |map|
|
||||
get "petpages/new"
|
||||
|
||||
get "closet_lists/new"
|
||||
|
||||
get "closet_lists/create"
|
||||
|
||||
root :to => 'outfits#new'
|
||||
|
||||
devise_for :users
|
||||
|
@ -24,6 +30,8 @@ OpenneoImpressItems::Application.routes.draw do |map|
|
|||
resources :pet_attributes, :only => [:index]
|
||||
resources :swf_assets, :only => [:index, :show]
|
||||
|
||||
resources :closet_pages, :only => [:new, :create], :path => 'closet/pages'
|
||||
|
||||
match '/users/current-user/outfits' => 'outfits#index', :as => :current_user_outfits
|
||||
|
||||
match '/pets/load' => 'pets#load', :method => :post, :as => :load_pet
|
||||
|
@ -33,9 +41,22 @@ OpenneoImpressItems::Application.routes.draw do |map|
|
|||
match '/logout' => 'sessions#destroy', :as => :logout
|
||||
match '/users/authorize' => 'sessions#create'
|
||||
|
||||
resources :user, :only => [] do
|
||||
resources :users, :path => 'user', :only => [:update] do
|
||||
resources :contributions, :only => [:index]
|
||||
resources :closet_hangers, :only => [:index], :path => 'closet' do
|
||||
collection do
|
||||
get :petpage
|
||||
end
|
||||
end
|
||||
resources :closet_lists, :only => [:new, :create, :edit, :update, :destroy], :path => 'closet/lists'
|
||||
|
||||
resources :items, :only => [] do
|
||||
resource :closet_hanger, :only => [:create, :update, :destroy]
|
||||
end
|
||||
end
|
||||
|
||||
match 'users/current-user/closet' => 'closet_hangers#index', :as => :your_items
|
||||
|
||||
match 'users/top-contributors' => 'users#top_contributors', :as => :top_contributors
|
||||
match 'users/top_contributors' => redirect('/users/top-contributors')
|
||||
|
||||
|
|
15
db/migrate/20110712232259_create_closet_hangers.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
class CreateClosetHangers < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :closet_hangers do |t|
|
||||
t.integer :item_id
|
||||
t.integer :user_id
|
||||
t.integer :quantity
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :closet_hangers
|
||||
end
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
class SetClosetHangersQuantityDefaultToZero < ActiveRecord::Migration
|
||||
def self.up
|
||||
end
|
||||
|
||||
def self.down
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
class AddNeopetsUsernameToUsers < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :users, :neopets_username, :string
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :users, :neopets_username
|
||||
end
|
||||
end
|
10
db/migrate/20110722180616_add_owned_to_closet_hangers.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
class AddOwnedToClosetHangers < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :closet_hangers, :owned, :boolean, :null => false, :default => true
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :closet_hangers, :owned
|
||||
end
|
||||
end
|
||||
|
21
db/migrate/20110726231143_create_closet_lists.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
class CreateClosetLists < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :closet_lists do |t|
|
||||
t.string :name
|
||||
t.text :description
|
||||
t.integer :user_id
|
||||
t.boolean :hangers_owned, :null => false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_column :closet_hangers, :list_id, :integer
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :closet_lists
|
||||
|
||||
remove_column :closet_hangers, :list_id
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
class AddClosetHangersVisibilityToUsers < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :users, :owned_closet_hangers_visibility, :integer, :null => false, :default => 1
|
||||
add_column :users, :wanted_closet_hangers_visibility, :integer, :null => false, :default => 1
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :users, :wanted_closet_hangers_visibility
|
||||
remove_column :users, :owned_closet_hangers_visibility
|
||||
end
|
||||
end
|
||||
|
10
db/migrate/20110731021808_add_visibility_to_closet_lists.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
class AddVisibilityToClosetLists < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :closet_lists, :visibility, :integer, :null => false, :default => 1
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :closet_lists, :visibility
|
||||
end
|
||||
end
|
||||
|
26
db/schema.rb
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20110626202605) do
|
||||
ActiveRecord::Schema.define(:version => 20110731021808) do
|
||||
|
||||
create_table "auth_servers", :force => true do |t|
|
||||
t.string "short_name", :limit => 10, :null => false
|
||||
|
@ -20,6 +20,26 @@ ActiveRecord::Schema.define(:version => 20110626202605) do
|
|||
t.string "secret", :limit => 64, :null => false
|
||||
end
|
||||
|
||||
create_table "closet_hangers", :force => true do |t|
|
||||
t.integer "item_id"
|
||||
t.integer "user_id"
|
||||
t.integer "quantity"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.boolean "owned", :default => true, :null => false
|
||||
t.integer "list_id"
|
||||
end
|
||||
|
||||
create_table "closet_lists", :force => true do |t|
|
||||
t.string "name"
|
||||
t.text "description"
|
||||
t.integer "user_id"
|
||||
t.boolean "hangers_owned", :null => false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.integer "visibility", :default => 1, :null => false
|
||||
end
|
||||
|
||||
create_table "contributions", :force => true do |t|
|
||||
t.string "contributed_type", :limit => 8, :null => false
|
||||
t.integer "contributed_id", :null => false
|
||||
|
@ -169,6 +189,10 @@ ActiveRecord::Schema.define(:version => 20110626202605) do
|
|||
t.boolean "forum_admin", :default => false, :null => false
|
||||
t.boolean "forum_moderator"
|
||||
t.boolean "image_mode_tester", :default => false, :null => false
|
||||
t.text "closet_description", :null => false
|
||||
t.string "neopets_username"
|
||||
t.integer "owned_closet_hangers_visibility", :default => 1, :null => false
|
||||
t.integer "wanted_closet_hangers_visibility", :default => 1, :null => false
|
||||
end
|
||||
|
||||
create_table "zones", :force => true do |t|
|
||||
|
|
28
public/403.html
Normal file
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>You do not have permission to access this page (403)</title>
|
||||
<style type="text/css">
|
||||
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
|
||||
div.dialog {
|
||||
width: 25em;
|
||||
padding: 0 4em;
|
||||
margin: 4em auto 0 auto;
|
||||
border: 1px solid #ccc;
|
||||
border-right-color: #999;
|
||||
border-bottom-color: #999;
|
||||
}
|
||||
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- This file lives in public/403.html -->
|
||||
<div class="dialog">
|
||||
<h1>You do not have permission to access this page.</h1>
|
||||
<p>This resource might belong to another user, or your session may have expired.</p>
|
||||
<p><a href="/login">Try logging in again.</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 738 B After Width: | Height: | Size: 732 B |
BIN
public/images/neomail.png
Normal file
After Width: | Height: | Size: 754 B |
BIN
public/images/neomail_edit.png
Normal file
After Width: | Height: | Size: 756 B |
BIN
public/images/owned.png
Normal file
After Width: | Height: | Size: 537 B |
BIN
public/images/wanted.png
Normal file
After Width: | Height: | Size: 671 B |
BIN
public/images/your_items.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
510
public/javascripts/closet_hangers/index.js
Normal file
|
@ -0,0 +1,510 @@
|
|||
(function () {
|
||||
var hangersInitCallbacks = [];
|
||||
|
||||
function onHangersInit(callback) {
|
||||
hangersInitCallbacks[hangersInitCallbacks.length] = callback;
|
||||
}
|
||||
|
||||
function hangersInit() {
|
||||
for(var i = 0; i < hangersInitCallbacks.length; i++) {
|
||||
hangersInitCallbacks[i]();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Hanger groups
|
||||
|
||||
*/
|
||||
|
||||
var hangerGroups = [];
|
||||
|
||||
$('div.closet-hangers-group').each(function () {
|
||||
var el = $(this);
|
||||
var lists = [];
|
||||
|
||||
el.find('div.closet-list').each(function () {
|
||||
var el = $(this);
|
||||
var id = el.attr('data-id');
|
||||
if(id) {
|
||||
lists[lists.length] = {
|
||||
id: parseInt(id, 10),
|
||||
label: el.find('h4').text()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
hangerGroups[hangerGroups.length] = {
|
||||
label: el.find('h3').text(),
|
||||
lists: lists,
|
||||
owned: (el.attr('data-owned') == 'true')
|
||||
};
|
||||
});
|
||||
|
||||
$('div.closet-hangers-group span.toggle').live('click', function () {
|
||||
$(this).closest('.closet-hangers-group').toggleClass('hidden');
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
Hanger forms
|
||||
|
||||
*/
|
||||
|
||||
$.fn.liveDraggable = function (opts) {
|
||||
this.live("mouseover", function() {
|
||||
if (!$(this).data("init")) {
|
||||
$(this).data("init", true).draggable(opts);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var body = $(document.body).addClass("js");
|
||||
if(!body.hasClass("current-user")) return false;
|
||||
|
||||
var hangersElQuery = '#closet-hangers';
|
||||
var hangersEl = $(hangersElQuery);
|
||||
|
||||
$.fn.disableForms = function () {
|
||||
return this.data("formsDisabled", true).find("input").attr("disabled", "disabled").end();
|
||||
}
|
||||
|
||||
$.fn.enableForms = function () {
|
||||
return this.data("formsDisabled", false).find("input").removeAttr("disabled").end();
|
||||
}
|
||||
|
||||
$.fn.hasChanged = function () {
|
||||
return this.attr('data-previous-value') != this.val();
|
||||
}
|
||||
|
||||
$.fn.revertValue = function () {
|
||||
return this.each(function () {
|
||||
var el = $(this);
|
||||
el.val(el.attr('data-previous-value'));
|
||||
});
|
||||
}
|
||||
|
||||
$.fn.storeValue = function () {
|
||||
return this.each(function () {
|
||||
var el = $(this);
|
||||
el.attr('data-previous-value', el.val());
|
||||
});
|
||||
}
|
||||
|
||||
$.fn.insertIntoSortedList = function (list, compare) {
|
||||
var newChild = this, inserted = false;
|
||||
list.children().each(function () {
|
||||
if(compare(newChild, $(this)) < 1) {
|
||||
newChild.insertBefore(this);
|
||||
inserted = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if(!inserted) newChild.appendTo(list);
|
||||
return this;
|
||||
}
|
||||
|
||||
function handleSaveError(xhr, action) {
|
||||
try {
|
||||
var data = $.parseJSON(xhr.responseText);
|
||||
} catch(e) {
|
||||
var data = {};
|
||||
}
|
||||
|
||||
if(typeof data.errors != 'undefined') {
|
||||
$.jGrowl("Error " + action + ": " + data.errors.join(", "));
|
||||
} else {
|
||||
$.jGrowl("We had trouble " + action + " just now. Try again?");
|
||||
}
|
||||
}
|
||||
|
||||
function objectRemoved(objectWrapper) {
|
||||
objectWrapper.hide(250, $.proxy(objectWrapper, 'remove'));
|
||||
}
|
||||
|
||||
function compareItemsByName(a, b) {
|
||||
return a.find('span.name').text().localeCompare(b.find('span.name').text());
|
||||
}
|
||||
|
||||
function findList(id, item) {
|
||||
if(id) {
|
||||
return $('#closet-list-' + id);
|
||||
} else {
|
||||
return item.closest('.closet-hangers-group').find('div.closet-list.unlisted');
|
||||
}
|
||||
}
|
||||
|
||||
function updateListHangersCount(el) {
|
||||
el.attr('data-hangers-count', el.find('div.object').length);
|
||||
}
|
||||
|
||||
function moveItemToList(item, listId) {
|
||||
var newList = findList(listId, item);
|
||||
var oldList = item.closest('div.closet-list');
|
||||
var hangersWrapper = newList.find('div.closet-list-hangers');
|
||||
item.insertIntoSortedList(hangersWrapper, compareItemsByName);
|
||||
updateListHangersCount(oldList);
|
||||
updateListHangersCount(newList);
|
||||
}
|
||||
|
||||
function submitUpdateForm(form) {
|
||||
if(form.data('loading')) return false;
|
||||
var quantityEl = form.children("input[name=closet_hanger\[quantity\]]");
|
||||
var listEl = form.children("input[name=closet_hanger\[list_id\]]");
|
||||
var listChanged = listEl.hasChanged();
|
||||
if(listChanged || quantityEl.hasChanged()) {
|
||||
var objectWrapper = form.closest(".object").addClass("loading");
|
||||
var newQuantity = quantityEl.val();
|
||||
var quantitySpan = objectWrapper.find(".quantity span").text(newQuantity);
|
||||
objectWrapper.attr('data-quantity', newQuantity);
|
||||
var data = form.serialize(); // get data before disabling inputs
|
||||
objectWrapper.disableForms();
|
||||
form.data('loading', true);
|
||||
if(listChanged) moveItemToList(objectWrapper, listEl.val());
|
||||
$.ajax({
|
||||
url: form.attr("action") + ".json",
|
||||
type: "post",
|
||||
data: data,
|
||||
dataType: "json",
|
||||
complete: function (data) {
|
||||
if(quantityEl.val() == 0) {
|
||||
objectRemoved(objectWrapper);
|
||||
} else {
|
||||
objectWrapper.removeClass("loading").enableForms();
|
||||
}
|
||||
form.data('loading', false);
|
||||
},
|
||||
success: function () {
|
||||
quantityEl.storeValue();
|
||||
listEl.storeValue();
|
||||
},
|
||||
error: function (xhr) {
|
||||
quantityEl.revertValue();
|
||||
listEl.revertValue();
|
||||
if(listChanged) moveItemToList(objectWrapper, listEl.val());
|
||||
quantitySpan.text(quantityEl.val());
|
||||
|
||||
handleSaveError(xhr, "updating the quantity");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$(hangersElQuery + ' form.closet-hanger-update').live('submit', function (e) {
|
||||
e.preventDefault();
|
||||
submitUpdateForm($(this));
|
||||
});
|
||||
|
||||
function editableInputs() { return $(hangersElQuery).find('input[name=closet_hanger\[quantity\]], input[name=closet_hanger\[list_id\]]') }
|
||||
|
||||
$(hangersElQuery + 'input[name=closet_hanger\[quantity\]]').live('change', function () {
|
||||
submitUpdateForm($(this).parent());
|
||||
}).storeValue();
|
||||
|
||||
onHangersInit(function () {
|
||||
editableInputs().storeValue();
|
||||
});
|
||||
|
||||
$(hangersElQuery + ' div.object').live('mouseleave', function () {
|
||||
submitUpdateForm($(this).find('form.closet-hanger-update'));
|
||||
}).liveDraggable({
|
||||
appendTo: '#closet-hangers',
|
||||
distance: 20,
|
||||
helper: "clone",
|
||||
revert: "invalid"
|
||||
});
|
||||
|
||||
$(hangersElQuery + " form.closet-hanger-destroy").live("submit", function (e) {
|
||||
e.preventDefault();
|
||||
var form = $(this);
|
||||
var button = form.children("input[type=submit]").val("Removing…");
|
||||
var objectWrapper = form.closest(".object").addClass("loading");
|
||||
var data = form.serialize(); // get data before disabling inputs
|
||||
objectWrapper.addClass("loading").disableForms();
|
||||
$.ajax({
|
||||
url: form.attr("action") + ".json",
|
||||
type: "post",
|
||||
data: data,
|
||||
dataType: "json",
|
||||
complete: function () {
|
||||
button.val("Remove");
|
||||
},
|
||||
success: function () {
|
||||
objectRemoved(objectWrapper);
|
||||
},
|
||||
error: function () {
|
||||
objectWrapper.removeClass("loading").enableForms();
|
||||
$.jGrowl("Error removing item. Try again?");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
Search, autocomplete
|
||||
|
||||
*/
|
||||
|
||||
$('input, textarea').placeholder();
|
||||
|
||||
var itemsSearchForm = $("#closet-hangers-items-search[data-current-user-id]");
|
||||
var itemsSearchField = itemsSearchForm.children("input[name=q]");
|
||||
|
||||
itemsSearchField.autocomplete({
|
||||
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);
|
||||
} else {
|
||||
var item = ui.item.item;
|
||||
var group = ui.item.group;
|
||||
|
||||
itemsSearchField.addClass("loading");
|
||||
|
||||
var closetHanger = {
|
||||
owned: group.owned,
|
||||
list_id: ui.item.list ? ui.item.list.id : ''
|
||||
};
|
||||
|
||||
if(!item.hangerInGroup) closetHanger.quantity = 1;
|
||||
|
||||
$.ajax({
|
||||
url: "/user/" + itemsSearchForm.data("current-user-id") + "/items/" + item.id + "/closet_hanger",
|
||||
type: "post",
|
||||
data: {closet_hanger: closetHanger, return_to: window.location.pathname + window.location.search},
|
||||
complete: function () {
|
||||
itemsSearchField.removeClass("loading");
|
||||
},
|
||||
success: function (html) {
|
||||
var doc = $(html);
|
||||
hangersEl.html( doc.find('#closet-hangers').html() );
|
||||
hangersInit();
|
||||
doc.find('.flash').hide().insertBefore(hangersEl).show(500).delay(5000).hide(250);
|
||||
itemsSearchField.val("");
|
||||
},
|
||||
error: function (xhr) {
|
||||
handleSaveError(xhr, "adding the item");
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
source: function (input, callback) {
|
||||
if(typeof input.term == 'string') { // user-typed query
|
||||
$.getJSON("/items.json?q=" + input.term, function (data) {
|
||||
var output = [];
|
||||
var items = data.items;
|
||||
for(var i in items) {
|
||||
items[i].label = items[i].name;
|
||||
items[i].is_item = true;
|
||||
output[output.length] = items[i];
|
||||
}
|
||||
callback(output);
|
||||
});
|
||||
} else { // item was chosen, now choose a group to insert
|
||||
var groupInserts = [], group;
|
||||
var item = input.term, itemEl, hangerInGroup, currentListId;
|
||||
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');
|
||||
|
||||
groupInserts[groupInserts.length] = {
|
||||
group: group,
|
||||
item: item,
|
||||
label: item.label,
|
||||
hangerInGroup: hangerInGroup,
|
||||
hangerInList: !!currentListId
|
||||
}
|
||||
|
||||
for(var i = 0; i < group.lists.length; i++) {
|
||||
groupInserts[groupInserts.length] = {
|
||||
group: group,
|
||||
item: item,
|
||||
label: item.label,
|
||||
list: group.lists[i],
|
||||
hangerInGroup: hangerInGroup,
|
||||
currentListId: currentListId
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(groupInserts);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var autocompleter = itemsSearchField.data("autocomplete");
|
||||
|
||||
autocompleter._renderItem = function( ul, item ) {
|
||||
var li = $("<li></li>").data("item.autocomplete", item);
|
||||
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>");
|
||||
}
|
||||
} 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");
|
||||
}
|
||||
} else {
|
||||
li.append("<a>Add to <strong>" + item.group.label + "</strong>");
|
||||
}
|
||||
li.addClass('closet-hangers-group-autocomplete-item');
|
||||
}
|
||||
return li.appendTo(ul);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Contact Neopets username form
|
||||
|
||||
*/
|
||||
|
||||
var contactEl = $('#closet-hangers-contact');
|
||||
var editContactLink = $('.edit-contact-link');
|
||||
var contactForm = contactEl.children('form');
|
||||
var cancelContactLink = $('#cancel-contact-link');
|
||||
var contactFormUsername = contactForm.children('input[type=text]');
|
||||
var editContactLinkUsername = $('#contact-link-has-value span');
|
||||
|
||||
function closeContactForm() {
|
||||
contactEl.removeClass('editing');
|
||||
}
|
||||
|
||||
editContactLink.click(function () {
|
||||
contactEl.addClass('editing');
|
||||
contactFormUsername.focus();
|
||||
});
|
||||
|
||||
cancelContactLink.click(closeContactForm);
|
||||
|
||||
contactForm.submit(function (e) {
|
||||
var data = contactForm.serialize();
|
||||
contactForm.disableForms();
|
||||
$.ajax({
|
||||
url: contactForm.attr('action') + '.json',
|
||||
type: 'post',
|
||||
data: data,
|
||||
dataType: 'json',
|
||||
complete: function () {
|
||||
contactForm.enableForms();
|
||||
},
|
||||
success: function () {
|
||||
var newName = contactFormUsername.val();
|
||||
if(newName.length > 0) {
|
||||
editContactLink.addClass('has-value');
|
||||
editContactLinkUsername.text(newName);
|
||||
} else {
|
||||
editContactLink.removeClass('has-value');
|
||||
}
|
||||
closeContactForm();
|
||||
},
|
||||
error: function (xhr) {
|
||||
handleSaveError(xhr, 'saving Neopets username');
|
||||
}
|
||||
});
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
Hanger list controls
|
||||
|
||||
*/
|
||||
|
||||
$('input[type=submit][data-confirm]').live('click', function (e) {
|
||||
if(!confirm(this.getAttribute('data-confirm'))) e.preventDefault();
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
Closet list droppable
|
||||
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
Visibility Descriptions
|
||||
|
||||
*/
|
||||
|
||||
function updateVisibilityDescription() {
|
||||
var descriptions = $(this).closest('.visibility-form').
|
||||
find('ul.visibility-descriptions');
|
||||
|
||||
descriptions.children('li.current').removeClass('current');
|
||||
descriptions.children('li[data-id=' + $(this).val() + ']').addClass('current');
|
||||
}
|
||||
|
||||
function visibilitySelects() { return $('form.visibility-form select') }
|
||||
|
||||
visibilitySelects().live('change', updateVisibilityDescription);
|
||||
|
||||
onHangersInit(function () {
|
||||
visibilitySelects().each(updateVisibilityDescription);
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
Help
|
||||
|
||||
*/
|
||||
|
||||
$('#toggle-help').click(function () {
|
||||
$('#closet-hangers-help').toggleClass('hidden');
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
Share URL
|
||||
|
||||
*/
|
||||
|
||||
$('#closet-hangers-share-box').mouseover(function () {
|
||||
$(this).focus();
|
||||
}).mouseout(function () {
|
||||
$(this).blur();
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
Initialize
|
||||
|
||||
*/
|
||||
|
||||
hangersInit();
|
||||
})();
|
||||
|
|
@ -260,3 +260,23 @@ window.MainWardrobe = {View: {Outfit: {setFlashIsReady: previewSWFIsReady}}}
|
|||
|
||||
var SWFLog = $.noop;
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Trade hangers
|
||||
|
||||
*/
|
||||
|
||||
$(document.body).addClass('js');
|
||||
|
||||
$('#trade-hangers p').wrapInner('<div/>').each(function () {
|
||||
var el = $(this);
|
||||
if(el.height() < el.children().height()) {
|
||||
el.addClass('overflows');
|
||||
}
|
||||
});
|
||||
|
||||
$('#trade-hangers .toggle').click(function () {
|
||||
$(this).closest('p').toggleClass('showing-more');
|
||||
});
|
||||
|
||||
|
|
175
public/javascripts/jquery.ui.js
Normal file
|
@ -0,0 +1,175 @@
|
|||
/*!
|
||||
* jQuery UI 1.8.14
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI
|
||||
*/
|
||||
(function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.14",
|
||||
keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();
|
||||
b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,
|
||||
"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",
|
||||
function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,
|
||||
outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,"tabindex"),d=isNaN(b);
|
||||
return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=
|
||||
0;e<b.length;e++)a.options[b[e][0]]&&b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&c.ui.isOverAxis(b,e,i)}})}})(jQuery);
|
||||
;/*!
|
||||
* jQuery UI Widget 1.8.14
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Widget
|
||||
*/
|
||||
(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)b(d).triggerHandler("remove");k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){b(this).triggerHandler("remove")});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=function(h){return!!b.data(h,
|
||||
a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):d;if(e&&d.charAt(0)==="_")return h;
|
||||
e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=b.extend(true,{},this.options,
|
||||
this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},
|
||||
widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this},
|
||||
enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
|
||||
;/*!
|
||||
* jQuery UI Mouse 1.8.14
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Mouse
|
||||
*
|
||||
* Depends:
|
||||
* jquery.ui.widget.js
|
||||
*/
|
||||
(function(b){var d=false;b(document).mousedown(function(){d=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(c){return a._mouseDown(c)}).bind("click."+this.widgetName,function(c){if(true===b.data(c.target,a.widgetName+".preventClickEvent")){b.removeData(c.target,a.widgetName+".preventClickEvent");c.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+
|
||||
this.widgetName)},_mouseDown:function(a){if(!d){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var c=this,f=a.which==1,g=typeof this.options.cancel=="string"?b(a.target).closest(this.options.cancel).length:false;if(!f||g||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){c.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=this._mouseStart(a)!==
|
||||
false;if(!this._mouseStarted){a.preventDefault();return true}}true===b.data(a.target,this.widgetName+".preventClickEvent")&&b.removeData(a.target,this.widgetName+".preventClickEvent");this._mouseMoveDelegate=function(e){return c._mouseMove(e)};this._mouseUpDelegate=function(e){return c._mouseUp(e)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return d=true}},_mouseMove:function(a){if(b.browser.msie&&
|
||||
!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=
|
||||
false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
|
||||
;/*
|
||||
* jQuery UI Position 1.8.14
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Position
|
||||
*/
|
||||
(function(c){c.ui=c.ui||{};var n=/left|center|right/,o=/top|center|bottom/,t=c.fn.position,u=c.fn.offset;c.fn.position=function(b){if(!b||!b.of)return t.apply(this,arguments);b=c.extend({},b);var a=c(b.of),d=a[0],g=(b.collision||"flip").split(" "),e=b.offset?b.offset.split(" "):[0,0],h,k,j;if(d.nodeType===9){h=a.width();k=a.height();j={top:0,left:0}}else if(d.setTimeout){h=a.width();k=a.height();j={top:a.scrollTop(),left:a.scrollLeft()}}else if(d.preventDefault){b.at="left top";h=k=0;j={top:b.of.pageY,
|
||||
left:b.of.pageX}}else{h=a.outerWidth();k=a.outerHeight();j=a.offset()}c.each(["my","at"],function(){var f=(b[this]||"").split(" ");if(f.length===1)f=n.test(f[0])?f.concat(["center"]):o.test(f[0])?["center"].concat(f):["center","center"];f[0]=n.test(f[0])?f[0]:"center";f[1]=o.test(f[1])?f[1]:"center";b[this]=f});if(g.length===1)g[1]=g[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(b.at[0]==="right")j.left+=h;else if(b.at[0]==="center")j.left+=h/2;if(b.at[1]==="bottom")j.top+=
|
||||
k;else if(b.at[1]==="center")j.top+=k/2;j.left+=e[0];j.top+=e[1];return this.each(function(){var f=c(this),l=f.outerWidth(),m=f.outerHeight(),p=parseInt(c.curCSS(this,"marginLeft",true))||0,q=parseInt(c.curCSS(this,"marginTop",true))||0,v=l+p+(parseInt(c.curCSS(this,"marginRight",true))||0),w=m+q+(parseInt(c.curCSS(this,"marginBottom",true))||0),i=c.extend({},j),r;if(b.my[0]==="right")i.left-=l;else if(b.my[0]==="center")i.left-=l/2;if(b.my[1]==="bottom")i.top-=m;else if(b.my[1]==="center")i.top-=
|
||||
m/2;i.left=Math.round(i.left);i.top=Math.round(i.top);r={left:i.left-p,top:i.top-q};c.each(["left","top"],function(s,x){c.ui.position[g[s]]&&c.ui.position[g[s]][x](i,{targetWidth:h,targetHeight:k,elemWidth:l,elemHeight:m,collisionPosition:r,collisionWidth:v,collisionHeight:w,offset:e,my:b.my,at:b.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(i,{using:b.using}))})};c.ui.position={fit:{left:function(b,a){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();b.left=
|
||||
d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+=
|
||||
a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b),
|
||||
g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery);
|
||||
;/*
|
||||
* jQuery UI Draggable 1.8.14
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Draggables
|
||||
*
|
||||
* Depends:
|
||||
* jquery.ui.core.js
|
||||
* jquery.ui.mouse.js
|
||||
* jquery.ui.widget.js
|
||||
*/
|
||||
(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper==
|
||||
"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b=
|
||||
this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options;this.helper=
|
||||
this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});
|
||||
this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);d.ui.ddmanager&&d.ui.ddmanager.dragStart(this,a);return true},
|
||||
_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=
|
||||
false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,
|
||||
10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});d.ui.ddmanager&&d.ui.ddmanager.dragStop(this,a);return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||
|
||||
!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&
|
||||
a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=
|
||||
this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),
|
||||
10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),
|
||||
10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[a.containment=="document"?0:d(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,a.containment=="document"?0:d(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,
|
||||
(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!=
|
||||
"hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),
|
||||
10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+
|
||||
this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&
|
||||
!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.left<g[0])e=g[0]+this.offset.click.left;
|
||||
if(a.pageY-this.offset.click.top<g[1])h=g[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>g[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=b.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1]:this.originalPageY;h=g?!(h-this.offset.click.top<g[1]||h-this.offset.click.top>g[3])?h:!(h-this.offset.click.top<g[1])?h-b.grid[1]:h+b.grid[1]:h;e=b.grid[0]?this.originalPageX+Math.round((e-this.originalPageX)/
|
||||
b.grid[0])*b.grid[0]:this.originalPageX;e=g?!(e-this.offset.click.left<g[0]||e-this.offset.click.left>g[2])?e:!(e-this.offset.click.left<g[0])?e-b.grid[0]:e+b.grid[0]:e}}return{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop()),left:e-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&d.browser.version<
|
||||
526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging");this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove();this.helper=null;this.cancelHelperRemoval=false},_trigger:function(a,b,c){c=c||this._uiHash();d.ui.plugin.call(this,a,[b,c]);if(a=="drag")this.positionAbs=this._convertPositionTo("absolute");return d.Widget.prototype._trigger.call(this,a,b,
|
||||
c)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}});d.extend(d.ui.draggable,{version:"1.8.14"});d.ui.plugin.add("draggable","connectToSortable",{start:function(a,b){var c=d(this).data("draggable"),f=c.options,e=d.extend({},b,{item:c.element});c.sortables=[];d(f.connectToSortable).each(function(){var h=d.data(this,"sortable");if(h&&!h.options.disabled){c.sortables.push({instance:h,shouldRevert:h.options.revert});
|
||||
h.refreshPositions();h._trigger("activate",a,e)}})},stop:function(a,b){var c=d(this).data("draggable"),f=d.extend({},b,{item:c.element});d.each(c.sortables,function(){if(this.instance.isOver){this.instance.isOver=0;c.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(a);this.instance.options.helper=this.instance.options._helper;c.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})}else{this.instance.cancelHelperRemoval=
|
||||
false;this.instance._trigger("deactivate",a,f)}})},drag:function(a,b){var c=d(this).data("draggable"),f=this;d.each(c.sortables,function(){this.instance.positionAbs=c.positionAbs;this.instance.helperProportions=c.helperProportions;this.instance.offset.click=c.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=d(f).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",true);
|
||||
this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return b.helper[0]};a.target=this.instance.currentItem[0];this.instance._mouseCapture(a,true);this.instance._mouseStart(a,true,true);this.instance.offset.click.top=c.offset.click.top;this.instance.offset.click.left=c.offset.click.left;this.instance.offset.parent.left-=c.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=c.offset.parent.top-this.instance.offset.parent.top;
|
||||
c._trigger("toSortable",a);c.dropped=this.instance.element;c.currentItem=c.element;this.instance.fromOutside=c}this.instance.currentItem&&this.instance._mouseDrag(a)}else if(this.instance.isOver){this.instance.isOver=0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",a,this.instance._uiHash(this.instance));this.instance._mouseStop(a,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();this.instance.placeholder&&
|
||||
this.instance.placeholder.remove();c._trigger("fromSortable",a);c.dropped=false}})}});d.ui.plugin.add("draggable","cursor",{start:function(){var a=d("body"),b=d(this).data("draggable").options;if(a.css("cursor"))b._cursor=a.css("cursor");a.css("cursor",b.cursor)},stop:function(){var a=d(this).data("draggable").options;a._cursor&&d("body").css("cursor",a._cursor)}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("opacity"))b._opacity=
|
||||
a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){if(!c.axis||c.axis!=
|
||||
"x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop+c.scrollSpeed;else if(a.pageY-b.overflowOffset.top<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop-c.scrollSpeed;if(!c.axis||c.axis!="y")if(b.overflowOffset.left+b.scrollParent[0].offsetWidth-a.pageX<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft+c.scrollSpeed;else if(a.pageX-b.overflowOffset.left<
|
||||
c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft-c.scrollSpeed}else{if(!c.axis||c.axis!="x")if(a.pageY-d(document).scrollTop()<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()-c.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()+c.scrollSpeed);if(!c.axis||c.axis!="y")if(a.pageX-d(document).scrollLeft()<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()-
|
||||
c.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()+c.scrollSpeed)}f!==false&&d.ui.ddmanager&&!c.dropBehaviour&&d.ui.ddmanager.prepareOffsets(b,a)}});d.ui.plugin.add("draggable","snap",{start:function(){var a=d(this).data("draggable"),b=a.options;a.snapElements=[];d(b.snap.constructor!=String?b.snap.items||":data(draggable)":b.snap).each(function(){var c=d(this),f=c.offset();this!=a.element[0]&&a.snapElements.push({item:this,
|
||||
width:c.outerWidth(),height:c.outerHeight(),top:f.top,left:f.left})})},drag:function(a,b){for(var c=d(this).data("draggable"),f=c.options,e=f.snapTolerance,h=b.offset.left,g=h+c.helperProportions.width,n=b.offset.top,o=n+c.helperProportions.height,i=c.snapElements.length-1;i>=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e<h&&h<l+e&&k-e<n&&n<m+e||j-e<h&&h<l+e&&k-e<o&&o<m+e||j-e<g&&g<l+e&&k-e<n&&n<m+e||j-e<g&&g<l+e&&k-e<o&&
|
||||
o<m+e){if(f.snapMode!="inner"){var p=Math.abs(k-o)<=e,q=Math.abs(m-n)<=e,r=Math.abs(j-g)<=e,s=Math.abs(l-h)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k-c.helperProportions.height,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j-c.helperProportions.width}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l}).left-c.margins.left}var t=
|
||||
p||q||r||s;if(f.snapMode!="outer"){p=Math.abs(k-n)<=e;q=Math.abs(m-o)<=e;r=Math.abs(j-h)<=e;s=Math.abs(l-g)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m-c.helperProportions.height,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l-c.helperProportions.width}).left-c.margins.left}if(!c.snapElements[i].snapping&&
|
||||
(p||q||r||s||t))c.options.snap.snap&&c.options.snap.snap.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=p||q||r||s||t}else{c.snapElements[i].snapping&&c.options.snap.release&&c.options.snap.release.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=false}}}});d.ui.plugin.add("draggable","stack",{start:function(){var a=d(this).data("draggable").options;a=d.makeArray(d(a.stack)).sort(function(c,f){return(parseInt(d(c).css("zIndex"),
|
||||
10)||0)-(parseInt(d(f).css("zIndex"),10)||0)});if(a.length){var b=parseInt(a[0].style.zIndex)||0;d(a).each(function(c){this.style.zIndex=b+c});this[0].style.zIndex=b+a.length}}});d.ui.plugin.add("draggable","zIndex",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("zIndex"))b._zIndex=a.css("zIndex");a.css("zIndex",b.zIndex)},stop:function(a,b){a=d(this).data("draggable").options;a._zIndex&&d(b.helper).css("zIndex",a._zIndex)}})})(jQuery);
|
||||
;/*
|
||||
* jQuery UI Droppable 1.8.14
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Droppables
|
||||
*
|
||||
* Depends:
|
||||
* jquery.ui.core.js
|
||||
* jquery.ui.widget.js
|
||||
* jquery.ui.mouse.js
|
||||
* jquery.ui.draggable.js
|
||||
*/
|
||||
(function(d){d.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:false,addClasses:true,greedy:false,hoverClass:false,scope:"default",tolerance:"intersect"},_create:function(){var a=this.options,b=a.accept;this.isover=0;this.isout=1;this.accept=d.isFunction(b)?b:function(c){return c.is(b)};this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};d.ui.ddmanager.droppables[a.scope]=d.ui.ddmanager.droppables[a.scope]||[];d.ui.ddmanager.droppables[a.scope].push(this);
|
||||
a.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){for(var a=d.ui.ddmanager.droppables[this.options.scope],b=0;b<a.length;b++)a[b]==this&&a.splice(b,1);this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");return this},_setOption:function(a,b){if(a=="accept")this.accept=d.isFunction(b)?b:function(c){return c.is(b)};d.Widget.prototype._setOption.apply(this,arguments)},_activate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&
|
||||
this.element.addClass(this.options.activeClass);b&&this._trigger("activate",a,this.ui(b))},_deactivate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass);b&&this._trigger("deactivate",a,this.ui(b))},_over:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.addClass(this.options.hoverClass);
|
||||
this._trigger("over",a,this.ui(b))}},_out:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("out",a,this.ui(b))}},_drop:function(a,b){var c=b||d.ui.ddmanager.current;if(!c||(c.currentItem||c.element)[0]==this.element[0])return false;var e=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var g=
|
||||
d.data(this,"droppable");if(g.options.greedy&&!g.options.disabled&&g.options.scope==c.options.scope&&g.accept.call(g.element[0],c.currentItem||c.element)&&d.ui.intersect(c,d.extend(g,{offset:g.element.offset()}),g.options.tolerance)){e=true;return false}});if(e)return false;if(this.accept.call(this.element[0],c.currentItem||c.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass);this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("drop",
|
||||
a,this.ui(c));return this.element}return false},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}});d.extend(d.ui.droppable,{version:"1.8.14"});d.ui.intersect=function(a,b,c){if(!b.offset)return false;var e=(a.positionAbs||a.position.absolute).left,g=e+a.helperProportions.width,f=(a.positionAbs||a.position.absolute).top,h=f+a.helperProportions.height,i=b.offset.left,k=i+b.proportions.width,j=b.offset.top,l=j+b.proportions.height;
|
||||
switch(c){case "fit":return i<=e&&g<=k&&j<=f&&h<=l;case "intersect":return i<e+a.helperProportions.width/2&&g-a.helperProportions.width/2<k&&j<f+a.helperProportions.height/2&&h-a.helperProportions.height/2<l;case "pointer":return d.ui.isOver((a.positionAbs||a.position.absolute).top+(a.clickOffset||a.offset.click).top,(a.positionAbs||a.position.absolute).left+(a.clickOffset||a.offset.click).left,j,i,b.proportions.height,b.proportions.width);case "touch":return(f>=j&&f<=l||h>=j&&h<=l||f<j&&h>l)&&(e>=
|
||||
i&&e<=k||g>=i&&g<=k||e<i&&g>k);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f<c.length;f++)if(!(c[f].options.disabled||a&&!c[f].accept.call(c[f].element[0],a.currentItem||a.element))){for(var h=0;h<g.length;h++)if(g[h]==c[f].element[0]){c[f].proportions.height=0;continue a}c[f].visible=c[f].element.css("display")!=
|
||||
"none";if(c[f].visible){e=="mousedown"&&c[f]._activate.call(c[f],b);c[f].offset=c[f].element.offset();c[f].proportions={width:c[f].element[0].offsetWidth,height:c[f].element[0].offsetHeight}}}},drop:function(a,b){var c=false;d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(this.options){if(!this.options.disabled&&this.visible&&d.ui.intersect(a,this,this.options.tolerance))c=c||this._drop.call(this,b);if(!this.options.disabled&&this.visible&&this.accept.call(this.element[0],a.currentItem||
|
||||
a.element)){this.isout=1;this.isover=0;this._deactivate.call(this,b)}}});return c},dragStart:function(a,b){a.element.parentsUntil("body").bind("scroll.droppable",function(){a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)})},drag:function(a,b){a.options.refreshPositions&&d.ui.ddmanager.prepareOffsets(a,b);d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var c=d.ui.intersect(a,this,this.options.tolerance);if(c=
|
||||
!c&&this.isover==1?"isout":c&&this.isover==0?"isover":null){var e;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");if(g.length){e=d.data(g[0],"droppable");e.greedyChild=c=="isover"?1:0}}if(e&&c=="isover"){e.isover=0;e.isout=1;e._out.call(e,b)}this[c]=1;this[c=="isout"?"isover":"isout"]=0;this[c=="isover"?"_over":"_out"].call(this,b);if(e&&c=="isout"){e.isout=0;e.isover=1;e._over.call(e,b)}}}})},dragStop:function(a,b){a.element.parentsUntil("body").unbind("scroll.droppable");
|
||||
a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)}}})(jQuery);
|
||||
;/*
|
||||
* jQuery UI Autocomplete 1.8.14
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Autocomplete
|
||||
*
|
||||
* Depends:
|
||||
* jquery.ui.core.js
|
||||
* jquery.ui.widget.js
|
||||
* jquery.ui.position.js
|
||||
*/
|
||||
(function(d){var e=0;d.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:false,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var a=this,b=this.element[0].ownerDocument,g;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!(a.options.disabled||a.element.attr("readonly"))){g=
|
||||
false;var f=d.ui.keyCode;switch(c.keyCode){case f.PAGE_UP:a._move("previousPage",c);break;case f.PAGE_DOWN:a._move("nextPage",c);break;case f.UP:a._move("previous",c);c.preventDefault();break;case f.DOWN:a._move("next",c);c.preventDefault();break;case f.ENTER:case f.NUMPAD_ENTER:if(a.menu.active){g=true;c.preventDefault()}case f.TAB:if(!a.menu.active)return;a.menu.select(c);break;case f.ESCAPE:a.element.val(a.term);a.close(c);break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){if(a.term!=
|
||||
a.element.val()){a.selectedItem=null;a.search(null,c)}},a.options.delay);break}}}).bind("keypress.autocomplete",function(c){if(g){g=false;c.preventDefault()}}).bind("focus.autocomplete",function(){if(!a.options.disabled){a.selectedItem=null;a.previous=a.element.val()}}).bind("blur.autocomplete",function(c){if(!a.options.disabled){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)}});this._initSource();this.response=function(){return a._response.apply(a,arguments)};
|
||||
this.menu=d("<ul></ul>").addClass("ui-autocomplete").appendTo(d(this.options.appendTo||"body",b)[0]).mousedown(function(c){var f=a.menu.element[0];d(c.target).closest(".ui-menu-item").length||setTimeout(function(){d(document).one("mousedown",function(h){h.target!==a.element[0]&&h.target!==f&&!d.ui.contains(f,h.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,f){f=f.item.data("item.autocomplete");false!==a._trigger("focus",c,{item:f})&&/^key/.test(c.originalEvent.type)&&
|
||||
a.element.val(f.value)},selected:function(c,f){var h=f.item.data("item.autocomplete"),i=a.previous;if(a.element[0]!==b.activeElement){a.element.focus();a.previous=i;setTimeout(function(){a.previous=i;a.selectedItem=h},1)}false!==a._trigger("select",c,{item:h})&&a.element.val(h.value);a.term=a.element.val();a.close(c);a.selectedItem=h},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu");
|
||||
d.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");this.menu.element.remove();d.Widget.prototype.destroy.call(this)},_setOption:function(a,b){d.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(d(b||"body",this.element[0].ownerDocument)[0]);a==="disabled"&&
|
||||
b&&this.xhr&&this.xhr.abort()},_initSource:function(){var a=this,b,g;if(d.isArray(this.options.source)){b=this.options.source;this.source=function(c,f){f(d.ui.autocomplete.filter(b,c.term))}}else if(typeof this.options.source==="string"){g=this.options.source;this.source=function(c,f){a.xhr&&a.xhr.abort();a.xhr=d.ajax({url:g,data:c,dataType:"json",autocompleteRequest:++e,success:function(h){this.autocompleteRequest===e&&f(h)},error:function(){this.autocompleteRequest===e&&f([])}})}}else this.source=
|
||||
this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length<this.options.minLength)return this.close(b);clearTimeout(this.closing);if(this._trigger("search",b)!==false)return this._search(a)},_search:function(a){this.pending++;this.element.addClass("ui-autocomplete-loading");this.source({term:a},this.response)},_response:function(a){if(!this.options.disabled&&a&&a.length){a=this._normalize(a);this._suggest(a);this._trigger("open")}else this.close();
|
||||
this.pending--;this.pending||this.element.removeClass("ui-autocomplete-loading")},close:function(a){clearTimeout(this.closing);if(this.menu.element.is(":visible")){this.menu.element.hide();this.menu.deactivate();this._trigger("close",a)}},_change:function(a){this.previous!==this.element.val()&&this._trigger("change",a,{item:this.selectedItem})},_normalize:function(a){if(a.length&&a[0].label&&a[0].value)return a;return d.map(a,function(b){if(typeof b==="string")return{label:b,value:b};return d.extend({label:b.label||
|
||||
b.value,value:b.value||b.label},b)})},_suggest:function(a){var b=this.menu.element.empty().zIndex(this.element.zIndex()+1);this._renderMenu(b,a);this.menu.deactivate();this.menu.refresh();b.show();this._resizeMenu();b.position(d.extend({of:this.element},this.options.position));this.options.autoFocus&&this.menu.next(new d.Event("mouseover"))},_resizeMenu:function(){var a=this.menu.element;a.outerWidth(Math.max(a.width("").outerWidth(),this.element.outerWidth()))},_renderMenu:function(a,b){var g=this;
|
||||
d.each(b,function(c,f){g._renderItem(a,f)})},_renderItem:function(a,b){return d("<li></li>").data("item.autocomplete",b).append(d("<a></a>").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});d.extend(d.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,
|
||||
"\\$&")},filter:function(a,b){var g=new RegExp(d.ui.autocomplete.escapeRegex(b),"i");return d.grep(a,function(c){return g.test(c.label||c.value||c)})}})})(jQuery);
|
||||
(function(d){d.widget("ui.menu",{_create:function(){var e=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(a){if(d(a.target).closest(".ui-menu-item a").length){a.preventDefault();e.select(a)}});this.refresh()},refresh:function(){var e=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex",
|
||||
-1).mouseenter(function(a){e.activate(a,d(this).parent())}).mouseleave(function(){e.deactivate()})},activate:function(e,a){this.deactivate();if(this.hasScroll()){var b=a.offset().top-this.element.offset().top,g=this.element.scrollTop(),c=this.element.height();if(b<0)this.element.scrollTop(g+b);else b>=c&&this.element.scrollTop(g+b-c+a.height())}this.active=a.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",e,{item:a})},deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id");
|
||||
this._trigger("blur");this.active=null}},next:function(e){this.move("next",".ui-menu-item:first",e)},previous:function(e){this.move("prev",".ui-menu-item:last",e)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(e,a,b){if(this.active){e=this.active[e+"All"](".ui-menu-item").eq(0);e.length?this.activate(b,e):this.activate(b,this.element.children(a))}else this.activate(b,
|
||||
this.element.children(a))},nextPage:function(e){if(this.hasScroll())if(!this.active||this.last())this.activate(e,this.element.children(".ui-menu-item:first"));else{var a=this.active.offset().top,b=this.element.height(),g=this.element.children(".ui-menu-item").filter(function(){var c=d(this).offset().top-a-b+d(this).height();return c<10&&c>-10});g.length||(g=this.element.children(".ui-menu-item:last"));this.activate(e,g)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||
|
||||
this.last()?":first":":last"))},previousPage:function(e){if(this.hasScroll())if(!this.active||this.first())this.activate(e,this.element.children(".ui-menu-item:last"));else{var a=this.active.offset().top,b=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var g=d(this).offset().top-a+b-d(this).height();return g<10&&g>-10});result.length||(result=this.element.children(".ui-menu-item:first"));this.activate(e,result)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||
|
||||
this.first()?":last":":first"))},hasScroll:function(){return this.element.height()<this.element[d.fn.prop?"prop":"attr"]("scrollHeight")},select:function(e){this._trigger("selected",e,{item:this.active})}})})(jQuery);
|
||||
;
|
|
@ -102,6 +102,13 @@ Partial.ItemSet = function ItemSet(wardrobe, selector) {
|
|||
) {
|
||||
$('<div/>', {'class': 'nc-icon', text: 'NC', title: 'NC'}).appendTo(li);
|
||||
}
|
||||
if(item.owned || item.wanted) {
|
||||
var iconsWrapper = $('<div/>', {'class': 'closeted-icons'}).appendTo(li);
|
||||
if(item.owned) {
|
||||
$('<img/>', {alt: 'Own', title: 'You own this', src: '/images/owned.png'}).appendTo(iconsWrapper);
|
||||
$('<img/>', {alt: 'Want', title: 'You want this', src: '/images/wanted.png'}).appendTo(iconsWrapper);
|
||||
}
|
||||
}
|
||||
li.append(img).append(controls).append(info_link).append(item.name).appendTo(ul);
|
||||
}
|
||||
setClosetItems(wardrobe.outfit.getClosetItems());
|
||||
|
@ -860,6 +867,7 @@ View.Search = function (wardrobe) {
|
|||
loading_el = $('#preview-search-form-loading'),
|
||||
no_results_el = $('#preview-search-form-no-results'),
|
||||
no_results_span = no_results_el.children('span'),
|
||||
your_items_links = $('.preview-search-form-your-items'),
|
||||
PAGINATION = {
|
||||
INNER_WINDOW: 4,
|
||||
OUTER_WINDOW: 1,
|
||||
|
@ -924,6 +932,12 @@ View.Search = function (wardrobe) {
|
|||
form.submit();
|
||||
});
|
||||
|
||||
your_items_links.click(function (e) {
|
||||
e.preventDefault();
|
||||
input_el.val('user:' + this.getAttribute('data-search-value'));
|
||||
form.submit();
|
||||
});
|
||||
|
||||
wardrobe.search.bind('startRequest', function () {
|
||||
loading_el.delay(1000).show('slow');
|
||||
});
|
||||
|
|
3
public/javascripts/placeholder.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
/*! http://mths.be/placeholder v1.8.4 by @mathias */
|
||||
(function($){var e='placeholder' in document.createElement('input'),a='placeholder' in document.createElement('textarea');if(e&&a){$.fn.placeholder=function(){return this};$.fn.placeholder.input=$.fn.placeholder.textarea=true}else{$.fn.placeholder=function(){return this.filter((e?'textarea':':input')+'[placeholder]').bind('focus.placeholder',b).bind('blur.placeholder',d).trigger('blur.placeholder').end()};$.fn.placeholder.input=e;$.fn.placeholder.textarea=a;$(function(){$('form').bind('submit.placeholder',function(){var f=$('.placeholder',this).each(b);setTimeout(function(){f.each(d)},10)})});$(window).bind('unload.placeholder',function(){$('.placeholder').val('')})}function c(g){var f={},h=/^jQuery\d+$/;$.each(g.attributes,function(k,j){if(j.specified&&!h.test(j.name)){f[j.name]=j.value}});return f}function b(){var f=$(this);if(f.val()===f.attr('placeholder')&&f.hasClass('placeholder')){if(f.data('placeholder-password')){f.hide().next().attr('id',f.removeAttr('id').data('placeholder-id')).show().focus()}else{f.val('').removeClass('placeholder')}}}function d(){var j,i=$(this),f=i,h=this.id;if(i.val()===''){if(i.is(':password')){if(!i.data('placeholder-textinput')){try{j=i.clone().attr({type:'text'})}catch(g){j=$('<input>').attr($.extend(c(this),{type:'text'}))}j.removeAttr('name').data('placeholder-password',true).data('placeholder-id',h).bind('focus.placeholder',b);i.data('placeholder-textinput',j).data('placeholder-id',h).before(j)}i=i.removeAttr('id').hide().prev().attr('id',h).show()}i.addClass('placeholder').val(i.attr('placeholder'))}else{i.removeClass('placeholder')}}}(jQuery));
|
||||
|
After Width: | Height: | Size: 127 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 139 B |
After Width: | Height: | Size: 96 B |
After Width: | Height: | Size: 153 B |
After Width: | Height: | Size: 105 B |
After Width: | Height: | Size: 124 B |
After Width: | Height: | Size: 165 B |
After Width: | Height: | Size: 119 B |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.3 KiB |
342
public/stylesheets/south-street/jquery-ui.css
vendored
Normal file
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
* jQuery UI CSS Framework 1.8.14
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Theming/API
|
||||
*/
|
||||
|
||||
/* Layout helpers
|
||||
----------------------------------*/
|
||||
.ui-helper-hidden { display: none; }
|
||||
.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
|
||||
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
|
||||
.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
|
||||
.ui-helper-clearfix { display: inline-block; }
|
||||
/* required comment for clearfix to work in Opera \*/
|
||||
* html .ui-helper-clearfix { height:1%; }
|
||||
.ui-helper-clearfix { display:block; }
|
||||
/* end clearfix */
|
||||
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
|
||||
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-disabled { cursor: default !important; }
|
||||
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
||||
|
||||
|
||||
/*
|
||||
* jQuery UI CSS Framework 1.8.14
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Theming/API
|
||||
*
|
||||
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=segoe%20ui,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=6px&bgColorHeader=ece8da&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=100&borderColorHeader=d4ccb0&fcHeader=433f38&iconColorHeader=847e71&bgColorContent=f5f3e5&bgTextureContent=04_highlight_hard.png&bgImgOpacityContent=100&borderColorContent=dfd9c3&fcContent=312e25&iconColorContent=808080&bgColorDefault=459e00&bgTextureDefault=04_highlight_hard.png&bgImgOpacityDefault=15&borderColorDefault=327E04&fcDefault=ffffff&iconColorDefault=eeeeee&bgColorHover=67b021&bgTextureHover=03_highlight_soft.png&bgImgOpacityHover=25&borderColorHover=327E04&fcHover=ffffff&iconColorHover=ffffff&bgColorActive=fafaf4&bgTextureActive=04_highlight_hard.png&bgImgOpacityActive=100&borderColorActive=d4ccb0&fcActive=459e00&iconColorActive=8DC262&bgColorHighlight=fcf0ba&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=e8e1b5&fcHighlight=363636&iconColorHighlight=8DC262&bgColorError=ffedad&bgTextureError=03_highlight_soft.png&bgImgOpacityError=95&borderColorError=e3a345&fcError=cd5c0a&iconColorError=cd0a0a&bgColorOverlay=2b2922&bgTextureOverlay=05_inset_soft.png&bgImgOpacityOverlay=15&opacityOverlay=90&bgColorShadow=cccccc&bgTextureShadow=04_highlight_hard.png&bgImgOpacityShadow=95&opacityShadow=20&thicknessShadow=12px&offsetTopShadow=-12px&offsetLeftShadow=-12px&cornerRadiusShadow=10px
|
||||
*/
|
||||
|
||||
|
||||
/* Component containers
|
||||
----------------------------------*/
|
||||
.ui-widget { font-family: segoe ui, Arial, sans-serif; font-size: 1.1em; }
|
||||
.ui-widget .ui-widget { font-size: 1em; }
|
||||
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: segoe ui, Arial, sans-serif; font-size: 1em; }
|
||||
.ui-widget-content { border: 1px solid #dfd9c3; background: #f5f3e5 url(images/ui-bg_highlight-hard_100_f5f3e5_1x100.png) 50% top repeat-x; color: #312e25; }
|
||||
.ui-widget-content a { color: #312e25; }
|
||||
.ui-widget-header { border: 1px solid #d4ccb0; background: #ece8da url(images/ui-bg_gloss-wave_100_ece8da_500x100.png) 50% 50% repeat-x; color: #433f38; font-weight: bold; }
|
||||
.ui-widget-header a { color: #433f38; }
|
||||
|
||||
/* Interaction states
|
||||
----------------------------------*/
|
||||
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #327e04; background: #459e00 url(images/ui-bg_highlight-hard_15_459e00_1x100.png) 50% 50% repeat-x; font-weight: bold; color: #ffffff; }
|
||||
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #ffffff; text-decoration: none; }
|
||||
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #327e04; background: #67b021 url(images/ui-bg_highlight-soft_25_67b021_1x100.png) 50% 50% repeat-x; font-weight: bold; color: #ffffff; }
|
||||
.ui-state-hover a, .ui-state-hover a:hover { color: #ffffff; text-decoration: none; }
|
||||
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #d4ccb0; background: #fafaf4 url(images/ui-bg_highlight-hard_100_fafaf4_1x100.png) 50% 50% repeat-x; font-weight: bold; color: #459e00; }
|
||||
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #459e00; text-decoration: none; }
|
||||
.ui-widget :active { outline: none; }
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #e8e1b5; background: #fcf0ba url(images/ui-bg_glass_55_fcf0ba_1x400.png) 50% 50% repeat-x; color: #363636; }
|
||||
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
|
||||
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #e3a345; background: #ffedad url(images/ui-bg_highlight-soft_95_ffedad_1x100.png) 50% top repeat-x; color: #cd5c0a; }
|
||||
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd5c0a; }
|
||||
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd5c0a; }
|
||||
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
|
||||
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
|
||||
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_808080_256x240.png); }
|
||||
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_808080_256x240.png); }
|
||||
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_847e71_256x240.png); }
|
||||
.ui-state-default .ui-icon { background-image: url(images/ui-icons_eeeeee_256x240.png); }
|
||||
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
|
||||
.ui-state-active .ui-icon {background-image: url(images/ui-icons_8dc262_256x240.png); }
|
||||
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_8dc262_256x240.png); }
|
||||
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
|
||||
|
||||
/* positioning */
|
||||
.ui-icon-carat-1-n { background-position: 0 0; }
|
||||
.ui-icon-carat-1-ne { background-position: -16px 0; }
|
||||
.ui-icon-carat-1-e { background-position: -32px 0; }
|
||||
.ui-icon-carat-1-se { background-position: -48px 0; }
|
||||
.ui-icon-carat-1-s { background-position: -64px 0; }
|
||||
.ui-icon-carat-1-sw { background-position: -80px 0; }
|
||||
.ui-icon-carat-1-w { background-position: -96px 0; }
|
||||
.ui-icon-carat-1-nw { background-position: -112px 0; }
|
||||
.ui-icon-carat-2-n-s { background-position: -128px 0; }
|
||||
.ui-icon-carat-2-e-w { background-position: -144px 0; }
|
||||
.ui-icon-triangle-1-n { background-position: 0 -16px; }
|
||||
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
|
||||
.ui-icon-triangle-1-e { background-position: -32px -16px; }
|
||||
.ui-icon-triangle-1-se { background-position: -48px -16px; }
|
||||
.ui-icon-triangle-1-s { background-position: -64px -16px; }
|
||||
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
|
||||
.ui-icon-triangle-1-w { background-position: -96px -16px; }
|
||||
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
|
||||
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
|
||||
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
|
||||
.ui-icon-arrow-1-n { background-position: 0 -32px; }
|
||||
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
|
||||
.ui-icon-arrow-1-e { background-position: -32px -32px; }
|
||||
.ui-icon-arrow-1-se { background-position: -48px -32px; }
|
||||
.ui-icon-arrow-1-s { background-position: -64px -32px; }
|
||||
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
|
||||
.ui-icon-arrow-1-w { background-position: -96px -32px; }
|
||||
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
|
||||
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
|
||||
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
|
||||
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
|
||||
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
|
||||
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
|
||||
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
|
||||
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
|
||||
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
|
||||
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
|
||||
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
|
||||
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
|
||||
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
|
||||
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
|
||||
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
|
||||
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
|
||||
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
|
||||
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
|
||||
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
|
||||
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
|
||||
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
|
||||
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
|
||||
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
|
||||
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
|
||||
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
|
||||
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
|
||||
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
|
||||
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
|
||||
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
|
||||
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
|
||||
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
|
||||
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
|
||||
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
|
||||
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
|
||||
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
|
||||
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
|
||||
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
|
||||
.ui-icon-arrow-4 { background-position: 0 -80px; }
|
||||
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
|
||||
.ui-icon-extlink { background-position: -32px -80px; }
|
||||
.ui-icon-newwin { background-position: -48px -80px; }
|
||||
.ui-icon-refresh { background-position: -64px -80px; }
|
||||
.ui-icon-shuffle { background-position: -80px -80px; }
|
||||
.ui-icon-transfer-e-w { background-position: -96px -80px; }
|
||||
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
|
||||
.ui-icon-folder-collapsed { background-position: 0 -96px; }
|
||||
.ui-icon-folder-open { background-position: -16px -96px; }
|
||||
.ui-icon-document { background-position: -32px -96px; }
|
||||
.ui-icon-document-b { background-position: -48px -96px; }
|
||||
.ui-icon-note { background-position: -64px -96px; }
|
||||
.ui-icon-mail-closed { background-position: -80px -96px; }
|
||||
.ui-icon-mail-open { background-position: -96px -96px; }
|
||||
.ui-icon-suitcase { background-position: -112px -96px; }
|
||||
.ui-icon-comment { background-position: -128px -96px; }
|
||||
.ui-icon-person { background-position: -144px -96px; }
|
||||
.ui-icon-print { background-position: -160px -96px; }
|
||||
.ui-icon-trash { background-position: -176px -96px; }
|
||||
.ui-icon-locked { background-position: -192px -96px; }
|
||||
.ui-icon-unlocked { background-position: -208px -96px; }
|
||||
.ui-icon-bookmark { background-position: -224px -96px; }
|
||||
.ui-icon-tag { background-position: -240px -96px; }
|
||||
.ui-icon-home { background-position: 0 -112px; }
|
||||
.ui-icon-flag { background-position: -16px -112px; }
|
||||
.ui-icon-calendar { background-position: -32px -112px; }
|
||||
.ui-icon-cart { background-position: -48px -112px; }
|
||||
.ui-icon-pencil { background-position: -64px -112px; }
|
||||
.ui-icon-clock { background-position: -80px -112px; }
|
||||
.ui-icon-disk { background-position: -96px -112px; }
|
||||
.ui-icon-calculator { background-position: -112px -112px; }
|
||||
.ui-icon-zoomin { background-position: -128px -112px; }
|
||||
.ui-icon-zoomout { background-position: -144px -112px; }
|
||||
.ui-icon-search { background-position: -160px -112px; }
|
||||
.ui-icon-wrench { background-position: -176px -112px; }
|
||||
.ui-icon-gear { background-position: -192px -112px; }
|
||||
.ui-icon-heart { background-position: -208px -112px; }
|
||||
.ui-icon-star { background-position: -224px -112px; }
|
||||
.ui-icon-link { background-position: -240px -112px; }
|
||||
.ui-icon-cancel { background-position: 0 -128px; }
|
||||
.ui-icon-plus { background-position: -16px -128px; }
|
||||
.ui-icon-plusthick { background-position: -32px -128px; }
|
||||
.ui-icon-minus { background-position: -48px -128px; }
|
||||
.ui-icon-minusthick { background-position: -64px -128px; }
|
||||
.ui-icon-close { background-position: -80px -128px; }
|
||||
.ui-icon-closethick { background-position: -96px -128px; }
|
||||
.ui-icon-key { background-position: -112px -128px; }
|
||||
.ui-icon-lightbulb { background-position: -128px -128px; }
|
||||
.ui-icon-scissors { background-position: -144px -128px; }
|
||||
.ui-icon-clipboard { background-position: -160px -128px; }
|
||||
.ui-icon-copy { background-position: -176px -128px; }
|
||||
.ui-icon-contact { background-position: -192px -128px; }
|
||||
.ui-icon-image { background-position: -208px -128px; }
|
||||
.ui-icon-video { background-position: -224px -128px; }
|
||||
.ui-icon-script { background-position: -240px -128px; }
|
||||
.ui-icon-alert { background-position: 0 -144px; }
|
||||
.ui-icon-info { background-position: -16px -144px; }
|
||||
.ui-icon-notice { background-position: -32px -144px; }
|
||||
.ui-icon-help { background-position: -48px -144px; }
|
||||
.ui-icon-check { background-position: -64px -144px; }
|
||||
.ui-icon-bullet { background-position: -80px -144px; }
|
||||
.ui-icon-radio-off { background-position: -96px -144px; }
|
||||
.ui-icon-radio-on { background-position: -112px -144px; }
|
||||
.ui-icon-pin-w { background-position: -128px -144px; }
|
||||
.ui-icon-pin-s { background-position: -144px -144px; }
|
||||
.ui-icon-play { background-position: 0 -160px; }
|
||||
.ui-icon-pause { background-position: -16px -160px; }
|
||||
.ui-icon-seek-next { background-position: -32px -160px; }
|
||||
.ui-icon-seek-prev { background-position: -48px -160px; }
|
||||
.ui-icon-seek-end { background-position: -64px -160px; }
|
||||
.ui-icon-seek-start { background-position: -80px -160px; }
|
||||
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
|
||||
.ui-icon-seek-first { background-position: -80px -160px; }
|
||||
.ui-icon-stop { background-position: -96px -160px; }
|
||||
.ui-icon-eject { background-position: -112px -160px; }
|
||||
.ui-icon-volume-off { background-position: -128px -160px; }
|
||||
.ui-icon-volume-on { background-position: -144px -160px; }
|
||||
.ui-icon-power { background-position: 0 -176px; }
|
||||
.ui-icon-signal-diag { background-position: -16px -176px; }
|
||||
.ui-icon-signal { background-position: -32px -176px; }
|
||||
.ui-icon-battery-0 { background-position: -48px -176px; }
|
||||
.ui-icon-battery-1 { background-position: -64px -176px; }
|
||||
.ui-icon-battery-2 { background-position: -80px -176px; }
|
||||
.ui-icon-battery-3 { background-position: -96px -176px; }
|
||||
.ui-icon-circle-plus { background-position: 0 -192px; }
|
||||
.ui-icon-circle-minus { background-position: -16px -192px; }
|
||||
.ui-icon-circle-close { background-position: -32px -192px; }
|
||||
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
|
||||
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
|
||||
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
|
||||
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
|
||||
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
|
||||
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
|
||||
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
|
||||
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
|
||||
.ui-icon-circle-zoomin { background-position: -176px -192px; }
|
||||
.ui-icon-circle-zoomout { background-position: -192px -192px; }
|
||||
.ui-icon-circle-check { background-position: -208px -192px; }
|
||||
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
|
||||
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
|
||||
.ui-icon-circlesmall-close { background-position: -32px -208px; }
|
||||
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
|
||||
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
|
||||
.ui-icon-squaresmall-close { background-position: -80px -208px; }
|
||||
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
|
||||
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
|
||||
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
|
||||
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
|
||||
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
|
||||
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Corner radius */
|
||||
.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; -khtml-border-top-left-radius: 6px; border-top-left-radius: 6px; }
|
||||
.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; -khtml-border-top-right-radius: 6px; border-top-right-radius: 6px; }
|
||||
.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; -khtml-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; }
|
||||
.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; -khtml-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; }
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay { background: #2b2922 url(images/ui-bg_inset-soft_15_2b2922_1x100.png) 50% bottom repeat-x; opacity: .90;filter:Alpha(Opacity=90); }
|
||||
.ui-widget-shadow { margin: -12px 0 0 -12px; padding: 12px; background: #cccccc url(images/ui-bg_highlight-hard_95_cccccc_1x100.png) 50% top repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 10px; -khtml-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; }/*
|
||||
* jQuery UI Autocomplete 1.8.14
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Autocomplete#theming
|
||||
*/
|
||||
.ui-autocomplete { position: absolute; cursor: default; }
|
||||
|
||||
/* workarounds */
|
||||
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
|
||||
|
||||
/*
|
||||
* jQuery UI Menu 1.8.14
|
||||
*
|
||||
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Menu#theming
|
||||
*/
|
||||
.ui-menu {
|
||||
list-style:none;
|
||||
padding: 2px;
|
||||
margin: 0;
|
||||
display:block;
|
||||
float: left;
|
||||
}
|
||||
.ui-menu .ui-menu {
|
||||
margin-top: -3px;
|
||||
}
|
||||
.ui-menu .ui-menu-item {
|
||||
margin:0;
|
||||
padding: 0;
|
||||
zoom: 1;
|
||||
float: left;
|
||||
clear: left;
|
||||
width: 100%;
|
||||
}
|
||||
.ui-menu .ui-menu-item a {
|
||||
text-decoration:none;
|
||||
display:block;
|
||||
padding:.2em .4em;
|
||||
line-height:1.5;
|
||||
zoom:1;
|
||||
}
|
||||
.ui-menu .ui-menu-item a.ui-state-hover,
|
||||
.ui-menu .ui-menu-item a.ui-state-active {
|
||||
font-weight: normal;
|
||||
margin: -1px;
|
||||
}
|
5
spec/controllers/closet_hangers_controller_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe ClosetHangersController do
|
||||
|
||||
end
|
19
spec/controllers/closet_lists_controller_spec.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe ClosetListsController do
|
||||
|
||||
describe "GET 'new'" do
|
||||
it "should be successful" do
|
||||
get 'new'
|
||||
response.should be_success
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET 'create'" do
|
||||
it "should be successful" do
|
||||
get 'create'
|
||||
response.should be_success
|
||||
end
|
||||
end
|
||||
|
||||
end
|
5
spec/controllers/closet_pages_controller_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe ClosetPagesController do
|
||||
|
||||
end
|
15
spec/helpers/closet_hangers_helper_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
require 'spec_helper'
|
||||
|
||||
# Specs in this file have access to a helper object that includes
|
||||
# the ClosetHangersHelper. For example:
|
||||
#
|
||||
# describe ClosetHangersHelper do
|
||||
# describe "string concat" do
|
||||
# it "concats two strings with spaces" do
|
||||
# helper.concat_strings("this","that").should == "this that"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
describe ClosetHangersHelper do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
15
spec/helpers/closet_lists_helper_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
require 'spec_helper'
|
||||
|
||||
# Specs in this file have access to a helper object that includes
|
||||
# the ClosetListsHelper. For example:
|
||||
#
|
||||
# describe ClosetListsHelper do
|
||||
# describe "string concat" do
|
||||
# it "concats two strings with spaces" do
|
||||
# helper.concat_strings("this","that").should == "this that"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
describe ClosetListsHelper do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
15
spec/helpers/closet_pages_helper_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
require 'spec_helper'
|
||||
|
||||
# Specs in this file have access to a helper object that includes
|
||||
# the ClosetPagesHelper. For example:
|
||||
#
|
||||
# describe ClosetPagesHelper do
|
||||
# describe "string concat" do
|
||||
# it "concats two strings with spaces" do
|
||||
# helper.concat_strings("this","that").should == "this that"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
describe ClosetPagesHelper do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
5
spec/models/closet_hanger_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe ClosetHanger do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
5
spec/models/closet_list_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe ClosetList do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
5
spec/views/closet_lists/create.html.erb_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe "closet_lists/create.html.erb" do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
5
spec/views/closet_lists/new.html.erb_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe "closet_lists/new.html.erb" do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
5
spec/views/petpages/new.html.erb_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe "petpages/new.html.erb" do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|