Commit 90efe3ca9da72beb3c46a6e347253a5bedd94c2a

Authored by Daniela Feitosa
Committed by Joenio Costa
1 parent 2836c18c

Enhancing edition of product

Fixing categories selection and fixing width of sideboxes in leftbar and rightbar templates
Added themes to jquery-ui

(ActionItem1392)
Showing 88 changed files with 2905 additions and 196 deletions   Show diff stats
app/controllers/my_profile/manage_products_controller.rb
1 class ManageProductsController < ApplicationController 1 class ManageProductsController < ApplicationController
2 needs_profile 2 needs_profile
3 3
4 - protect 'manage_products', :profile 4 + protect 'manage_products', :profile, :except => [:show]
5 before_filter :check_environment_feature 5 before_filter :check_environment_feature
  6 + before_filter :login_required, :except => [:show]
6 7
7 protected 8 protected
  9 +
8 def check_environment_feature 10 def check_environment_feature
9 if profile.environment.enabled?('disable_products_for_enterprises') 11 if profile.environment.enabled?('disable_products_for_enterprises')
10 render_not_found 12 render_not_found
@@ -13,6 +15,7 @@ class ManageProductsController &lt; ApplicationController @@ -13,6 +15,7 @@ class ManageProductsController &lt; ApplicationController
13 end 15 end
14 16
15 public 17 public
  18 +
16 def index 19 def index
17 @products = @profile.products 20 @products = @profile.products
18 @consumptions = @profile.consumptions 21 @consumptions = @profile.consumptions
@@ -51,15 +54,34 @@ class ManageProductsController &lt; ApplicationController @@ -51,15 +54,34 @@ class ManageProductsController &lt; ApplicationController
51 end 54 end
52 55
53 def edit 56 def edit
54 - @object = @product = @profile.products.find(params[:id])  
55 - @current_category = @product.product_category  
56 - @categories = @current_category.nil? ? [] : @current_category.children 57 + @product = @profile.products.find(params[:id])
  58 + field = params[:field]
57 if request.post? 59 if request.post?
58 - if @product.update_attributes(params[:product])  
59 - flash[:notice] = _('Product succesfully updated')  
60 - redirect_back_or_default :action => 'show', :id => @product  
61 - else 60 + begin
  61 + @product.update_attributes!(params[:product])
  62 + render :partial => "display_#{field}", :locals => {:product => @product}
  63 + rescue Exception => e
  64 + render :partial => "edit_#{field}", :locals => {:product => @product, :errors => true}
  65 + end
  66 + else
  67 + render :partial => "edit_#{field}", :locals => {:product => @product, :errors => false}
  68 + end
  69 + end
  70 +
  71 + def edit_category
  72 + @product = @profile.products.find(params[:id])
  73 + @category = @product.product_category
  74 + @categories = ProductCategory.top_level_for(environment)
  75 + @edit = true
  76 + @level = @category.level
  77 + if request.post?
  78 + begin
  79 + @product.update_attributes!(params[:product])
  80 + render :partial => 'shared/redirect_via_javascript',
  81 + :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) }
  82 + rescue Exception => e
62 flash[:notice] = _('Could not update the product') 83 flash[:notice] = _('Could not update the product')
  84 + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' }
63 end 85 end
64 end 86 end
65 end 87 end
app/controllers/public/catalog_controller.rb
1 class CatalogController < PublicController 1 class CatalogController < PublicController
2 needs_profile 2 needs_profile
  3 +
3 before_filter :check_enterprise_and_environment 4 before_filter :check_enterprise_and_environment
4 5
5 def index 6 def index
6 @products = @profile.products 7 @products = @profile.products
7 end 8 end
8 9
9 - def show  
10 - @product = @profile.products.find(params[:id])  
11 - end  
12 -  
13 protected 10 protected
14 def check_enterprise_and_environment 11 def check_enterprise_and_environment
15 unless @profile.kind_of?(Enterprise) && !@profile.environment.enabled?('disable_products_for_enterprises') 12 unless @profile.kind_of?(Enterprise) && !@profile.environment.enabled?('disable_products_for_enterprises')
app/helpers/application_helper.rb
@@ -907,7 +907,7 @@ module ApplicationHelper @@ -907,7 +907,7 @@ module ApplicationHelper
907 (@section ? @section.title + ' - ' : '') + 907 (@section ? @section.title + ' - ' : '') +
908 (@toc ? _('Online Manual') + ' - ' : '') + 908 (@toc ? _('Online Manual') + ' - ' : '') +
909 environment.name + 909 environment.name +
910 - (@category ? "&rarr; #{@category.full_name}" : '') 910 + (@category ? " - #{@category.full_name}" : '')
911 end 911 end
912 912
913 def noosfero_javascript 913 def noosfero_javascript
@@ -921,6 +921,7 @@ module ApplicationHelper @@ -921,6 +921,7 @@ module ApplicationHelper
921 'lightbox', 921 'lightbox',
922 'colorpicker', 922 'colorpicker',
923 pngfix_stylesheet_path, 923 pngfix_stylesheet_path,
  924 + 'jquery.ui/sunny/jquery-ui-1.8.2.custom',
924 ] 925 ]
925 end 926 end
926 927
@@ -960,13 +961,16 @@ module ApplicationHelper @@ -960,13 +961,16 @@ module ApplicationHelper
960 text_field_tag(name, value, options.merge(:class => 'colorpicker_field')) 961 text_field_tag(name, value, options.merge(:class => 'colorpicker_field'))
961 end 962 end
962 963
963 - # for now force currency to Brazillian format, like: "12.345,20"  
964 - def float_to_currency(price)  
965 - number_to_currency(price, :unit => 'R$', :separator => ',', :delimiter => '.')  
966 - end  
967 -  
968 def ui_icon(icon_type) 964 def ui_icon(icon_type)
969 "<span class='ui-icon ui-icon-#{icon_type}' style='float:left; margin-right:7px;'></span>" 965 "<span class='ui-icon ui-icon-#{icon_type}' style='float:left; margin-right:7px;'></span>"
970 end 966 end
971 967
  968 + def ui_button(label, url, html_options = {})
  969 + link_to(label, url, html_options.merge(:class => 'ui_button fg-button'))
  970 + end
  971 +
  972 + def ui_button_to_remote(label, options, html_options = {})
  973 + link_to_remote(label, options, html_options.merge(:class => 'ui_button fg-button'))
  974 + end
  975 +
972 end 976 end
app/helpers/catalog_helper.rb
1 module CatalogHelper 1 module CatalogHelper
2 2
3 include DisplayHelper 3 include DisplayHelper
  4 +include ManageProductsHelper
4 5
5 def display_products_list(profile, products) 6 def display_products_list(profile, products)
6 data = '' 7 data = ''
@@ -19,7 +20,7 @@ include DisplayHelper @@ -19,7 +20,7 @@ include DisplayHelper
19 content_tag('h1', _('Products/Services')) + content_tag('ul', data, :id => 'product_list') 20 content_tag('h1', _('Products/Services')) + content_tag('ul', data, :id => 'product_list')
20 end 21 end
21 22
22 -private 23 + private
23 24
24 def product_category_name(profile, product_category) 25 def product_category_name(profile, product_category)
25 if profile.enabled? 26 if profile.enabled?
app/helpers/display_helper.rb
@@ -2,7 +2,7 @@ module DisplayHelper @@ -2,7 +2,7 @@ module DisplayHelper
2 2
3 def link_to_product(product, opts={}) 3 def link_to_product(product, opts={})
4 return _('No product') unless product 4 return _('No product') unless product
5 - target = product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'catalog', :action => 'show', :id => product) : product.enterprise.url 5 + target = product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => product) : product.enterprise.url
6 link_to content_tag( 'span', product.name ), 6 link_to content_tag( 'span', product.name ),
7 target, 7 target,
8 opts 8 opts
app/helpers/manage_products_helper.rb
@@ -49,12 +49,52 @@ module ManageProductsHelper @@ -49,12 +49,52 @@ module ManageProductsHelper
49 hierarchy.reverse.join(options[:separator] || ' &rarr; ') 49 hierarchy.reverse.join(options[:separator] || ' &rarr; ')
50 end 50 end
51 51
52 - def options_for_select_categories(categories) 52 + def options_for_select_categories(categories, selected = nil)
53 categories.sort_by{|cat| cat.name.transliterate}.map do |category| 53 categories.sort_by{|cat| cat.name.transliterate}.map do |category|
54 - "<option value='#{category.id}'>#{category.name + (category.leaf? ? '': ' &raquo;')}</option>" 54 + selected_attribute = selected.nil? ? '' : (category == selected ? "selected='selected'" : '')
  55 + "<option value='#{category.id}' #{selected_attribute}>#{category.name + (category.leaf? ? '': ' &raquo;')}</option>"
55 end.join("\n") 56 end.join("\n")
56 end 57 end
57 58
  59 + def build_selects_for_ancestors(ancestors, last_level)
  60 + current_category = ancestors.shift
  61 + if current_category.nil?
  62 + content_tag('div', '<!-- no categories -->',
  63 + :class => 'categories_container',
  64 + :id => "categories_container_level#{ last_level }"
  65 + )
  66 + else
  67 + content_tag('div',
  68 + select_tag('category_id',
  69 + options_for_select_categories(current_category.siblings + [current_category], current_category),
  70 + :size => 10,
  71 + :onchange => remote_function_to_update_categories_selection("categories_container_level#{ current_category.level + 1 }", :with => "'category_id=' + this.value")
  72 + ) +
  73 + build_selects_for_ancestors(ancestors, last_level),
  74 + :class => 'categories_container',
  75 + :id => "categories_container_level#{ current_category.level }"
  76 + )
  77 + end
  78 + end
  79 +
  80 + def selects_for_all_ancestors(current_category)
  81 + build_selects_for_ancestors(current_category.ancestors.reverse + [current_category], current_category.level + 1)
  82 + end
  83 +
  84 + def select_for_new_category
  85 + content_tag('div',
  86 + render(:partial => 'categories_for_selection'),
  87 + :class => 'categories_container',
  88 + :id => 'categories_container_level0'
  89 + )
  90 + end
  91 +
  92 + def categories_container(field_id_html, categories_selection_html, hierarchy_html = '')
  93 + field_id_html +
  94 + content_tag('div', hierarchy_html, :id => 'hierarchy_navigation') +
  95 + content_tag('div', categories_selection_html, :id => 'categories_container_wrapper')
  96 + end
  97 +
58 def select_for_categories(categories, level = 0) 98 def select_for_categories(categories, level = 0)
59 if categories.empty? 99 if categories.empty?
60 content_tag('div', '', :id => 'no_subcategories') 100 content_tag('div', '', :id => 'no_subcategories')
@@ -68,4 +108,98 @@ module ManageProductsHelper @@ -68,4 +108,98 @@ module ManageProductsHelper
68 end 108 end
69 end 109 end
70 110
  111 + def edit_product_link(product, field, label, html_options = {})
  112 + return '' unless (user && user.has_permission?('manage_products', profile))
  113 + options = html_options.merge(:id => 'link-edit-product-' + field)
  114 +
  115 + link_to_remote(label,
  116 + {:update => "product-#{field}",
  117 + :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field },
  118 + :method => :get},
  119 + options)
  120 + end
  121 +
  122 + def edit_product_button(product, field, label, html_options = {})
  123 + the_class = 'button with-text icon-edit'
  124 + if html_options.has_key?(:class)
  125 + the_class << ' ' << html_options[:class]
  126 + end
  127 + edit_product_link(product, field, label, html_options.merge(:class => the_class))
  128 + end
  129 +
  130 + def edit_product_ui_button(product, field, label, html_options = {})
  131 + return '' unless (user && user.has_permission?('manage_products', profile))
  132 + options = html_options.merge(:id => 'edit-product-button-ui-' + field)
  133 +
  134 + ui_button_to_remote(label,
  135 + {:update => "product-#{field}",
  136 + :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field },
  137 + :complete => "$('edit-product-button-ui-#{field}').hide()",
  138 + :method => :get},
  139 + options)
  140 + end
  141 +
  142 + def cancel_edit_product_link(product, field, html_options = {})
  143 + return '' unless (user && user.has_permission?('manage_products', profile))
  144 + button_to_function(:cancel, _('Cancel'), nil, html_options) do |page|
  145 + page.replace_html "product-#{field}", :partial => "display_#{field}", :locals => {:product => product}
  146 + end
  147 + end
  148 +
  149 + def edit_product_category_link(product, html_options = {})
  150 + return '' unless (user && user.has_permission?('manage_products', profile))
  151 + options = html_options.merge(:id => 'link-edit-product-category')
  152 + link_to(_('Change category'), { :action => 'edit_category', :id => product.id}, options)
  153 + end
  154 +
  155 + def float_to_currency(value)
  156 + number_to_currency(value, :unit => environment.currency_unit, :separator => environment.currency_separator, :delimiter => environment.currency_delimiter, :format => "%u %n")
  157 + end
  158 +
  159 + def display_value(product)
  160 + price = product.price
  161 + unless price.blank? || price.zero?
  162 + unit = product.unit
  163 + return '' if unit.blank?
  164 + discount = product.discount
  165 + if discount.blank? || discount.zero?
  166 + display_price(_('Price: '), price, unit)
  167 + else
  168 + display_price_with_discount(price, unit, product.price_with_discount)
  169 + end
  170 + else
  171 + ''
  172 + end
  173 + end
  174 +
  175 + def display_price(label, price, unit)
  176 + content_tag('span', label, :class => 'field-name') +
  177 + content_tag('span', float_to_currency(price), :class => 'field-value') +
  178 + ' (%s)' % unit
  179 + end
  180 +
  181 + def display_price_with_discount(price, unit, price_with_discount)
  182 + original_value = content_tag('span', float_to_currency(price), :class => 'field-value')
  183 + discount_value = display_price(_('On sale: '), price_with_discount, unit)
  184 + content_tag('span', _('List price: '), :class => 'field-name') + original_value + "<p/>" + discount_value
  185 + end
  186 +
  187 + def display_qualifiers(product)
  188 + data = ''
  189 + product.product_qualifiers.each do |pq|
  190 + certified_by = pq.certifier ? _(' certified by %s') % link_to(pq.certifier.name, pq.certifier.link) : ''
  191 + data << content_tag('li', '✔ ' + pq.qualifier.name + certified_by, :class => 'product-qualifiers-item')
  192 + end
  193 + content_tag('ul', data, :id => 'product-qualifiers')
  194 + end
  195 +
  196 + def checkboxes_qualifiers(product, qualifier)
  197 + check_box_tag("product[qualifiers_list][#{qualifier.id}][qualifier_id]", qualifier.id, product.qualifiers.include?(qualifier)) + qualifier.name
  198 + end
  199 +
  200 + def select_certifiers(product, qualifier, certifiers)
  201 + relation = product.product_qualifiers.find(:first, :conditions => {:qualifier_id => qualifier.id})
  202 + selected = relation.nil? ? 0 : relation.certifier_id
  203 + select_tag("product[qualifiers_list][#{qualifier.id}][certifier_id]", options_for_select([[_('Select...') , 0 ]] + certifiers.map {|c|[c.name, c.id]}, selected))
  204 + end
71 end 205 end
app/models/certifier.rb 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +class Certifier < ActiveRecord::Base
  2 +
  3 + belongs_to :environment
  4 +
  5 + def link
  6 + self[:link] || ''
  7 + end
  8 +end
app/models/environment.rb
@@ -152,6 +152,9 @@ class Environment &lt; ActiveRecord::Base @@ -152,6 +152,9 @@ class Environment &lt; ActiveRecord::Base
152 152
153 has_many :roles 153 has_many :roles
154 154
  155 + has_many :qualifiers
  156 + has_many :certifiers
  157 +
155 acts_as_accessible 158 acts_as_accessible
156 159
157 def superior_intances 160 def superior_intances
@@ -209,6 +212,10 @@ class Environment &lt; ActiveRecord::Base @@ -209,6 +212,10 @@ class Environment &lt; ActiveRecord::Base
209 settings_items :help_message_to_add_enterprise, :type => String, :default => '' 212 settings_items :help_message_to_add_enterprise, :type => String, :default => ''
210 settings_items :tip_message_enterprise_activation_question, :type => String, :default => '' 213 settings_items :tip_message_enterprise_activation_question, :type => String, :default => ''
211 214
  215 + settings_items :currency_unit, :type => String, :default => '$'
  216 + settings_items :currency_separator, :type => String, :default => '.'
  217 + settings_items :currency_delimiter, :type => String, :default => ','
  218 +
212 def news_amount_by_folder=(amount) 219 def news_amount_by_folder=(amount)
213 settings[:news_amount_by_folder] = amount.to_i 220 settings[:news_amount_by_folder] = amount.to_i
214 end 221 end
app/models/product.rb
@@ -2,10 +2,14 @@ class Product &lt; ActiveRecord::Base @@ -2,10 +2,14 @@ class Product &lt; ActiveRecord::Base
2 belongs_to :enterprise 2 belongs_to :enterprise
3 belongs_to :product_category 3 belongs_to :product_category
4 has_many :product_categorizations 4 has_many :product_categorizations
  5 + has_many :product_qualifiers
  6 + has_many :qualifiers, :through => :product_qualifiers
5 7
6 validates_uniqueness_of :name, :scope => :enterprise_id, :allow_nil => true 8 validates_uniqueness_of :name, :scope => :enterprise_id, :allow_nil => true
7 validates_presence_of :product_category 9 validates_presence_of :product_category
  10 +
8 validates_numericality_of :price, :allow_nil => true 11 validates_numericality_of :price, :allow_nil => true
  12 + validates_numericality_of :discount, :allow_nil => true
9 13
10 after_update :save_image 14 after_update :save_image
11 15
@@ -31,10 +35,36 @@ class Product &lt; ActiveRecord::Base @@ -31,10 +35,36 @@ class Product &lt; ActiveRecord::Base
31 35
32 acts_as_searchable :fields => [ :name, :description, :category_full_name ] 36 acts_as_searchable :fields => [ :name, :description, :category_full_name ]
33 37
34 - xss_terminate :only => [ :name, :description ], :on => 'validation' 38 + xss_terminate :only => [ :name ], :on => 'validation'
  39 + xss_terminate :only => [ :description ], :with => 'white_list', :on => 'validation'
35 40
36 acts_as_mappable 41 acts_as_mappable
37 - 42 +
  43 + def self.units
  44 + {
  45 + _('Litre') => 'litre',
  46 + _('Kilo') => 'kilo',
  47 + _('Meter') => 'meter',
  48 + _('Unit') => 'unit',
  49 + }
  50 + end
  51 +
  52 + def name
  53 + self[:name].blank? ? category_name : self[:name]
  54 + end
  55 +
  56 + def name=(value)
  57 + if (value == category_name)
  58 + self[:name] = nil
  59 + else
  60 + self[:name] = value
  61 + end
  62 + end
  63 +
  64 + def default_image(size='thumb')
  65 + '/images/icons-app/product-default-pic-%s.png' % size
  66 + end
  67 +
38 def category_full_name 68 def category_full_name
39 product_category ? product_category.full_name.split('/') : nil 69 product_category ? product_category.full_name.split('/') : nil
40 end 70 end
@@ -60,13 +90,21 @@ class Product &lt; ActiveRecord::Base @@ -60,13 +90,21 @@ class Product &lt; ActiveRecord::Base
60 end 90 end
61 91
62 def url 92 def url
63 - enterprise.public_profile_url.merge(:controller => 'catalog', :action => 'show', :id => id) 93 + enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => id)
64 end 94 end
65 95
66 def public? 96 def public?
67 enterprise.public_profile 97 enterprise.public_profile
68 end 98 end
69 99
  100 + def formatted_value(value)
  101 + ("%.2f" % self[value]).to_s.gsub('.', enterprise.environment.currency_separator) if self[value]
  102 + end
  103 +
  104 + def price_with_discount
  105 + price - discount if discount
  106 + end
  107 +
70 def price=(value) 108 def price=(value)
71 if value.is_a?(String) 109 if value.is_a?(String)
72 super(currency_to_float(value)) 110 super(currency_to_float(value))
@@ -75,6 +113,14 @@ class Product &lt; ActiveRecord::Base @@ -75,6 +113,14 @@ class Product &lt; ActiveRecord::Base
75 end 113 end
76 end 114 end
77 115
  116 + def discount=(value)
  117 + if value.is_a?(String)
  118 + super(currency_to_float(value))
  119 + else
  120 + super(value)
  121 + end
  122 + end
  123 +
78 def currency_to_float( num ) 124 def currency_to_float( num )
79 if num.count('.') == 1 && num.count(',') == 0 125 if num.count('.') == 1 && num.count(',') == 0
80 # number like "12.34" 126 # number like "12.34"
@@ -99,4 +145,17 @@ class Product &lt; ActiveRecord::Base @@ -99,4 +145,17 @@ class Product &lt; ActiveRecord::Base
99 return num.tr(',.','').to_f 145 return num.tr(',.','').to_f
100 end 146 end
101 147
  148 + def has_basic_info?
  149 + %w[description price].each do |field|
  150 + return true if !self.send(field).blank?
  151 + end
  152 + false
  153 + end
  154 +
  155 + def qualifiers_list=(qualifiers)
  156 + self.product_qualifiers.delete_all
  157 + qualifiers.each do |item|
  158 + self.product_qualifiers.create(item[1]) if item[1].has_key?('qualifier_id')
  159 + end
  160 + end
102 end 161 end
app/models/product_qualifier.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class ProductQualifier < ActiveRecord::Base
  2 + belongs_to :qualifier
  3 + belongs_to :product
  4 + belongs_to :certifier
  5 +end
app/models/products_block.rb
@@ -20,7 +20,7 @@ class ProductsBlock &lt; Block @@ -20,7 +20,7 @@ class ProductsBlock &lt; Block
20 block_title(title) + 20 block_title(title) +
21 content_tag( 21 content_tag(
22 'ul', 22 'ul',
23 - products.map {|product| content_tag('li', link_to(product.name, product.url, :style => 'background-image:url(%s)' % ( product.image ? product.image.public_filename(:minor) : '/images/icons-app/product-default-pic-minor.png' )), :class => 'product' )} 23 + products.map {|product| content_tag('li', link_to(product.name, product.url, :style => 'background-image:url(%s)' % ( product.image ? product.image.public_filename(:minor) : product.default_image('minor'))), :class => 'product' )}
24 ) 24 )
25 end 25 end
26 26
app/models/profile.rb
@@ -386,7 +386,7 @@ class Profile &lt; ActiveRecord::Base @@ -386,7 +386,7 @@ class Profile &lt; ActiveRecord::Base
386 386
387 def url_options 387 def url_options
388 options = { :host => default_hostname, :profile => (own_hostname ? nil : self.identifier) } 388 options = { :host => default_hostname, :profile => (own_hostname ? nil : self.identifier) }
389 - Noosfero.url_options.merge(options) 389 + options.merge(Noosfero.url_options.merge(options))
390 end 390 end
391 391
392 private :generate_url, :url_options 392 private :generate_url, :url_options
app/models/qualifier.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class Qualifier < ActiveRecord::Base
  2 +
  3 + belongs_to :environment
  4 +
  5 +end
app/views/catalog/show.rhtml
@@ -1,25 +0,0 @@ @@ -1,25 +0,0 @@
1 -<div id="show_product">  
2 -  
3 -<h1> <%= @product.name %> </h1>  
4 -  
5 -<%= image_tag @product.image.public_filename, :class => 'product-pic' if @product.image %>  
6 -  
7 -<div class="product_description">  
8 -<%= txt2html @product.description if @product.description %>  
9 -</div>  
10 -  
11 -<% if @product.price %>  
12 - <p class="product_price">  
13 - <%= _('Price: %s') % ( "<strong>%.2f</strong>" % @product.price) %>  
14 - </p>  
15 -<% end %>  
16 -  
17 -<p class="product_category">  
18 -<%= _('Category: %s ') % link_to_product_category(@product.product_category) %>  
19 -</p>  
20 -  
21 -<% button_bar do %>  
22 - <%= button :back, _("%s's products/services listing") % profile.name, :action => 'index', :id => nil %>  
23 -<% end %>  
24 -  
25 -</div>  
app/views/cms/_upload_file_form.rhtml
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 <% end %> 11 <% end %>
12 </div> 12 </div>
13 13
14 -<%= hidden_field_tag('back_to', @back_to) if @back_to %> 14 +<%= hidden_field_tag('back_to', @back_to) %>
15 15
16 <% button_bar do %> 16 <% button_bar do %>
17 <%= add_upload_file_field(_('More files'), {:size => size}) %> 17 <%= add_upload_file_field(_('More files'), {:size => size}) %>
app/views/layouts/_javascript.rhtml
1 -<%= javascript_include_tag :defaults, 'jquery-latest.js', 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'colorpicker', 'colorpicker-noosfero', 'reflection', :cache => 'cache-general' %> 1 +<%= javascript_include_tag :defaults, 'jquery-latest.js', 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.form.js', 'colorpicker', 'colorpicker-noosfero', 'reflection', :cache => 'cache-general' %>
app/views/layouts/application.rhtml
@@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
15 <%= 15 <%=
16 # Load the principal css files: 16 # Load the principal css files:
17 stylesheet_link_tag(noosfero_stylesheets, :cache => 'cache') + 17 stylesheet_link_tag(noosfero_stylesheets, :cache => 'cache') +
18 - stylesheet_link_tag('jquery.ui/smoothness/jquery-ui-1.8.2.custom') +  
19 stylesheet_import( %w( common help menu article button search blocks forms login-box ), 18 stylesheet_import( %w( common help menu article button search blocks forms login-box ),
20 :themed_source => true ) + "\n" + 19 :themed_source => true ) + "\n" +
21 import_blocks_stylesheets(:themed_source => true) + "\n" + 20 import_blocks_stylesheets(:themed_source => true) + "\n" +
@@ -126,5 +125,10 @@ @@ -126,5 +125,10 @@
126 app/views/shared/noosfero_layout_features.rhtml! %> 125 app/views/shared/noosfero_layout_features.rhtml! %>
127 <%= noosfero_layout_features %> 126 <%= noosfero_layout_features %>
128 127
  128 + <script type='text/javascript'>
  129 + // transform all element with class ui_button in a jQuery UI button
  130 + render_jquery_ui_buttons()
  131 + </script>
  132 +
129 </body> 133 </body>
130 </html> 134 </html>
app/views/manage_products/_display_category.rhtml 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +<div id='display-product-category'>
  2 + <p><%= hierarchy_category_navigation(@product.product_category, :make_links => false)%></p>
  3 + <%= edit_product_category_link(@product) %>
  4 +</div>
app/views/manage_products/_display_image.rhtml 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +<div id='display-product-image'>
  2 + <%= image_tag (@product.reload.image ? @product.image.public_filename : @product.default_image('thumb')), :class => 'product-pic' %>
  3 +</div>
  4 +
  5 +<%= edit_product_link(@product, 'image', _('Change image')) %>
app/views/manage_products/_display_info.rhtml 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +<%= render :file => 'shared/tiny_mce', :locals => {:mode => 'simple'} %>
  2 +<div id='display-product-info'>
  3 + <%= display_value(@product) %>
  4 + <%= display_qualifiers(@product) %>
  5 +</div>
  6 +
  7 +<div id='display-product-description'><%= @product.description %></div>
  8 +
  9 +<% if @product.has_basic_info? %>
  10 + <%= edit_product_button(@product, 'info', _('Edit basic information')) %>
  11 +<% else %>
  12 + <%= edit_product_ui_button(@product, 'info', _('Add description, price and other basic information'), :title => _('Click here to add description, price, discount and qualifiers/certifiers to make your product more attractive and detailed for the consumers'), 'data-primary-icon' => 'ui-icon-pencil', 'data-secondary-icon' => 'ui-icon-triangle-1-s') %>
  13 + <%= javascript_tag("render_jquery_ui_buttons('edit-product-button-ui-info')") %>
  14 +<% end%>
app/views/manage_products/_display_name.rhtml 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +<div id='display-product-name'>
  2 + <h2><%= @product.reload.name %></h2>
  3 + <%= edit_product_link(@product, 'name', _('Edit name')) %>
  4 +</div>
  5 +
app/views/manage_products/_edit_image.rhtml 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +<div id='display-product-image'>
  2 + <%= image_tag (@product.reload.image ? @product.image.public_filename : @product.default_image('thumb')), :class => 'product-pic' %>
  3 +</div>
  4 +
  5 +<% form_for(:product, :url => { :controller => 'manage_products', :action => 'edit', :id => @product, :field => 'image' }, :html => { :method => 'post', :id => 'uploadForm', :multipart => true}) do |f| %>
  6 + <% f.fields_for :image_builder, @product.image do |i| %>
  7 + <%= i.file_field( :uploaded_data, { :size => 10 } ) %>
  8 + <p><%= _("Max size: %s (.jpg, .gif, .png)")% Image.max_size.to_humanreadable %></p>
  9 + <% end %>
  10 +
  11 + <%= submit_button 'save', _('Save') %>
  12 + <%= cancel_edit_product_link(@product, 'image') %>
  13 +<% end %>
  14 +
  15 +<script type="text/javascript">
  16 + jQuery("#uploadForm").ajaxForm({
  17 + beforeSubmit: function(a,f,o) {
  18 + o.loading = open_loading('<%= _('loading... ') %>');
  19 + o.loaded = close_loading();
  20 + },
  21 + target: '#product-image'
  22 + });
  23 +</script>
  24 +
  25 +<% if errors %>
  26 + <%= render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' } %>
  27 +<% end %>
app/views/manage_products/_edit_info.rhtml 0 → 100644
@@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
  1 +<% if errors %>
  2 + <%= render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' } %>
  3 +<% end %>
  4 +
  5 +<%= render :file => 'shared/tiny_mce', :locals => {:mode => 'simple'} %>
  6 +<% remote_form_for(@product,
  7 + :loading => "open_loading('#{ _('loading...') }')",
  8 + :loaded => 'close_loading()',
  9 + :before => ("tinyMCE.triggerSave()" unless Rails.env == 'test'),
  10 + :update => 'product-info',
  11 + :url => {:controller => 'manage_products', :action => 'edit', :id => @product, :field => 'info'},
  12 + :html => {:id => 'product-info-form', :method => 'post'}) do |f| %>
  13 + <%= labelled_form_field(_('Description:'), f.text_area(:description, :rows => 15, :style => 'width: 90%;')) %>
  14 + <%= labelled_form_field(_('Price (%s):') % environment.currency_unit, f.text_field(:price, :value => @product.formatted_value(:price), :onKeyPress => "return numbersonly(event, '#{environment.currency_separator}');")) %>
  15 + <%= labelled_form_field(_('Unit:'), f.select(:unit, Product.units)) %>
  16 + <%= labelled_form_field(_('Discount (%s):') % environment.currency_unit, f.text_field(:discount, :value => @product.formatted_value(:discount), :onKeyPress => "return numbersonly(event, '#{environment.currency_separator}');")) %>
  17 + <div class='formlabel'><%= _('Available:') %></div>
  18 + <div class='formfield'>
  19 + <%= labelled_radio_button( _('Yes'), 'product[available]', 'true', @product.available) %>
  20 + <%= labelled_radio_button( _('No'), 'product[available]', 'false', !@product.available) %>
  21 + </div>
  22 +
  23 +
  24 + <% if !environment.qualifiers.empty? %>
  25 + <table id='product-qualifiers-list'>
  26 + <tr>
  27 + <th><%= _('Qualifier') %></th>
  28 + <th><%= _('Certifier') %></th>
  29 + </tr>
  30 + <% environment.qualifiers.each do |qualifier| %>
  31 + <tr>
  32 + <td>
  33 + <%= checkboxes_qualifiers(@product, qualifier) %>
  34 + </td>
  35 + <td>
  36 + <%= select_certifiers(@product, qualifier, environment.certifiers) %>
  37 + </td>
  38 + </tr>
  39 + <% end %>
  40 + </table>
  41 + <% end %>
  42 +
  43 + <% button_bar do %>
  44 + <%= submit_button :save, _('Save') %>
  45 + <%= cancel_edit_product_link(@product, 'info') %>
  46 + <% end %>
  47 +<% end %>
  48 +
  49 +<br style='clear:both'>
app/views/manage_products/_edit_name.rhtml 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +<% remote_form_for(@product,
  2 + :loading => "open_loading('#{ _('loading...') }')",
  3 + :loaded => 'close_loading()',
  4 + :update => 'product-name',
  5 + :url => {:controller => 'manage_products', :action => 'edit', :id => @product, :field => 'name'},
  6 + :html => {:method => 'post'}) do |f| %>
  7 + <%= f.text_field(:name, :value => @product.name, :class => 'name_edition') %>
  8 +
  9 + <% button_bar do %>
  10 + <%= submit_button :save, _('Save') %>
  11 + <%= cancel_edit_product_link(@product, 'name') %>
  12 + <% end %>
  13 +<% end %>
  14 +
  15 +<script type="text/javascript">
  16 + jQuery('#product_name').focus().select();
  17 +</script>
  18 +
  19 +<% if errors %>
  20 + <%= render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' } %>
  21 +<% end %>
app/views/manage_products/edit_category.rhtml 0 → 100644
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
  1 +<div class=product-category-hierarchy>
  2 + <%= hierarchy_category_navigation(@product.product_category, :make_links => false)%>
  3 +</div>
  4 +
  5 +<div id="category-product-edition">
  6 +
  7 + <h2><%= @product.name %></h2>
  8 +
  9 + <div id='request_result_message' style='display: none'></div>
  10 +
  11 + <% remote_form_for(:product, @product,
  12 + :loading => "open_loading('#{ _('loading...') }')",
  13 + :loaded => "close_loading()",
  14 + :update => "request_result_message",
  15 + :url => {:action => 'edit_category', :id => @product, :field => 'category'},
  16 + :html => {:method => 'post', :id => 'product_category_form'}) do |f| %>
  17 +
  18 + <h3><%= _('Edit category of this product:') %></h3>
  19 +
  20 + <%= categories_container(f.hidden_field('product_category_id'), selects_for_all_ancestors(@category), hierarchy_category_navigation(@category, :make_links => true)) %>
  21 +
  22 + <div id='categories_selection_actionbar'>
  23 + <%= button(:back, _('Back to product'), :action => 'show', :id => @product) %>
  24 + <span id='save_and_continue_wrapper'>
  25 + <%= submit_button(:save, _('Save and continue'), :id => 'save_and_continue') %>
  26 + <span class='tooltip' id='save_and_continue_disabled_tooltip'>
  27 + <%= ui_icon(:alert) %>
  28 + <%= _('This category does not allow registration of products, select a more specific category') %>
  29 + </span>
  30 + </span>
  31 + </div>
  32 +
  33 + <% end %>
  34 +</div>
app/views/manage_products/index.rhtml
@@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
16 <td><strong><%= link_to product.name, :action => 'show', :id => product %></strong></td> 16 <td><strong><%= link_to product.name, :action => 'show', :id => product %></strong></td>
17 <td><%= product.price %></td> 17 <td><%= product.price %></td>
18 <td> 18 <td>
19 - <%= button :edit, _('Edit'), :action => 'edit', :id => product %>  
20 <%= button :delete, _('Destroy'), :action => 'destroy', :id => product %> 19 <%= button :delete, _('Destroy'), :action => 'destroy', :id => product %>
21 </td> 20 </td>
22 </tr> 21 </tr>
app/views/manage_products/new.rhtml
@@ -6,19 +6,11 @@ @@ -6,19 +6,11 @@
6 :loading => "open_loading('#{ _('loading...') }')", 6 :loading => "open_loading('#{ _('loading...') }')",
7 :update => "request_result_message", 7 :update => "request_result_message",
8 :url => {:action => 'new'}, 8 :url => {:action => 'new'},
9 - :html => {:method => 'post', :id => 'new_product_form'} do |f| %> 9 + :html => {:method => 'post', :id => 'product_category_form'} do |f| %>
10 10
11 <h3><%= _('Select the category of the new product or service') %></h3> 11 <h3><%= _('Select the category of the new product or service') %></h3>
12 12
13 - <%= f.hidden_field 'product_category_id' %>  
14 -  
15 - <div id='hierarchy_navigation'> </div>  
16 -  
17 - <div id='categories_container_wrapper'>  
18 - <div class='categories_container' id='categories_container_level0'>  
19 - <%= render :partial => 'categories_for_selection' %>  
20 - </div>  
21 - </div> 13 + <%= categories_container(f.hidden_field('product_category_id'), select_for_new_category) %>
22 14
23 <div id='categories_selection_actionbar'> 15 <div id='categories_selection_actionbar'>
24 <%= button :back, _('Back to the product listing'), :action => 'index' %> 16 <%= button :back, _('Back to the product listing'), :action => 'index' %>
app/views/manage_products/show.rhtml
1 -<h3> <%= @product.name %> </h3> 1 +<div id="product-category">
  2 + <%= render :partial => 'manage_products/display_category', :locals => {:product => @product} %>
  3 +</div>
2 4
3 -<p> <%= image_tag @product.image.public_filename if @product.image %> </p>  
4 -<p> <strong><%= _('Price:') %></strong> <%= @product.price %> </p>  
5 -<p> <strong><%= _('Description:') %></strong> <%= txt2html @product.description if @product.description %> </p>  
6 -<p> <strong><%= _('Category:') %></strong> <%= link_to_product_category(@product.product_category) %> </p> 5 +<div id="show_product">
7 6
8 -<%= button :back, _('Back'), :action => 'index' %>  
9 -&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  
10 -<%= button :delete, _('Destroy'), :action => 'destroy', :id => @product %>  
11 -<%= button :edit, _('Edit'), :action => 'edit', :id => @product %> 7 + <div id='product-name'>
  8 + <%= render :partial => 'manage_products/display_name', :locals => {:product => @product} %>
  9 + </div>
12 10
  11 + <div id='product-details'>
  12 + <div id='product-image'>
  13 + <%= render :partial => 'manage_products/display_image', :locals => {:product => @product} %>
  14 + </div>
  15 +
  16 + <div id='product-info'>
  17 + <%= render :partial => 'manage_products/display_info', :locals => {:product => @product} %>
  18 + </div>
  19 +
  20 + </div>
  21 +</div>
  22 +
  23 +<% button_bar do %>
  24 + <%= button :back, _('Back to the product listing'), :action => 'index' %>
  25 + <%= button :delete, _('Remove product or service'), {:action => 'destroy', :id => @product}, :class => 'requires-permission-manage_products', :style => 'display:none;' %>
  26 +<% end %>
app/views/shared/_dialog_error_messages.rhtml
1 -<%= error_messages_for object_name %>  
2 -  
3 <% javascript_tag do %> 1 <% javascript_tag do %>
4 close_loading() 2 close_loading()
5 jQuery('#errorExplanation h2').hide() 3 jQuery('#errorExplanation h2').hide()
@@ -11,3 +9,4 @@ @@ -11,3 +9,4 @@
11 title: "<%= ui_icon(:alert) %>Ops…", 9 title: "<%= ui_icon(:alert) %>Ops…",
12 }); 10 });
13 <% end %> 11 <% end %>
  12 +<%= error_messages_for object_name %>
app/views/shared/tiny_mce.rhtml
1 <%= javascript_include_tag 'tinymce/jscripts/tiny_mce/tiny_mce.js' %> 1 <%= javascript_include_tag 'tinymce/jscripts/tiny_mce/tiny_mce.js' %>
2 <script type="text/javascript"> 2 <script type="text/javascript">
3 var myplugins = "searchreplace,print,table"; 3 var myplugins = "searchreplace,print,table";
  4 + var first_line, second_line;
  5 + var mode = '<%= mode ||= false %>'
  6 + <% if mode %>
  7 + first_line = "fontsizeselect,bold,italic,underline,bullist,numlist,justifyleft,justifycenter,justifyright,link,unlink"
  8 + second_line = ""
  9 + <% else %>
  10 + first_line = "print,separator,copy,paste,separator,undo,redo,separator,search,replace,separator,fontsizeselect,formatselect"
  11 + second_line = "bold,italic,underline,strikethrough,separator,bullist,numlist,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,link,unlink,image,table,separator,cleanup,code"
  12 + <% end %>
  13 +
4 if (tinymce.isIE) { 14 if (tinymce.isIE) {
5 // the paste plugin is only useful in Internet Explorer 15 // the paste plugin is only useful in Internet Explorer
6 myplugins = "paste," + myplugins; 16 myplugins = "paste," + myplugins;
@@ -14,8 +24,8 @@ tinyMCE.init({ @@ -14,8 +24,8 @@ tinyMCE.init({
14 plugins: myplugins, 24 plugins: myplugins,
15 theme_advanced_toolbar_location : "top", 25 theme_advanced_toolbar_location : "top",
16 theme_advanced_layout_manager: 'SimpleLayout', 26 theme_advanced_layout_manager: 'SimpleLayout',
17 - theme_advanced_buttons1 : "print,separator,copy,paste,separator,undo,redo,separator,search,replace,separator,fontsizeselect,formatselect",  
18 - theme_advanced_buttons2 : "bold,italic,underline,strikethrough,separator,bullist,numlist,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,link,unlink,image,table,separator,cleanup,code", 27 + theme_advanced_buttons1 : first_line,
  28 + theme_advanced_buttons2 : second_line,
19 theme_advanced_buttons3 : "", 29 theme_advanced_buttons3 : "",
20 theme_advanced_blockformats :"p,address,pre,h2,h3,h4,h5,h6", 30 theme_advanced_blockformats :"p,address,pre,h2,h3,h4,h5,h6",
21 paste_auto_cleanup_on_paste : true, 31 paste_auto_cleanup_on_paste : true,
config/routes.rb
@@ -60,7 +60,6 @@ ActionController::Routing::Routes.draw do |map| @@ -60,7 +60,6 @@ ActionController::Routing::Routes.draw do |map|
60 60
61 # catalog 61 # catalog
62 map.catalog 'catalog/:profile', :controller => 'catalog', :action => 'index', :profile => /#{Noosfero.identifier_format}/ 62 map.catalog 'catalog/:profile', :controller => 'catalog', :action => 'index', :profile => /#{Noosfero.identifier_format}/
63 - map.product 'catalog/:profile/:id', :controller => 'catalog', :action => 'show', :profile => /#{Noosfero.identifier_format}/  
64 63
65 # invite 64 # invite
66 map.invite 'profile/:profile/invite/:action', :controller => 'invite', :profile => /#{Noosfero.identifier_format}/ 65 map.invite 'profile/:profile/invite/:action', :controller => 'invite', :profile => /#{Noosfero.identifier_format}/
db/migrate/20100618021603_add_columns_to_products.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +class AddColumnsToProducts < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :products, :unit, :string
  4 + add_column :products, :discount, :float
  5 + add_column :products, :available, :boolean
  6 + end
  7 +
  8 + def self.down
  9 + remove_column :products, :unit
  10 + remove_column :products, :discount
  11 + remove_column :products, :available
  12 + end
  13 +end
db/migrate/20100619114048_create_product_qualifiers.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +class CreateProductQualifiers < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :product_qualifiers do |t|
  4 + t.references :product
  5 + t.references :qualifier
  6 + t.references :certifier
  7 + t.timestamps
  8 + end
  9 + end
  10 +
  11 + def self.down
  12 + drop_table :product_qualifiers
  13 + end
  14 +end
db/migrate/20100619120420_create_qualifiers.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +class CreateQualifiers < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :qualifiers do |t|
  4 + t.string :name
  5 + t.references :environment
  6 +
  7 + t.timestamps
  8 + end
  9 + end
  10 +
  11 + def self.down
  12 + drop_table :qualifiers
  13 + end
  14 +end
db/migrate/20100619121512_create_certifiers.rb 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +class CreateCertifiers < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :certifiers do |t|
  4 + t.string :name
  5 + t.string :description
  6 + t.string :link
  7 + t.references :environment
  8 +
  9 + t.timestamps
  10 + end
  11 + end
  12 +
  13 + def self.down
  14 + drop_table :certifiers
  15 + end
  16 +end
@@ -141,6 +141,15 @@ ActiveRecord::Schema.define(:version =&gt; 20100621235235) do @@ -141,6 +141,15 @@ ActiveRecord::Schema.define(:version =&gt; 20100621235235) do
141 add_index "categories_profiles", ["category_id"], :name => "index_categories_profiles_on_category_id" 141 add_index "categories_profiles", ["category_id"], :name => "index_categories_profiles_on_category_id"
142 add_index "categories_profiles", ["profile_id"], :name => "index_categories_profiles_on_profile_id" 142 add_index "categories_profiles", ["profile_id"], :name => "index_categories_profiles_on_profile_id"
143 143
  144 + create_table "certifiers", :force => true do |t|
  145 + t.string "name"
  146 + t.string "description"
  147 + t.string "link"
  148 + t.integer "environment_id"
  149 + t.datetime "created_at"
  150 + t.datetime "updated_at"
  151 + end
  152 +
144 create_table "comments", :force => true do |t| 153 create_table "comments", :force => true do |t|
145 t.string "title" 154 t.string "title"
146 t.text "body" 155 t.text "body"
@@ -230,6 +239,14 @@ ActiveRecord::Schema.define(:version =&gt; 20100621235235) do @@ -230,6 +239,14 @@ ActiveRecord::Schema.define(:version =&gt; 20100621235235) do
230 add_index "product_categorizations", ["category_id"], :name => "index_product_categorizations_on_category_id" 239 add_index "product_categorizations", ["category_id"], :name => "index_product_categorizations_on_category_id"
231 add_index "product_categorizations", ["product_id"], :name => "index_product_categorizations_on_product_id" 240 add_index "product_categorizations", ["product_id"], :name => "index_product_categorizations_on_product_id"
232 241
  242 + create_table "product_qualifiers", :force => true do |t|
  243 + t.integer "product_id"
  244 + t.integer "qualifier_id"
  245 + t.integer "certifier_id"
  246 + t.datetime "created_at"
  247 + t.datetime "updated_at"
  248 + end
  249 +
233 create_table "products", :force => true do |t| 250 create_table "products", :force => true do |t|
234 t.integer "enterprise_id" 251 t.integer "enterprise_id"
235 t.integer "product_category_id" 252 t.integer "product_category_id"
@@ -241,6 +258,9 @@ ActiveRecord::Schema.define(:version =&gt; 20100621235235) do @@ -241,6 +258,9 @@ ActiveRecord::Schema.define(:version =&gt; 20100621235235) do
241 t.datetime "updated_at" 258 t.datetime "updated_at"
242 t.float "lat" 259 t.float "lat"
243 t.float "lng" 260 t.float "lng"
  261 + t.string "unit"
  262 + t.float "discount"
  263 + t.boolean "available"
244 t.boolean "highlighted" 264 t.boolean "highlighted"
245 end 265 end
246 266
@@ -276,6 +296,13 @@ ActiveRecord::Schema.define(:version =&gt; 20100621235235) do @@ -276,6 +296,13 @@ ActiveRecord::Schema.define(:version =&gt; 20100621235235) do
276 296
277 add_index "profiles", ["environment_id"], :name => "index_profiles_on_environment_id" 297 add_index "profiles", ["environment_id"], :name => "index_profiles_on_environment_id"
278 298
  299 + create_table "qualifiers", :force => true do |t|
  300 + t.string "name"
  301 + t.integer "environment_id"
  302 + t.datetime "created_at"
  303 + t.datetime "updated_at"
  304 + end
  305 +
279 create_table "refused_join_community", :id => false, :force => true do |t| 306 create_table "refused_join_community", :id => false, :force => true do |t|
280 t.integer "person_id" 307 t.integer "person_id"
281 t.integer "community_id" 308 t.integer "community_id"
features/manage_products.feature
@@ -10,18 +10,28 @@ Feature: manage products @@ -10,18 +10,28 @@ Feature: manage products
10 | identifier | owner | name | enabled | 10 | identifier | owner | name | enabled |
11 | redemoinho | joaosilva | Rede Moinho | true | 11 | redemoinho | joaosilva | Rede Moinho | true |
12 And feature "disable_products_for_enterprises" is disabled on environment 12 And feature "disable_products_for_enterprises" is disabled on environment
13 - And I am logged in as "joaosilva"  
14 - And I am on Rede Moinho's control panel  
15 - And I follow "Manage Products and Services"  
16 13
17 Scenario: listing products and services 14 Scenario: listing products and services
  15 + Given I am logged in as "joaosilva"
  16 + And I am on Rede Moinho's control panel
  17 + And I follow "Manage Products and Services"
18 Then I should see "Listing products and services" 18 Then I should see "Listing products and services"
19 19
  20 + Scenario: see button to back in categories hierarchy
  21 + Given I am logged in as "joaosilva"
  22 + And I am on Rede Moinho's control panel
  23 + And I follow "Manage Products and Services"
  24 + When I follow "New product or service"
  25 + Then I should see "Back to the product listing" link
  26 +
20 Scenario: see toplevel categories 27 Scenario: see toplevel categories
21 Given the following product_categories 28 Given the following product_categories
22 | name | 29 | name |
23 | Products | 30 | Products |
24 | Services | 31 | Services |
  32 + Given I am logged in as "joaosilva"
  33 + And I am on Rede Moinho's control panel
  34 + And I follow "Manage Products and Services"
25 When I follow "New product or service" 35 When I follow "New product or service"
26 Then I should see "Products" 36 Then I should see "Products"
27 And I should see "Service" 37 And I should see "Service"
@@ -35,6 +45,9 @@ Feature: manage products @@ -35,6 +45,9 @@ Feature: manage products
35 | name | parent | 45 | name | parent |
36 | Computers level1 | products-level0 | 46 | Computers level1 | products-level0 |
37 | DVDs level1 | products-level0 | 47 | DVDs level1 | products-level0 |
  48 + Given I am logged in as "joaosilva"
  49 + And I am on Rede Moinho's control panel
  50 + And I follow "Manage Products and Services"
38 When I follow "New product or service" 51 When I follow "New product or service"
39 And I select "Products level0 »" 52 And I select "Products level0 »"
40 Then I should see "Computers level1" 53 Then I should see "Computers level1"
@@ -50,6 +63,9 @@ Feature: manage products @@ -50,6 +63,9 @@ Feature: manage products
50 | name | parent | 63 | name | parent |
51 | Computers level1 | products-level0 | 64 | Computers level1 | products-level0 |
52 | Software development level1 | services-level0 | 65 | Software development level1 | services-level0 |
  66 + Given I am logged in as "joaosilva"
  67 + And I am on Rede Moinho's control panel
  68 + And I follow "Manage Products and Services"
53 When I follow "New product or service" 69 When I follow "New product or service"
54 And I select "Products level0 »" 70 And I select "Products level0 »"
55 And I select "Computers level1" 71 And I select "Computers level1"
@@ -65,6 +81,9 @@ Feature: manage products @@ -65,6 +81,9 @@ Feature: manage products
65 And the following product_category 81 And the following product_category
66 | name | parent | 82 | name | parent |
67 | Computers | products | 83 | Computers | products |
  84 + Given I am logged in as "joaosilva"
  85 + And I am on Rede Moinho's control panel
  86 + And I follow "Manage Products and Services"
68 When I follow "New product or service" 87 When I follow "New product or service"
69 And I select "Products »" 88 And I select "Products »"
70 And I select "Computers" 89 And I select "Computers"
@@ -78,6 +97,9 @@ Feature: manage products @@ -78,6 +97,9 @@ Feature: manage products
78 Given the following product_category 97 Given the following product_category
79 | name | parent | 98 | name | parent |
80 | Category Level 1 | toplevel-product-categories | 99 | Category Level 1 | toplevel-product-categories |
  100 + Given I am logged in as "joaosilva"
  101 + And I am on Rede Moinho's control panel
  102 + And I follow "Manage Products and Services"
81 When I follow "New product or service" 103 When I follow "New product or service"
82 And I select "Toplevel Product Categories »" 104 And I select "Toplevel Product Categories »"
83 And I select "Category Level 1" 105 And I select "Category Level 1"
@@ -89,6 +111,7 @@ Feature: manage products @@ -89,6 +111,7 @@ Feature: manage products
89 Given the following product_category 111 Given the following product_category
90 | name | 112 | name |
91 | Only for test | 113 | Only for test |
  114 + And I am logged in as "joaosilva"
92 When I go to /myprofile/redemoinho/manage_products/new 115 When I go to /myprofile/redemoinho/manage_products/new
93 Then the "#save_and_continue" button should not be enabled 116 Then the "#save_and_continue" button should not be enabled
94 117
@@ -97,6 +120,9 @@ Feature: manage products @@ -97,6 +120,9 @@ Feature: manage products
97 Given the following product_category 120 Given the following product_category
98 | name | 121 | name |
99 | Browsers (accept categories) | 122 | Browsers (accept categories) |
  123 + Given I am logged in as "joaosilva"
  124 + And I am on Rede Moinho's control panel
  125 + And I follow "Manage Products and Services"
100 When I follow "New product or service" 126 When I follow "New product or service"
101 And I select "Browsers (accept categories)" 127 And I select "Browsers (accept categories)"
102 Then the "Save and continue" button should be enabled 128 Then the "Save and continue" button should be enabled
@@ -106,6 +132,9 @@ Feature: manage products @@ -106,6 +132,9 @@ Feature: manage products
106 Given the following product_category 132 Given the following product_category
107 | name | accept_products | 133 | name | accept_products |
108 | Browsers | false | 134 | Browsers | false |
  135 + Given I am logged in as "joaosilva"
  136 + And I am on Rede Moinho's control panel
  137 + And I follow "Manage Products and Services"
109 When I follow "New product or service" 138 When I follow "New product or service"
110 And I select "Browsers" 139 And I select "Browsers"
111 Then the "#save_and_continue" button should not be enabled 140 Then the "#save_and_continue" button should not be enabled
@@ -115,16 +144,23 @@ Feature: manage products @@ -115,16 +144,23 @@ Feature: manage products
115 Given the following product_category 144 Given the following product_category
116 | name | 145 | name |
117 | Bicycle | 146 | Bicycle |
  147 + Given I am logged in as "joaosilva"
  148 + And I am on Rede Moinho's control panel
  149 + And I follow "Manage Products and Services"
118 When I follow "New product or service" 150 When I follow "New product or service"
119 And I select "Bicycle" 151 And I select "Bicycle"
120 And I press "Save and continue" 152 And I press "Save and continue"
121 - Then I should see "Category: Bicycle" 153 + Then I should see "Bicycle"
  154 + And I should see "Change category"
122 155
123 @selenium 156 @selenium
124 Scenario: stay on the same place after error on save 157 Scenario: stay on the same place after error on save
125 Given the following product_category 158 Given the following product_category
126 | name | 159 | name |
127 | Bicycle | 160 | Bicycle |
  161 + Given I am logged in as "joaosilva"
  162 + And I am on Rede Moinho's control panel
  163 + And I follow "Manage Products and Services"
128 And I follow "New product or service" 164 And I follow "New product or service"
129 And I select "Bicycle" 165 And I select "Bicycle"
130 And I press "Save and continue" 166 And I press "Save and continue"
@@ -134,3 +170,184 @@ Feature: manage products @@ -134,3 +170,184 @@ Feature: manage products
134 And I press "Save and continue" 170 And I press "Save and continue"
135 Then I should be on Rede Moinho's new product page 171 Then I should be on Rede Moinho's new product page
136 And I should see "Bicycle" 172 And I should see "Bicycle"
  173 +
  174 + Scenario: a user with permission can see edit links
  175 + Given the following product_category
  176 + | name |
  177 + | Bicycle |
  178 + And the following products
  179 + | owner | category | name | description |
  180 + | redemoinho | bicycle | Bike | Red bicycle |
  181 + And I am logged in as "joaosilva"
  182 + When I go to Rede Moinho's page of product Bike
  183 + Then I should see "Change category"
  184 + And I should see "Edit name"
  185 + And I should see "Edit basic information"
  186 + And I should see "Change image"
  187 +
  188 + Scenario: an allowed user will see a different button when has no basic info
  189 + Given the following product_category
  190 + | name |
  191 + | Bicycle |
  192 + And the following products
  193 + | owner | category | name |
  194 + | redemoinho | bicycle | Bike |
  195 + And I am logged in as "joaosilva"
  196 + When I go to Rede Moinho's page of product Bike
  197 + Then I should see "Change category"
  198 + And I should see "Edit name"
  199 + And I should see "Add description, price and other basic information"
  200 + And I should see "Change image"
  201 +
  202 + Scenario: a not logged user cannot see edit links
  203 + Given I am not logged in
  204 + And the following product_category
  205 + | name |
  206 + | Bicycle |
  207 + And the following products
  208 + | owner | category | name | description |
  209 + | redemoinho | bicycle | Bike | Red bicycle |
  210 + When I go to Rede Moinho's page of product Bike
  211 + Then I should not see "Change category"
  212 + And I should not see "Edit name"
  213 + And I should not see "Edit basic information"
  214 + And I should not see "Change image"
  215 +
  216 + Scenario: a not allowed user cannot see edit links
  217 + Given the following users
  218 + | login | name |
  219 + | mariasantos | Maria Santos |
  220 + And the following product_category
  221 + | name |
  222 + | Bicycle |
  223 + And the following products
  224 + | owner | category | name | description |
  225 + | redemoinho | bicycle | Bike | Red bicycle |
  226 + And I am logged in as "mariasantos"
  227 + When I go to Rede Moinho's page of product Bike
  228 + Then I should not see "Change category"
  229 + And I should not see "Edit name"
  230 + And I should not see "Edit basic information"
  231 + And I should not see "Change image"
  232 +
  233 + @selenium
  234 + Scenario: edit name of a product
  235 + Given the following product_category
  236 + | name |
  237 + | Bicycle |
  238 + And the following products
  239 + | owner | category | name |
  240 + | redemoinho | bicycle | Bike |
  241 + And I am logged in as "joaosilva"
  242 + When I go to Rede Moinho's page of product Bike
  243 + And I follow "Edit name"
  244 + And I fill in "product_name" with "Red bicycle"
  245 + And I press "Save"
  246 + Then I should see "Red bicycle"
  247 + And I should be on Rede Moinho's page of product Red bicycle
  248 +
  249 + @selenium
  250 + Scenario: cancel edition of a product name
  251 + Given the following product_category
  252 + | name |
  253 + | Bicycle |
  254 + And the following products
  255 + | owner | category | name |
  256 + | redemoinho | bicycle | Bike |
  257 + And I am logged in as "joaosilva"
  258 + When I go to Rede Moinho's page of product Bike
  259 + And I follow "Edit name"
  260 + When I follow "Cancel"
  261 + Then I should see "Bike"
  262 +
  263 + @selenium
  264 + Scenario: edit category of a product
  265 + Given the following product_category
  266 + | name |
  267 + | Eletronics |
  268 + And the following product_categories
  269 + | name | parent |
  270 + | Computers | eletronics |
  271 + | DVDs | eletronics |
  272 + And the following products
  273 + | owner | category | name |
  274 + | redemoinho | computers | Generic pc |
  275 + And I am logged in as "joaosilva"
  276 + When I go to Rede Moinho's page of product Generic pc
  277 + And I follow "Change category"
  278 + And I select "Eletronics »"
  279 + Then I select "DVDs"
  280 + And I press "Save and continue"
  281 + Then I should see "Eletronics → DVDs"
  282 +
  283 + @selenium
  284 + Scenario: cancel edition of a product category
  285 + Given the following product_category
  286 + | name |
  287 + | Eletronics |
  288 + And the following product_categories
  289 + | name | parent |
  290 + | Computers | eletronics |
  291 + | DVDs | eletronics |
  292 + And the following products
  293 + | owner | category | name |
  294 + | redemoinho | computers | Generic pc |
  295 + And I am logged in as "joaosilva"
  296 + When I go to Rede Moinho's page of product Generic pc
  297 + And I follow "Change category"
  298 + When I follow "Back to product"
  299 + Then I should see "Eletronics → Computers"
  300 +
  301 +
  302 + @selenium
  303 + Scenario: edit image of a product
  304 + Given the following product_category
  305 + | name |
  306 + | Eletronics |
  307 + And the following product_categories
  308 + | name | parent |
  309 + | Computers | eletronics |
  310 + | DVDs | eletronics |
  311 + And the following products
  312 + | owner | category | name |
  313 + | redemoinho | computers | Generic pc |
  314 + And I am logged in as "joaosilva"
  315 + When I go to Rede Moinho's page of product Generic pc
  316 + And I follow "Change image"
  317 + When I follow "Cancel"
  318 + Then I should be on Rede Moinho's page of product Generic pc
  319 +
  320 + # FIXME Not working because of tinyMCE plus selenium
  321 + # @selenium
  322 + # Scenario: edit description of a product
  323 + # Given the following product_category
  324 + # | name |
  325 + # | Bicycle |
  326 + # And the following products
  327 + # | owner | category | name | description |
  328 + # | redemoinho | bicycle | Bike | A new red bicycle |
  329 + # And I am logged in as "joaosilva"
  330 + # When I go to Rede Moinho's page of product Bike
  331 + # Then I should see "A new red bicycle"
  332 + # And I follow "Edit basic information"
  333 + # And I type in tinyMCE field "Description" the text "An used red bicycle"
  334 + # And I press "Save"
  335 + # Then I should not see "A new red bicycle"
  336 + # And I should see "An used red bicycle"
  337 + # And I should be on Rede Moinho's page of product Bike
  338 +
  339 + @selenium
  340 + Scenario: cancel edition of a product description
  341 + Given the following product_category
  342 + | name |
  343 + | Bicycle |
  344 + And the following products
  345 + | owner | category | name | description |
  346 + | redemoinho | bicycle | Bike | A new red bicycle |
  347 + And I am logged in as "joaosilva"
  348 + When I go to Rede Moinho's page of product Bike
  349 + Then I should see "A new red bicycle"
  350 + And I follow "Edit basic information"
  351 + When I follow "Cancel"
  352 + Then I should see "A new red bicycle"
  353 + And I should be on Rede Moinho's page of product Bike
features/step_definitions/noosfero_steps.rb
@@ -200,3 +200,5 @@ Given /^(.+) is disabled$/ do |enterprise_name| @@ -200,3 +200,5 @@ Given /^(.+) is disabled$/ do |enterprise_name|
200 enterprise.enabled = false 200 enterprise.enabled = false
201 enterprise.save 201 enterprise.save
202 end 202 end
  203 +
  204 +
features/support/paths.rb
@@ -54,6 +54,14 @@ module NavigationHelpers @@ -54,6 +54,14 @@ module NavigationHelpers
54 when /^(.+)'s new product page/ 54 when /^(.+)'s new product page/
55 '/myprofile/%s/manage_products/new' % Profile.find_by_name($1).identifier 55 '/myprofile/%s/manage_products/new' % Profile.find_by_name($1).identifier
56 56
  57 + when /^(.+)'s page of product (.*)$/
  58 + enterprise = Profile.find_by_name($1)
  59 + product = enterprise.products.find_by_name($2)
  60 + '/myprofile/%s/manage_products/show/%s' % [enterprise.identifier, product.id]
  61 +
  62 + when /^(.*)'s products page$/
  63 + '/catalog/%s' % Profile.find_by_name($1).identifier
  64 +
57 # Add more mappings here. 65 # Add more mappings here.
58 # Here is a more fancy example: 66 # Here is a more fancy example:
59 # 67 #
public/designs/templates/leftbar/stylesheets/style.css
1 .box-1 { 1 .box-1 {
2 - margin: 0 0 0 200px; 2 + margin: 0 30px 0 260px;
3 } 3 }
4 4
5 .box-1 .blocks { 5 .box-1 .blocks {
@@ -17,3 +17,7 @@ @@ -17,3 +17,7 @@
17 padding: 10px 0; 17 padding: 10px 0;
18 float: none; 18 float: none;
19 } 19 }
  20 +
  21 +#product-image {
  22 + margin-right: 0px;
  23 +}
public/designs/templates/rightbar/stylesheets/style.css
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 9
10 .box-2 { 10 .box-2 {
11 float: right; 11 float: right;
12 - width: 190px; 12 + width: 230px;
13 } 13 }
14 14
15 .block { 15 .block {
public/javascripts/application.js
@@ -138,3 +138,58 @@ function update_loading(message) { @@ -138,3 +138,58 @@ function update_loading(message) {
138 function redirect_to(url) { 138 function redirect_to(url) {
139 document.location=url; 139 document.location=url;
140 } 140 }
  141 +
  142 +/* Products edition */
  143 +
  144 +function numbersonly(e, separator) {
  145 + var key;
  146 + var keychar;
  147 +
  148 + if (window.event) {
  149 + key = window.event.keyCode;
  150 + }
  151 + else if (e) {
  152 + key = e.which;
  153 + }
  154 + else {
  155 + return true;
  156 + }
  157 + keychar = String.fromCharCode(key);
  158 +
  159 + if ((key==null) || (key==0) || (key==8) || (key==9) || (key==13) || (key==27) ) {
  160 + return true;
  161 + }
  162 + else if ((("0123456789").indexOf(keychar) > -1)) {
  163 + return true;
  164 + }
  165 + else if (keychar == separator) {
  166 + return true;
  167 + }
  168 + else
  169 + return false;
  170 +}
  171 +
  172 +// transform all element with class ui_button in a jQuery UI button
  173 +function render_jquery_ui_buttons(element_id) {
  174 + if (element_id) {
  175 + element_id = '#' + element_id
  176 + jQuery(element_id).button({
  177 + icons: {
  178 + primary: jQuery(element_id).attr('data-primary-icon'),
  179 + secondary: jQuery(element_id).attr('data-secondary-icon')
  180 + }
  181 + }
  182 + )
  183 + }
  184 + else {
  185 + jQuery('.ui_button').each(function() {
  186 + jQuery(this).button({
  187 + icons: {
  188 + primary: this.getAttribute('data-primary-icon'),
  189 + secondary: this.getAttribute('data-secondary-icon')
  190 + }
  191 + }
  192 + )
  193 + })
  194 + }
  195 +}
public/javascripts/jquery.form.js 0 → 100644
@@ -0,0 +1,675 @@ @@ -0,0 +1,675 @@
  1 +/*!
  2 + * jQuery Form Plugin
  3 + * version: 2.43 (12-MAR-2010)
  4 + * @requires jQuery v1.3.2 or later
  5 + *
  6 + * Examples and documentation at: http://malsup.com/jquery/form/
  7 + * Dual licensed under the MIT and GPL licenses:
  8 + * http://www.opensource.org/licenses/mit-license.php
  9 + * http://www.gnu.org/licenses/gpl.html
  10 + */
  11 +;(function($) {
  12 +
  13 +/*
  14 + Usage Note:
  15 + -----------
  16 + Do not use both ajaxSubmit and ajaxForm on the same form. These
  17 + functions are intended to be exclusive. Use ajaxSubmit if you want
  18 + to bind your own submit handler to the form. For example,
  19 +
  20 + $(document).ready(function() {
  21 + $('#myForm').bind('submit', function() {
  22 + $(this).ajaxSubmit({
  23 + target: '#output'
  24 + });
  25 + return false; // <-- important!
  26 + });
  27 + });
  28 +
  29 + Use ajaxForm when you want the plugin to manage all the event binding
  30 + for you. For example,
  31 +
  32 + $(document).ready(function() {
  33 + $('#myForm').ajaxForm({
  34 + target: '#output'
  35 + });
  36 + });
  37 +
  38 + When using ajaxForm, the ajaxSubmit function will be invoked for you
  39 + at the appropriate time.
  40 +*/
  41 +
  42 +/**
  43 + * ajaxSubmit() provides a mechanism for immediately submitting
  44 + * an HTML form using AJAX.
  45 + */
  46 +$.fn.ajaxSubmit = function(options) {
  47 + // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
  48 + if (!this.length) {
  49 + log('ajaxSubmit: skipping submit process - no element selected');
  50 + return this;
  51 + }
  52 +
  53 + if (typeof options == 'function')
  54 + options = { success: options };
  55 +
  56 + var url = $.trim(this.attr('action'));
  57 + if (url) {
  58 + // clean url (don't include hash vaue)
  59 + url = (url.match(/^([^#]+)/)||[])[1];
  60 + }
  61 + url = url || window.location.href || '';
  62 +
  63 + options = $.extend({
  64 + url: url,
  65 + type: this.attr('method') || 'GET',
  66 + iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
  67 + }, options || {});
  68 +
  69 + // hook for manipulating the form data before it is extracted;
  70 + // convenient for use with rich editors like tinyMCE or FCKEditor
  71 + var veto = {};
  72 + this.trigger('form-pre-serialize', [this, options, veto]);
  73 + if (veto.veto) {
  74 + log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
  75 + return this;
  76 + }
  77 +
  78 + // provide opportunity to alter form data before it is serialized
  79 + if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
  80 + log('ajaxSubmit: submit aborted via beforeSerialize callback');
  81 + return this;
  82 + }
  83 +
  84 + var a = this.formToArray(options.semantic);
  85 + if (options.data) {
  86 + options.extraData = options.data;
  87 + for (var n in options.data) {
  88 + if(options.data[n] instanceof Array) {
  89 + for (var k in options.data[n])
  90 + a.push( { name: n, value: options.data[n][k] } );
  91 + }
  92 + else
  93 + a.push( { name: n, value: options.data[n] } );
  94 + }
  95 + }
  96 +
  97 + // give pre-submit callback an opportunity to abort the submit
  98 + if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
  99 + log('ajaxSubmit: submit aborted via beforeSubmit callback');
  100 + return this;
  101 + }
  102 +
  103 + // fire vetoable 'validate' event
  104 + this.trigger('form-submit-validate', [a, this, options, veto]);
  105 + if (veto.veto) {
  106 + log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
  107 + return this;
  108 + }
  109 +
  110 + var q = $.param(a);
  111 +
  112 + if (options.type.toUpperCase() == 'GET') {
  113 + options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
  114 + options.data = null; // data is null for 'get'
  115 + }
  116 + else
  117 + options.data = q; // data is the query string for 'post'
  118 +
  119 + var $form = this, callbacks = [];
  120 + if (options.resetForm) callbacks.push(function() { $form.resetForm(); });
  121 + if (options.clearForm) callbacks.push(function() { $form.clearForm(); });
  122 +
  123 + // perform a load on the target only if dataType is not provided
  124 + if (!options.dataType && options.target) {
  125 + var oldSuccess = options.success || function(){};
  126 + callbacks.push(function(data) {
  127 + var fn = options.replaceTarget ? 'replaceWith' : 'html';
  128 + $(options.target)[fn](data).each(oldSuccess, arguments);
  129 + });
  130 + }
  131 + else if (options.success)
  132 + callbacks.push(options.success);
  133 +
  134 + options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
  135 + for (var i=0, max=callbacks.length; i < max; i++)
  136 + callbacks[i].apply(options, [data, status, xhr || $form, $form]);
  137 + };
  138 +
  139 + // are there files to upload?
  140 + var files = $('input:file', this).fieldValue();
  141 + var found = false;
  142 + for (var j=0; j < files.length; j++)
  143 + if (files[j])
  144 + found = true;
  145 +
  146 + var multipart = false;
  147 +// var mp = 'multipart/form-data';
  148 +// multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
  149 +
  150 + // options.iframe allows user to force iframe mode
  151 + // 06-NOV-09: now defaulting to iframe mode if file input is detected
  152 + if ((files.length && options.iframe !== false) || options.iframe || found || multipart) {
  153 + // hack to fix Safari hang (thanks to Tim Molendijk for this)
  154 + // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
  155 + if (options.closeKeepAlive)
  156 + $.get(options.closeKeepAlive, fileUpload);
  157 + else
  158 + fileUpload();
  159 + }
  160 + else
  161 + $.ajax(options);
  162 +
  163 + // fire 'notify' event
  164 + this.trigger('form-submit-notify', [this, options]);
  165 + return this;
  166 +
  167 +
  168 + // private function for handling file uploads (hat tip to YAHOO!)
  169 + function fileUpload() {
  170 + var form = $form[0];
  171 +
  172 + if ($(':input[name=submit]', form).length) {
  173 + alert('Error: Form elements must not be named "submit".');
  174 + return;
  175 + }
  176 +
  177 + var opts = $.extend({}, $.ajaxSettings, options);
  178 + var s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);
  179 +
  180 + var id = 'jqFormIO' + (new Date().getTime());
  181 + var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ opts.iframeSrc +'" onload="(jQuery(this).data(\'form-plugin-onload\'))()" />');
  182 + var io = $io[0];
  183 +
  184 + $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
  185 +
  186 + var xhr = { // mock object
  187 + aborted: 0,
  188 + responseText: null,
  189 + responseXML: null,
  190 + status: 0,
  191 + statusText: 'n/a',
  192 + getAllResponseHeaders: function() {},
  193 + getResponseHeader: function() {},
  194 + setRequestHeader: function() {},
  195 + abort: function() {
  196 + this.aborted = 1;
  197 + $io.attr('src', opts.iframeSrc); // abort op in progress
  198 + }
  199 + };
  200 +
  201 + var g = opts.global;
  202 + // trigger ajax global events so that activity/block indicators work like normal
  203 + if (g && ! $.active++) $.event.trigger("ajaxStart");
  204 + if (g) $.event.trigger("ajaxSend", [xhr, opts]);
  205 +
  206 + if (s.beforeSend && s.beforeSend(xhr, s) === false) {
  207 + s.global && $.active--;
  208 + return;
  209 + }
  210 + if (xhr.aborted)
  211 + return;
  212 +
  213 + var cbInvoked = false;
  214 + var timedOut = 0;
  215 +
  216 + // add submitting element to data if we know it
  217 + var sub = form.clk;
  218 + if (sub) {
  219 + var n = sub.name;
  220 + if (n && !sub.disabled) {
  221 + opts.extraData = opts.extraData || {};
  222 + opts.extraData[n] = sub.value;
  223 + if (sub.type == "image") {
  224 + opts.extraData[n+'.x'] = form.clk_x;
  225 + opts.extraData[n+'.y'] = form.clk_y;
  226 + }
  227 + }
  228 + }
  229 +
  230 + // take a breath so that pending repaints get some cpu time before the upload starts
  231 + function doSubmit() {
  232 + // make sure form attrs are set
  233 + var t = $form.attr('target'), a = $form.attr('action');
  234 +
  235 + // update form attrs in IE friendly way
  236 + form.setAttribute('target',id);
  237 + if (form.getAttribute('method') != 'POST')
  238 + form.setAttribute('method', 'POST');
  239 + if (form.getAttribute('action') != opts.url)
  240 + form.setAttribute('action', opts.url);
  241 +
  242 + // ie borks in some cases when setting encoding
  243 + if (! opts.skipEncodingOverride) {
  244 + $form.attr({
  245 + encoding: 'multipart/form-data',
  246 + enctype: 'multipart/form-data'
  247 + });
  248 + }
  249 +
  250 + // support timout
  251 + if (opts.timeout)
  252 + setTimeout(function() { timedOut = true; cb(); }, opts.timeout);
  253 +
  254 + // add "extra" data to form if provided in options
  255 + var extraInputs = [];
  256 + try {
  257 + if (opts.extraData)
  258 + for (var n in opts.extraData)
  259 + extraInputs.push(
  260 + $('<input type="hidden" name="'+n+'" value="'+opts.extraData[n]+'" />')
  261 + .appendTo(form)[0]);
  262 +
  263 + // add iframe to doc and submit the form
  264 + $io.appendTo('body');
  265 + $io.data('form-plugin-onload', cb);
  266 + form.submit();
  267 + }
  268 + finally {
  269 + // reset attrs and remove "extra" input elements
  270 + form.setAttribute('action',a);
  271 + t ? form.setAttribute('target', t) : $form.removeAttr('target');
  272 + $(extraInputs).remove();
  273 + }
  274 + };
  275 +
  276 + if (opts.forceSync)
  277 + doSubmit();
  278 + else
  279 + setTimeout(doSubmit, 10); // this lets dom updates render
  280 +
  281 + var domCheckCount = 100;
  282 +
  283 + function cb() {
  284 + if (cbInvoked)
  285 + return;
  286 +
  287 + var ok = true;
  288 + try {
  289 + if (timedOut) throw 'timeout';
  290 + // extract the server response from the iframe
  291 + var data, doc;
  292 +
  293 + doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
  294 +
  295 + var isXml = opts.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
  296 + log('isXml='+isXml);
  297 + if (!isXml && (doc.body == null || doc.body.innerHTML == '')) {
  298 + if (--domCheckCount) {
  299 + // in some browsers (Opera) the iframe DOM is not always traversable when
  300 + // the onload callback fires, so we loop a bit to accommodate
  301 + log('requeing onLoad callback, DOM not available');
  302 + setTimeout(cb, 250);
  303 + return;
  304 + }
  305 + log('Could not access iframe DOM after 100 tries.');
  306 + return;
  307 + }
  308 +
  309 + log('response detected');
  310 + cbInvoked = true;
  311 + xhr.responseText = doc.body ? doc.body.innerHTML : null;
  312 + xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
  313 + xhr.getResponseHeader = function(header){
  314 + var headers = {'content-type': opts.dataType};
  315 + return headers[header];
  316 + };
  317 +
  318 + if (opts.dataType == 'json' || opts.dataType == 'script') {
  319 + // see if user embedded response in textarea
  320 + var ta = doc.getElementsByTagName('textarea')[0];
  321 + if (ta)
  322 + xhr.responseText = ta.value;
  323 + else {
  324 + // account for browsers injecting pre around json response
  325 + var pre = doc.getElementsByTagName('pre')[0];
  326 + if (pre)
  327 + xhr.responseText = pre.innerHTML;
  328 + }
  329 + }
  330 + else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
  331 + xhr.responseXML = toXml(xhr.responseText);
  332 + }
  333 + data = $.httpData(xhr, opts.dataType);
  334 + }
  335 + catch(e){
  336 + log('error caught:',e);
  337 + ok = false;
  338 + xhr.error = e;
  339 + $.handleError(opts, xhr, 'error', e);
  340 + }
  341 +
  342 + // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
  343 + if (ok) {
  344 + opts.success(data, 'success');
  345 + if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
  346 + }
  347 + if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
  348 + if (g && ! --$.active) $.event.trigger("ajaxStop");
  349 + if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');
  350 +
  351 + // clean up
  352 + setTimeout(function() {
  353 + $io.removeData('form-plugin-onload');
  354 + $io.remove();
  355 + xhr.responseXML = null;
  356 + }, 100);
  357 + };
  358 +
  359 + function toXml(s, doc) {
  360 + if (window.ActiveXObject) {
  361 + doc = new ActiveXObject('Microsoft.XMLDOM');
  362 + doc.async = 'false';
  363 + doc.loadXML(s);
  364 + }
  365 + else
  366 + doc = (new DOMParser()).parseFromString(s, 'text/xml');
  367 + return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
  368 + };
  369 + };
  370 +};
  371 +
  372 +/**
  373 + * ajaxForm() provides a mechanism for fully automating form submission.
  374 + *
  375 + * The advantages of using this method instead of ajaxSubmit() are:
  376 + *
  377 + * 1: This method will include coordinates for <input type="image" /> elements (if the element
  378 + * is used to submit the form).
  379 + * 2. This method will include the submit element's name/value data (for the element that was
  380 + * used to submit the form).
  381 + * 3. This method binds the submit() method to the form for you.
  382 + *
  383 + * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
  384 + * passes the options argument along after properly binding events for submit elements and
  385 + * the form itself.
  386 + */
  387 +$.fn.ajaxForm = function(options) {
  388 + return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
  389 + e.preventDefault();
  390 + $(this).ajaxSubmit(options);
  391 + }).bind('click.form-plugin', function(e) {
  392 + var target = e.target;
  393 + var $el = $(target);
  394 + if (!($el.is(":submit,input:image"))) {
  395 + // is this a child element of the submit el? (ex: a span within a button)
  396 + var t = $el.closest(':submit');
  397 + if (t.length == 0)
  398 + return;
  399 + target = t[0];
  400 + }
  401 + var form = this;
  402 + form.clk = target;
  403 + if (target.type == 'image') {
  404 + if (e.offsetX != undefined) {
  405 + form.clk_x = e.offsetX;
  406 + form.clk_y = e.offsetY;
  407 + } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
  408 + var offset = $el.offset();
  409 + form.clk_x = e.pageX - offset.left;
  410 + form.clk_y = e.pageY - offset.top;
  411 + } else {
  412 + form.clk_x = e.pageX - target.offsetLeft;
  413 + form.clk_y = e.pageY - target.offsetTop;
  414 + }
  415 + }
  416 + // clear form vars
  417 + setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
  418 + });
  419 +};
  420 +
  421 +// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
  422 +$.fn.ajaxFormUnbind = function() {
  423 + return this.unbind('submit.form-plugin click.form-plugin');
  424 +};
  425 +
  426 +/**
  427 + * formToArray() gathers form element data into an array of objects that can
  428 + * be passed to any of the following ajax functions: $.get, $.post, or load.
  429 + * Each object in the array has both a 'name' and 'value' property. An example of
  430 + * an array for a simple login form might be:
  431 + *
  432 + * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
  433 + *
  434 + * It is this array that is passed to pre-submit callback functions provided to the
  435 + * ajaxSubmit() and ajaxForm() methods.
  436 + */
  437 +$.fn.formToArray = function(semantic) {
  438 + var a = [];
  439 + if (this.length == 0) return a;
  440 +
  441 + var form = this[0];
  442 + var els = semantic ? form.getElementsByTagName('*') : form.elements;
  443 + if (!els) return a;
  444 + for(var i=0, max=els.length; i < max; i++) {
  445 + var el = els[i];
  446 + var n = el.name;
  447 + if (!n) continue;
  448 +
  449 + if (semantic && form.clk && el.type == "image") {
  450 + // handle image inputs on the fly when semantic == true
  451 + if(!el.disabled && form.clk == el) {
  452 + a.push({name: n, value: $(el).val()});
  453 + a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
  454 + }
  455 + continue;
  456 + }
  457 +
  458 + var v = $.fieldValue(el, true);
  459 + if (v && v.constructor == Array) {
  460 + for(var j=0, jmax=v.length; j < jmax; j++)
  461 + a.push({name: n, value: v[j]});
  462 + }
  463 + else if (v !== null && typeof v != 'undefined')
  464 + a.push({name: n, value: v});
  465 + }
  466 +
  467 + if (!semantic && form.clk) {
  468 + // input type=='image' are not found in elements array! handle it here
  469 + var $input = $(form.clk), input = $input[0], n = input.name;
  470 + if (n && !input.disabled && input.type == 'image') {
  471 + a.push({name: n, value: $input.val()});
  472 + a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
  473 + }
  474 + }
  475 + return a;
  476 +};
  477 +
  478 +/**
  479 + * Serializes form data into a 'submittable' string. This method will return a string
  480 + * in the format: name1=value1&amp;name2=value2
  481 + */
  482 +$.fn.formSerialize = function(semantic) {
  483 + //hand off to jQuery.param for proper encoding
  484 + return $.param(this.formToArray(semantic));
  485 +};
  486 +
  487 +/**
  488 + * Serializes all field elements in the jQuery object into a query string.
  489 + * This method will return a string in the format: name1=value1&amp;name2=value2
  490 + */
  491 +$.fn.fieldSerialize = function(successful) {
  492 + var a = [];
  493 + this.each(function() {
  494 + var n = this.name;
  495 + if (!n) return;
  496 + var v = $.fieldValue(this, successful);
  497 + if (v && v.constructor == Array) {
  498 + for (var i=0,max=v.length; i < max; i++)
  499 + a.push({name: n, value: v[i]});
  500 + }
  501 + else if (v !== null && typeof v != 'undefined')
  502 + a.push({name: this.name, value: v});
  503 + });
  504 + //hand off to jQuery.param for proper encoding
  505 + return $.param(a);
  506 +};
  507 +
  508 +/**
  509 + * Returns the value(s) of the element in the matched set. For example, consider the following form:
  510 + *
  511 + * <form><fieldset>
  512 + * <input name="A" type="text" />
  513 + * <input name="A" type="text" />
  514 + * <input name="B" type="checkbox" value="B1" />
  515 + * <input name="B" type="checkbox" value="B2"/>
  516 + * <input name="C" type="radio" value="C1" />
  517 + * <input name="C" type="radio" value="C2" />
  518 + * </fieldset></form>
  519 + *
  520 + * var v = $(':text').fieldValue();
  521 + * // if no values are entered into the text inputs
  522 + * v == ['','']
  523 + * // if values entered into the text inputs are 'foo' and 'bar'
  524 + * v == ['foo','bar']
  525 + *
  526 + * var v = $(':checkbox').fieldValue();
  527 + * // if neither checkbox is checked
  528 + * v === undefined
  529 + * // if both checkboxes are checked
  530 + * v == ['B1', 'B2']
  531 + *
  532 + * var v = $(':radio').fieldValue();
  533 + * // if neither radio is checked
  534 + * v === undefined
  535 + * // if first radio is checked
  536 + * v == ['C1']
  537 + *
  538 + * The successful argument controls whether or not the field element must be 'successful'
  539 + * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
  540 + * The default value of the successful argument is true. If this value is false the value(s)
  541 + * for each element is returned.
  542 + *
  543 + * Note: This method *always* returns an array. If no valid value can be determined the
  544 + * array will be empty, otherwise it will contain one or more values.
  545 + */
  546 +$.fn.fieldValue = function(successful) {
  547 + for (var val=[], i=0, max=this.length; i < max; i++) {
  548 + var el = this[i];
  549 + var v = $.fieldValue(el, successful);
  550 + if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
  551 + continue;
  552 + v.constructor == Array ? $.merge(val, v) : val.push(v);
  553 + }
  554 + return val;
  555 +};
  556 +
  557 +/**
  558 + * Returns the value of the field element.
  559 + */
  560 +$.fieldValue = function(el, successful) {
  561 + var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
  562 + if (typeof successful == 'undefined') successful = true;
  563 +
  564 + if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
  565 + (t == 'checkbox' || t == 'radio') && !el.checked ||
  566 + (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
  567 + tag == 'select' && el.selectedIndex == -1))
  568 + return null;
  569 +
  570 + if (tag == 'select') {
  571 + var index = el.selectedIndex;
  572 + if (index < 0) return null;
  573 + var a = [], ops = el.options;
  574 + var one = (t == 'select-one');
  575 + var max = (one ? index+1 : ops.length);
  576 + for(var i=(one ? index : 0); i < max; i++) {
  577 + var op = ops[i];
  578 + if (op.selected) {
  579 + var v = op.value;
  580 + if (!v) // extra pain for IE...
  581 + v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
  582 + if (one) return v;
  583 + a.push(v);
  584 + }
  585 + }
  586 + return a;
  587 + }
  588 + return el.value;
  589 +};
  590 +
  591 +/**
  592 + * Clears the form data. Takes the following actions on the form's input fields:
  593 + * - input text fields will have their 'value' property set to the empty string
  594 + * - select elements will have their 'selectedIndex' property set to -1
  595 + * - checkbox and radio inputs will have their 'checked' property set to false
  596 + * - inputs of type submit, button, reset, and hidden will *not* be effected
  597 + * - button elements will *not* be effected
  598 + */
  599 +$.fn.clearForm = function() {
  600 + return this.each(function() {
  601 + $('input,select,textarea', this).clearFields();
  602 + });
  603 +};
  604 +
  605 +/**
  606 + * Clears the selected form elements.
  607 + */
  608 +$.fn.clearFields = $.fn.clearInputs = function() {
  609 + return this.each(function() {
  610 + var t = this.type, tag = this.tagName.toLowerCase();
  611 + if (t == 'text' || t == 'password' || tag == 'textarea')
  612 + this.value = '';
  613 + else if (t == 'checkbox' || t == 'radio')
  614 + this.checked = false;
  615 + else if (tag == 'select')
  616 + this.selectedIndex = -1;
  617 + });
  618 +};
  619 +
  620 +/**
  621 + * Resets the form data. Causes all form elements to be reset to their original value.
  622 + */
  623 +$.fn.resetForm = function() {
  624 + return this.each(function() {
  625 + // guard against an input with the name of 'reset'
  626 + // note that IE reports the reset function as an 'object'
  627 + if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
  628 + this.reset();
  629 + });
  630 +};
  631 +
  632 +/**
  633 + * Enables or disables any matching elements.
  634 + */
  635 +$.fn.enable = function(b) {
  636 + if (b == undefined) b = true;
  637 + return this.each(function() {
  638 + this.disabled = !b;
  639 + });
  640 +};
  641 +
  642 +/**
  643 + * Checks/unchecks any matching checkboxes or radio buttons and
  644 + * selects/deselects and matching option elements.
  645 + */
  646 +$.fn.selected = function(select) {
  647 + if (select == undefined) select = true;
  648 + return this.each(function() {
  649 + var t = this.type;
  650 + if (t == 'checkbox' || t == 'radio')
  651 + this.checked = select;
  652 + else if (this.tagName.toLowerCase() == 'option') {
  653 + var $sel = $(this).parent('select');
  654 + if (select && $sel[0] && $sel[0].type == 'select-one') {
  655 + // deselect all other options
  656 + $sel.find('option').selected(false);
  657 + }
  658 + this.selected = select;
  659 + }
  660 + });
  661 +};
  662 +
  663 +// helper fn for console logging
  664 +// set $.fn.ajaxSubmit.debug to true to enable debug logging
  665 +function log() {
  666 + if ($.fn.ajaxSubmit.debug) {
  667 + var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
  668 + if (window.console && window.console.log)
  669 + window.console.log(msg);
  670 + else if (window.opera && window.opera.postError)
  671 + window.opera.postError(msg);
  672 + }
  673 +};
  674 +
  675 +})(jQuery);
public/stylesheets/application.css
@@ -97,7 +97,7 @@ div#profile-disabled { @@ -97,7 +97,7 @@ div#profile-disabled {
97 padding-left: 60px; 97 padding-left: 60px;
98 min-height: 40px; 98 min-height: 40px;
99 width: 400px; 99 width: 400px;
100 - background: url("../images/icons-app/alert.png") no-repeat 5px #ffffa9; 100 + background: url('/images/icons-app/alert.png') no-repeat 5px #ffffa9;
101 } 101 }
102 102
103 div#profile-disabled .unlock-button{ 103 div#profile-disabled .unlock-button{
@@ -2503,20 +2503,158 @@ div#activation_enterprise div { @@ -2503,20 +2503,158 @@ div#activation_enterprise div {
2503 2503
2504 /* * * Show Product * * * * * * * * * * * * */ 2504 /* * * Show Product * * * * * * * * * * * * */
2505 2505
2506 -#show_product .product-pic { 2506 +.controller-catalog #show_product .product-pic {
  2507 + border: 1px solid #D3D7CF;
  2508 +}
  2509 +
  2510 +#show_product,
  2511 +#category-product-edition {
  2512 + background-color: transparent;
  2513 + position: relative;
  2514 + padding: 5px;
  2515 + -moz-border-radius: 5px;
  2516 + -webkit-border-radius: 5px;
  2517 +}
  2518 +
  2519 +#show_product h2,
  2520 +#category-product-edition h2 {
  2521 + margin-top: 0px;
  2522 +}
  2523 +
  2524 +#show_product .field-value {
  2525 + font-weight: bold;
  2526 +}
  2527 +
  2528 +#product-category,
  2529 +.product-category-hierarchy {
  2530 + margin-top: 40px;
  2531 + margin-bottom: 12px;
  2532 + padding-left: 7px;
  2533 +}
  2534 +
  2535 +#product-category {
  2536 + height: 18px;
  2537 +}
  2538 +
  2539 +#product-category p {
  2540 + display: inline;
  2541 +}
  2542 +
  2543 +#category-product-edition {
  2544 + padding-left: 10px;
  2545 + padding-right: 10px;
  2546 +}
  2547 +
  2548 +#display-product-name,
  2549 +#display-product-category {
2507 float: left; 2550 float: left;
2508 - margin-right: 15px;  
2509 - border: 1px solid #444;  
2510 - padding: 2px;  
2511 - background: #FFF;  
2512 } 2551 }
2513 2552
2514 -#show_product .product_category { 2553 +#product-details {
2515 padding-top: 10px; 2554 padding-top: 10px;
  2555 + padding-bottom: 10px;
  2556 + position: relative;
  2557 +}
  2558 +
  2559 +#product-name h2 {
  2560 + display: inline;
  2561 +}
  2562 +
  2563 +#product_name {
  2564 + width: 90%;
  2565 +}
  2566 +
  2567 +#edit-product-name {
  2568 + margin-top: 10px;
  2569 +}
  2570 +
  2571 +#product-name,
  2572 +#product-details {
  2573 + position: relative;
2516 clear: left; 2574 clear: left;
2517 } 2575 }
2518 2576
  2577 +#display-product-info {
  2578 + left: 290px;
  2579 + top: 10px;
  2580 + width: 360px;
  2581 + text-align: left;
  2582 + position: absolute;
  2583 + overflow: hidden;
  2584 +}
  2585 +
  2586 +#product-info-form {
  2587 + margin-top: 20px;
  2588 +}
2519 2589
  2590 +#edit-product-category {
  2591 + padding: 15px 5px 10px;
  2592 +}
  2593 +
  2594 +#product-info .field-name {
  2595 + margin-top: 5px;
  2596 + margin-bottom: 15px;
  2597 +}
  2598 +
  2599 +#product-info .type-text input,
  2600 +#product-info select {
  2601 + width: 150px;
  2602 +}
  2603 +
  2604 +#product-info .section-title {
  2605 + display: none;
  2606 +}
  2607 +
  2608 +#product-qualifiers {
  2609 + padding-left: 0px;
  2610 +}
  2611 +
  2612 +#product-qualifiers li {
  2613 + list-style: none;
  2614 +}
  2615 +
  2616 +#edit-product-button-ui-info {
  2617 + margin-top: 40px;
  2618 +}
  2619 +
  2620 +#product-image {
  2621 + text-align: center;
  2622 + padding: 10px;
  2623 + width: 250px;
  2624 + margin-right: 10px;
  2625 + margin-bottom: 5px;
  2626 + border: 1px solid #AAA;
  2627 +}
  2628 +
  2629 +#product-image p {
  2630 + font-size: 11px;
  2631 + font-style: italic;
  2632 + margin-top: 0px;
  2633 + color: #555;
  2634 +}
  2635 +
  2636 +#display-product-image {
  2637 + margin-bottom: 10px;
  2638 + overflow: hidden;
  2639 +}
  2640 +
  2641 +#edit-product-image {
  2642 + position: relative;
  2643 + margin-top: 5px;
  2644 +}
  2645 +
  2646 +#product-qualifiers-list {
  2647 + padding: 0px;
  2648 +}
  2649 +
  2650 +#product-qualifiers-list li {
  2651 + list-style: none;
  2652 +}
  2653 +
  2654 +#product-qualifiers-list .product-qualifier-title,
  2655 +#product-qualifiers-list .product-certifier-title {
  2656 + margin-right: 20px;
  2657 +}
2520 /* ==> public/stylesheets/controller_cms.css <== */ 2658 /* ==> public/stylesheets/controller_cms.css <== */
2521 2659
2522 .controller-cms #article_types li { 2660 .controller-cms #article_types li {
@@ -3007,7 +3145,7 @@ h1#agenda-title { @@ -3007,7 +3145,7 @@ h1#agenda-title {
3007 #new_product_title { 3145 #new_product_title {
3008 text-align: center; 3146 text-align: center;
3009 } 3147 }
3010 -#new_product_form { 3148 +#product_category_form {
3011 border: 1px solid #AABB88; 3149 border: 1px solid #AABB88;
3012 -moz-border-radius: 5px; 3150 -moz-border-radius: 5px;
3013 -webkit-border-radius: 5px; 3151 -webkit-border-radius: 5px;
@@ -3063,7 +3201,7 @@ h1#agenda-title { @@ -3063,7 +3201,7 @@ h1#agenda-title {
3063 display: block; 3201 display: block;
3064 } 3202 }
3065 #hierarchy_navigation { 3203 #hierarchy_navigation {
3066 - min-height: 1em; 3204 + min-height: 1.3em;
3067 } 3205 }
3068 .msie #hierarchy_navigation { 3206 .msie #hierarchy_navigation {
3069 font-size: small; 3207 font-size: small;
@@ -3903,7 +4041,8 @@ h1#agenda-title { @@ -3903,7 +4041,8 @@ h1#agenda-title {
3903 /* jquery.ui custom styles */ 4041 /* jquery.ui custom styles */
3904 4042
3905 .fg-button span { 4043 .fg-button span {
3906 - padding: 0.1em 1em !important; 4044 + padding-top: 0.1em !important;
  4045 + padding-bottom: 0.1em !important;
3907 } 4046 }
3908 4047
3909 .treeitem .button{ 4048 .treeitem .button{
public/stylesheets/jquery.ui/south-street/images/ui-anim_basic_16x16.gif 0 → 100644

1.52 KB

public/stylesheets/jquery.ui/south-street/images/ui-bg_glass_55_fcf0ba_1x400.png 0 → 100644

127 Bytes

public/stylesheets/jquery.ui/south-street/images/ui-bg_gloss-wave_100_ece8da_500x100.png 0 → 100644

2.08 KB

public/stylesheets/jquery.ui/south-street/images/ui-bg_highlight-hard_100_f5f3e5_1x100.png 0 → 100644

110 Bytes

public/stylesheets/jquery.ui/south-street/images/ui-bg_highlight-hard_100_fafaf4_1x100.png 0 → 100644

96 Bytes

public/stylesheets/jquery.ui/south-street/images/ui-bg_highlight-hard_15_459e00_1x100.png 0 → 100644

153 Bytes

public/stylesheets/jquery.ui/south-street/images/ui-bg_highlight-hard_95_cccccc_1x100.png 0 → 100644

105 Bytes

public/stylesheets/jquery.ui/south-street/images/ui-bg_highlight-soft_25_67b021_1x100.png 0 → 100644

124 Bytes

public/stylesheets/jquery.ui/south-street/images/ui-bg_highlight-soft_95_ffedad_1x100.png 0 → 100644

165 Bytes

public/stylesheets/jquery.ui/south-street/images/ui-bg_inset-soft_15_2b2922_1x100.png 0 → 100644

119 Bytes

public/stylesheets/jquery.ui/south-street/images/ui-icons_808080_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/south-street/images/ui-icons_847e71_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/south-street/images/ui-icons_8dc262_256x240.png 0 → 100644

5.23 KB

public/stylesheets/jquery.ui/south-street/images/ui-icons_cd0a0a_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/south-street/images/ui-icons_eeeeee_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/south-street/images/ui-icons_ffffff_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/south-street/jquery-ui-1.8.2.custom.css 0 → 100644
@@ -0,0 +1,489 @@ @@ -0,0 +1,489 @@
  1 +/*
  2 +* jQuery UI CSS Framework
  3 +* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
  4 +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
  5 +*/
  6 +
  7 +/* Layout helpers
  8 +----------------------------------*/
  9 +.ui-helper-hidden { display: none; }
  10 +.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
  11 +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
  12 +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
  13 +.ui-helper-clearfix { display: inline-block; }
  14 +/* required comment for clearfix to work in Opera \*/
  15 +* html .ui-helper-clearfix { height:1%; }
  16 +.ui-helper-clearfix { display:block; }
  17 +/* end clearfix */
  18 +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
  19 +
  20 +
  21 +/* Interaction Cues
  22 +----------------------------------*/
  23 +.ui-state-disabled { cursor: default !important; }
  24 +
  25 +
  26 +/* Icons
  27 +----------------------------------*/
  28 +
  29 +/* states and images */
  30 +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
  31 +
  32 +
  33 +/* Misc visuals
  34 +----------------------------------*/
  35 +
  36 +/* Overlays */
  37 +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
  38 +
  39 +
  40 +/*
  41 +* jQuery UI CSS Framework
  42 +* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
  43 +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
  44 +* 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
  45 +*/
  46 +
  47 +
  48 +/* Component containers
  49 +----------------------------------*/
  50 +.ui-widget { font-family: segoe ui, Arial, sans-serif; font-size: 1.1em; }
  51 +.ui-widget .ui-widget { font-size: 1em; }
  52 +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: segoe ui, Arial, sans-serif; font-size: 1em; }
  53 +.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; }
  54 +.ui-widget-content a { color: #312e25; }
  55 +.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; }
  56 +.ui-widget-header a { color: #433f38; }
  57 +
  58 +/* Interaction states
  59 +----------------------------------*/
  60 +.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; }
  61 +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #ffffff; text-decoration: none; }
  62 +.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; }
  63 +.ui-state-hover a, .ui-state-hover a:hover { color: #ffffff; text-decoration: none; }
  64 +.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; }
  65 +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #459e00; text-decoration: none; }
  66 +.ui-widget :active { outline: none; }
  67 +
  68 +/* Interaction Cues
  69 +----------------------------------*/
  70 +.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; }
  71 +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
  72 +.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; }
  73 +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd5c0a; }
  74 +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd5c0a; }
  75 +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
  76 +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
  77 +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
  78 +
  79 +/* Icons
  80 +----------------------------------*/
  81 +
  82 +/* states and images */
  83 +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_808080_256x240.png); }
  84 +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_808080_256x240.png); }
  85 +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_847e71_256x240.png); }
  86 +.ui-state-default .ui-icon { background-image: url(images/ui-icons_eeeeee_256x240.png); }
  87 +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
  88 +.ui-state-active .ui-icon {background-image: url(images/ui-icons_8dc262_256x240.png); }
  89 +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_8dc262_256x240.png); }
  90 +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
  91 +
  92 +/* positioning */
  93 +.ui-icon-carat-1-n { background-position: 0 0; }
  94 +.ui-icon-carat-1-ne { background-position: -16px 0; }
  95 +.ui-icon-carat-1-e { background-position: -32px 0; }
  96 +.ui-icon-carat-1-se { background-position: -48px 0; }
  97 +.ui-icon-carat-1-s { background-position: -64px 0; }
  98 +.ui-icon-carat-1-sw { background-position: -80px 0; }
  99 +.ui-icon-carat-1-w { background-position: -96px 0; }
  100 +.ui-icon-carat-1-nw { background-position: -112px 0; }
  101 +.ui-icon-carat-2-n-s { background-position: -128px 0; }
  102 +.ui-icon-carat-2-e-w { background-position: -144px 0; }
  103 +.ui-icon-triangle-1-n { background-position: 0 -16px; }
  104 +.ui-icon-triangle-1-ne { background-position: -16px -16px; }
  105 +.ui-icon-triangle-1-e { background-position: -32px -16px; }
  106 +.ui-icon-triangle-1-se { background-position: -48px -16px; }
  107 +.ui-icon-triangle-1-s { background-position: -64px -16px; }
  108 +.ui-icon-triangle-1-sw { background-position: -80px -16px; }
  109 +.ui-icon-triangle-1-w { background-position: -96px -16px; }
  110 +.ui-icon-triangle-1-nw { background-position: -112px -16px; }
  111 +.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
  112 +.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
  113 +.ui-icon-arrow-1-n { background-position: 0 -32px; }
  114 +.ui-icon-arrow-1-ne { background-position: -16px -32px; }
  115 +.ui-icon-arrow-1-e { background-position: -32px -32px; }
  116 +.ui-icon-arrow-1-se { background-position: -48px -32px; }
  117 +.ui-icon-arrow-1-s { background-position: -64px -32px; }
  118 +.ui-icon-arrow-1-sw { background-position: -80px -32px; }
  119 +.ui-icon-arrow-1-w { background-position: -96px -32px; }
  120 +.ui-icon-arrow-1-nw { background-position: -112px -32px; }
  121 +.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
  122 +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
  123 +.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
  124 +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
  125 +.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
  126 +.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
  127 +.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
  128 +.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
  129 +.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
  130 +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
  131 +.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
  132 +.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
  133 +.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
  134 +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
  135 +.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
  136 +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
  137 +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
  138 +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
  139 +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
  140 +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
  141 +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
  142 +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
  143 +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
  144 +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
  145 +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
  146 +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
  147 +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
  148 +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
  149 +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
  150 +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
  151 +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
  152 +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
  153 +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
  154 +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
  155 +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
  156 +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
  157 +.ui-icon-arrow-4 { background-position: 0 -80px; }
  158 +.ui-icon-arrow-4-diag { background-position: -16px -80px; }
  159 +.ui-icon-extlink { background-position: -32px -80px; }
  160 +.ui-icon-newwin { background-position: -48px -80px; }
  161 +.ui-icon-refresh { background-position: -64px -80px; }
  162 +.ui-icon-shuffle { background-position: -80px -80px; }
  163 +.ui-icon-transfer-e-w { background-position: -96px -80px; }
  164 +.ui-icon-transferthick-e-w { background-position: -112px -80px; }
  165 +.ui-icon-folder-collapsed { background-position: 0 -96px; }
  166 +.ui-icon-folder-open { background-position: -16px -96px; }
  167 +.ui-icon-document { background-position: -32px -96px; }
  168 +.ui-icon-document-b { background-position: -48px -96px; }
  169 +.ui-icon-note { background-position: -64px -96px; }
  170 +.ui-icon-mail-closed { background-position: -80px -96px; }
  171 +.ui-icon-mail-open { background-position: -96px -96px; }
  172 +.ui-icon-suitcase { background-position: -112px -96px; }
  173 +.ui-icon-comment { background-position: -128px -96px; }
  174 +.ui-icon-person { background-position: -144px -96px; }
  175 +.ui-icon-print { background-position: -160px -96px; }
  176 +.ui-icon-trash { background-position: -176px -96px; }
  177 +.ui-icon-locked { background-position: -192px -96px; }
  178 +.ui-icon-unlocked { background-position: -208px -96px; }
  179 +.ui-icon-bookmark { background-position: -224px -96px; }
  180 +.ui-icon-tag { background-position: -240px -96px; }
  181 +.ui-icon-home { background-position: 0 -112px; }
  182 +.ui-icon-flag { background-position: -16px -112px; }
  183 +.ui-icon-calendar { background-position: -32px -112px; }
  184 +.ui-icon-cart { background-position: -48px -112px; }
  185 +.ui-icon-pencil { background-position: -64px -112px; }
  186 +.ui-icon-clock { background-position: -80px -112px; }
  187 +.ui-icon-disk { background-position: -96px -112px; }
  188 +.ui-icon-calculator { background-position: -112px -112px; }
  189 +.ui-icon-zoomin { background-position: -128px -112px; }
  190 +.ui-icon-zoomout { background-position: -144px -112px; }
  191 +.ui-icon-search { background-position: -160px -112px; }
  192 +.ui-icon-wrench { background-position: -176px -112px; }
  193 +.ui-icon-gear { background-position: -192px -112px; }
  194 +.ui-icon-heart { background-position: -208px -112px; }
  195 +.ui-icon-star { background-position: -224px -112px; }
  196 +.ui-icon-link { background-position: -240px -112px; }
  197 +.ui-icon-cancel { background-position: 0 -128px; }
  198 +.ui-icon-plus { background-position: -16px -128px; }
  199 +.ui-icon-plusthick { background-position: -32px -128px; }
  200 +.ui-icon-minus { background-position: -48px -128px; }
  201 +.ui-icon-minusthick { background-position: -64px -128px; }
  202 +.ui-icon-close { background-position: -80px -128px; }
  203 +.ui-icon-closethick { background-position: -96px -128px; }
  204 +.ui-icon-key { background-position: -112px -128px; }
  205 +.ui-icon-lightbulb { background-position: -128px -128px; }
  206 +.ui-icon-scissors { background-position: -144px -128px; }
  207 +.ui-icon-clipboard { background-position: -160px -128px; }
  208 +.ui-icon-copy { background-position: -176px -128px; }
  209 +.ui-icon-contact { background-position: -192px -128px; }
  210 +.ui-icon-image { background-position: -208px -128px; }
  211 +.ui-icon-video { background-position: -224px -128px; }
  212 +.ui-icon-script { background-position: -240px -128px; }
  213 +.ui-icon-alert { background-position: 0 -144px; }
  214 +.ui-icon-info { background-position: -16px -144px; }
  215 +.ui-icon-notice { background-position: -32px -144px; }
  216 +.ui-icon-help { background-position: -48px -144px; }
  217 +.ui-icon-check { background-position: -64px -144px; }
  218 +.ui-icon-bullet { background-position: -80px -144px; }
  219 +.ui-icon-radio-off { background-position: -96px -144px; }
  220 +.ui-icon-radio-on { background-position: -112px -144px; }
  221 +.ui-icon-pin-w { background-position: -128px -144px; }
  222 +.ui-icon-pin-s { background-position: -144px -144px; }
  223 +.ui-icon-play { background-position: 0 -160px; }
  224 +.ui-icon-pause { background-position: -16px -160px; }
  225 +.ui-icon-seek-next { background-position: -32px -160px; }
  226 +.ui-icon-seek-prev { background-position: -48px -160px; }
  227 +.ui-icon-seek-end { background-position: -64px -160px; }
  228 +.ui-icon-seek-start { background-position: -80px -160px; }
  229 +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
  230 +.ui-icon-seek-first { background-position: -80px -160px; }
  231 +.ui-icon-stop { background-position: -96px -160px; }
  232 +.ui-icon-eject { background-position: -112px -160px; }
  233 +.ui-icon-volume-off { background-position: -128px -160px; }
  234 +.ui-icon-volume-on { background-position: -144px -160px; }
  235 +.ui-icon-power { background-position: 0 -176px; }
  236 +.ui-icon-signal-diag { background-position: -16px -176px; }
  237 +.ui-icon-signal { background-position: -32px -176px; }
  238 +.ui-icon-battery-0 { background-position: -48px -176px; }
  239 +.ui-icon-battery-1 { background-position: -64px -176px; }
  240 +.ui-icon-battery-2 { background-position: -80px -176px; }
  241 +.ui-icon-battery-3 { background-position: -96px -176px; }
  242 +.ui-icon-circle-plus { background-position: 0 -192px; }
  243 +.ui-icon-circle-minus { background-position: -16px -192px; }
  244 +.ui-icon-circle-close { background-position: -32px -192px; }
  245 +.ui-icon-circle-triangle-e { background-position: -48px -192px; }
  246 +.ui-icon-circle-triangle-s { background-position: -64px -192px; }
  247 +.ui-icon-circle-triangle-w { background-position: -80px -192px; }
  248 +.ui-icon-circle-triangle-n { background-position: -96px -192px; }
  249 +.ui-icon-circle-arrow-e { background-position: -112px -192px; }
  250 +.ui-icon-circle-arrow-s { background-position: -128px -192px; }
  251 +.ui-icon-circle-arrow-w { background-position: -144px -192px; }
  252 +.ui-icon-circle-arrow-n { background-position: -160px -192px; }
  253 +.ui-icon-circle-zoomin { background-position: -176px -192px; }
  254 +.ui-icon-circle-zoomout { background-position: -192px -192px; }
  255 +.ui-icon-circle-check { background-position: -208px -192px; }
  256 +.ui-icon-circlesmall-plus { background-position: 0 -208px; }
  257 +.ui-icon-circlesmall-minus { background-position: -16px -208px; }
  258 +.ui-icon-circlesmall-close { background-position: -32px -208px; }
  259 +.ui-icon-squaresmall-plus { background-position: -48px -208px; }
  260 +.ui-icon-squaresmall-minus { background-position: -64px -208px; }
  261 +.ui-icon-squaresmall-close { background-position: -80px -208px; }
  262 +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
  263 +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
  264 +.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
  265 +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
  266 +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
  267 +.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
  268 +
  269 +
  270 +/* Misc visuals
  271 +----------------------------------*/
  272 +
  273 +/* Corner radius */
  274 +.ui-corner-tl { -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; border-top-left-radius: 6px; }
  275 +.ui-corner-tr { -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px; }
  276 +.ui-corner-bl { -moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; }
  277 +.ui-corner-br { -moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; }
  278 +.ui-corner-top { -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; border-top-left-radius: 6px; -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px; }
  279 +.ui-corner-bottom { -moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; -moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; }
  280 +.ui-corner-right { -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px; -moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; }
  281 +.ui-corner-left { -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; border-top-left-radius: 6px; -moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; }
  282 +.ui-corner-all { -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px; }
  283 +
  284 +/* Overlays */
  285 +.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); }
  286 +.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; -webkit-border-radius: 10px; border-radius: 10px; }/* Resizable
  287 +----------------------------------*/
  288 +.ui-resizable { position: relative;}
  289 +.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
  290 +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
  291 +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
  292 +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
  293 +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
  294 +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
  295 +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
  296 +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
  297 +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
  298 +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Selectable
  299 +----------------------------------*/
  300 +.ui-selectable-helper { border:1px dotted black }
  301 +/* Accordion
  302 +----------------------------------*/
  303 +.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
  304 +.ui-accordion .ui-accordion-li-fix { display: inline; }
  305 +.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
  306 +.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
  307 +/* IE7-/Win - Fix extra vertical space in lists */
  308 +.ui-accordion a { zoom: 1; }
  309 +.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
  310 +.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
  311 +.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
  312 +.ui-accordion .ui-accordion-content-active { display: block; }/* Autocomplete
  313 +----------------------------------*/
  314 +.ui-autocomplete { position: absolute; cursor: default; }
  315 +.ui-autocomplete-loading { background: white url('images/ui-anim_basic_16x16.gif') right center no-repeat; }
  316 +
  317 +/* workarounds */
  318 +* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
  319 +
  320 +/* Menu
  321 +----------------------------------*/
  322 +.ui-menu {
  323 + list-style:none;
  324 + padding: 2px;
  325 + margin: 0;
  326 + display:block;
  327 +}
  328 +.ui-menu .ui-menu {
  329 + margin-top: -3px;
  330 +}
  331 +.ui-menu .ui-menu-item {
  332 + margin:0;
  333 + padding: 0;
  334 + zoom: 1;
  335 + float: left;
  336 + clear: left;
  337 + width: 100%;
  338 +}
  339 +.ui-menu .ui-menu-item a {
  340 + text-decoration:none;
  341 + display:block;
  342 + padding:.2em .4em;
  343 + line-height:1.5;
  344 + zoom:1;
  345 +}
  346 +.ui-menu .ui-menu-item a.ui-state-hover,
  347 +.ui-menu .ui-menu-item a.ui-state-active {
  348 + font-weight: normal;
  349 + margin: -1px;
  350 +}
  351 +/* Button
  352 +----------------------------------*/
  353 +
  354 +.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
  355 +.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
  356 +button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
  357 +.ui-button-icons-only { width: 3.4em; }
  358 +button.ui-button-icons-only { width: 3.7em; }
  359 +
  360 +/*button text element */
  361 +.ui-button .ui-button-text { display: block; line-height: 1.4; }
  362 +.ui-button-text-only .ui-button-text { padding: .4em 1em; }
  363 +.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
  364 +.ui-button-text-icon .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
  365 +.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
  366 +/* no icon support for input elements, provide padding by default */
  367 +input.ui-button { padding: .4em 1em; }
  368 +
  369 +/*button icon element(s) */
  370 +.ui-button-icon-only .ui-icon, .ui-button-text-icon .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
  371 +.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
  372 +.ui-button-text-icon .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
  373 +.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
  374 +
  375 +/*button sets*/
  376 +.ui-buttonset { margin-right: 7px; }
  377 +.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
  378 +
  379 +/* workarounds */
  380 +button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
  381 +
  382 +
  383 +
  384 +
  385 +
  386 +/* Dialog
  387 +----------------------------------*/
  388 +.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
  389 +.ui-dialog .ui-dialog-titlebar { padding: .5em 1em .3em; position: relative; }
  390 +.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; }
  391 +.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
  392 +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
  393 +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
  394 +.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
  395 +.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
  396 +.ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
  397 +.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
  398 +.ui-draggable .ui-dialog-titlebar { cursor: move; }
  399 +/* Slider
  400 +----------------------------------*/
  401 +.ui-slider { position: relative; text-align: left; }
  402 +.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
  403 +.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
  404 +
  405 +.ui-slider-horizontal { height: .8em; }
  406 +.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
  407 +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
  408 +.ui-slider-horizontal .ui-slider-range-min { left: 0; }
  409 +.ui-slider-horizontal .ui-slider-range-max { right: 0; }
  410 +
  411 +.ui-slider-vertical { width: .8em; height: 100px; }
  412 +.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
  413 +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
  414 +.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
  415 +.ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs
  416 +----------------------------------*/
  417 +.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
  418 +.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
  419 +.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
  420 +.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
  421 +.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
  422 +.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
  423 +.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
  424 +.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
  425 +.ui-tabs .ui-tabs-hide { display: none !important; }
  426 +/* Datepicker
  427 +----------------------------------*/
  428 +.ui-datepicker { width: 17em; padding: .2em .2em 0; }
  429 +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
  430 +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
  431 +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
  432 +.ui-datepicker .ui-datepicker-prev { left:2px; }
  433 +.ui-datepicker .ui-datepicker-next { right:2px; }
  434 +.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
  435 +.ui-datepicker .ui-datepicker-next-hover { right:1px; }
  436 +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
  437 +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
  438 +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
  439 +.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
  440 +.ui-datepicker select.ui-datepicker-month,
  441 +.ui-datepicker select.ui-datepicker-year { width: 49%;}
  442 +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
  443 +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
  444 +.ui-datepicker td { border: 0; padding: 1px; }
  445 +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
  446 +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
  447 +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
  448 +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
  449 +
  450 +/* with multiple calendars */
  451 +.ui-datepicker.ui-datepicker-multi { width:auto; }
  452 +.ui-datepicker-multi .ui-datepicker-group { float:left; }
  453 +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
  454 +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
  455 +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
  456 +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
  457 +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
  458 +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
  459 +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
  460 +.ui-datepicker-row-break { clear:both; width:100%; }
  461 +
  462 +/* RTL support */
  463 +.ui-datepicker-rtl { direction: rtl; }
  464 +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
  465 +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
  466 +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
  467 +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
  468 +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
  469 +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
  470 +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
  471 +.ui-datepicker-rtl .ui-datepicker-group { float:right; }
  472 +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
  473 +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
  474 +
  475 +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
  476 +.ui-datepicker-cover {
  477 + display: none; /*sorry for IE5*/
  478 + display/**/: block; /*sorry for IE5*/
  479 + position: absolute; /*must have*/
  480 + z-index: -1; /*must have*/
  481 + filter: mask(); /*must have*/
  482 + top: -4px; /*must have*/
  483 + left: -4px; /*must have*/
  484 + width: 200px; /*must have*/
  485 + height: 200px; /*must have*/
  486 +}/* Progressbar
  487 +----------------------------------*/
  488 +.ui-progressbar { height:2em; text-align: left; }
  489 +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }
0 \ No newline at end of file 490 \ No newline at end of file
public/stylesheets/jquery.ui/sunny/images/ui-anim_basic_16x16.gif 0 → 100644

1.52 KB

public/stylesheets/jquery.ui/sunny/images/ui-bg_diagonals-medium_20_d34d17_40x40.png 0 → 100644

199 Bytes

public/stylesheets/jquery.ui/sunny/images/ui-bg_flat_30_cccccc_40x100.png 0 → 100644

180 Bytes

public/stylesheets/jquery.ui/sunny/images/ui-bg_flat_50_5c5c5c_40x100.png 0 → 100644

180 Bytes

public/stylesheets/jquery.ui/sunny/images/ui-bg_gloss-wave_45_817865_500x100.png 0 → 100644

3.88 KB

public/stylesheets/jquery.ui/sunny/images/ui-bg_gloss-wave_60_fece2f_500x100.png 0 → 100644

3.43 KB

public/stylesheets/jquery.ui/sunny/images/ui-bg_gloss-wave_70_ffdd57_500x100.png 0 → 100644

3.05 KB

public/stylesheets/jquery.ui/sunny/images/ui-bg_gloss-wave_90_fff9e5_500x100.png 0 → 100644

2.2 KB

public/stylesheets/jquery.ui/sunny/images/ui-bg_highlight-soft_100_feeebd_1x100.png 0 → 100644

108 Bytes

public/stylesheets/jquery.ui/sunny/images/ui-bg_inset-soft_30_ffffff_1x100.png 0 → 100644

100 Bytes

public/stylesheets/jquery.ui/sunny/images/ui-icons_3d3d3d_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/sunny/images/ui-icons_bd7b00_256x240.png 0 → 100644

5.23 KB

public/stylesheets/jquery.ui/sunny/images/ui-icons_d19405_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/sunny/images/ui-icons_eb990f_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/sunny/images/ui-icons_ed9f26_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/sunny/images/ui-icons_fadc7a_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/sunny/images/ui-icons_ffe180_256x240.png 0 → 100644

5.23 KB

public/stylesheets/jquery.ui/sunny/jquery-ui-1.8.2.custom.css 0 → 100644
@@ -0,0 +1,489 @@ @@ -0,0 +1,489 @@
  1 +/*
  2 +* jQuery UI CSS Framework
  3 +* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
  4 +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
  5 +*/
  6 +
  7 +/* Layout helpers
  8 +----------------------------------*/
  9 +.ui-helper-hidden { display: none; }
  10 +.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
  11 +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
  12 +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
  13 +.ui-helper-clearfix { display: inline-block; }
  14 +/* required comment for clearfix to work in Opera \*/
  15 +* html .ui-helper-clearfix { height:1%; }
  16 +.ui-helper-clearfix { display:block; }
  17 +/* end clearfix */
  18 +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
  19 +
  20 +
  21 +/* Interaction Cues
  22 +----------------------------------*/
  23 +.ui-state-disabled { cursor: default !important; }
  24 +
  25 +
  26 +/* Icons
  27 +----------------------------------*/
  28 +
  29 +/* states and images */
  30 +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
  31 +
  32 +
  33 +/* Misc visuals
  34 +----------------------------------*/
  35 +
  36 +/* Overlays */
  37 +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
  38 +
  39 +
  40 +/*
  41 +* jQuery UI CSS Framework
  42 +* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
  43 +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
  44 +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Segoe%20UI,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=8px&bgColorHeader=817865&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=45&borderColorHeader=494437&fcHeader=ffffff&iconColorHeader=fadc7a&bgColorContent=feeebd&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=8e846b&fcContent=383838&iconColorContent=d19405&bgColorDefault=fece2f&bgTextureDefault=12_gloss_wave.png&bgImgOpacityDefault=60&borderColorDefault=d19405&fcDefault=4c3000&iconColorDefault=3d3d3d&bgColorHover=ffdd57&bgTextureHover=12_gloss_wave.png&bgImgOpacityHover=70&borderColorHover=a45b13&fcHover=381f00&iconColorHover=bd7b00&bgColorActive=ffffff&bgTextureActive=05_inset_soft.png&bgImgOpacityActive=30&borderColorActive=655e4e&fcActive=0074c7&iconColorActive=eb990f&bgColorHighlight=fff9e5&bgTextureHighlight=12_gloss_wave.png&bgImgOpacityHighlight=90&borderColorHighlight=eeb420&fcHighlight=1f1f1f&iconColorHighlight=ed9f26&bgColorError=d34d17&bgTextureError=07_diagonals_medium.png&bgImgOpacityError=20&borderColorError=ffb73d&fcError=ffffff&iconColorError=ffe180&bgColorOverlay=5c5c5c&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=50&opacityOverlay=80&bgColorShadow=cccccc&bgTextureShadow=01_flat.png&bgImgOpacityShadow=30&opacityShadow=60&thicknessShadow=7px&offsetTopShadow=-7px&offsetLeftShadow=-7px&cornerRadiusShadow=8px
  45 +*/
  46 +
  47 +
  48 +/* Component containers
  49 +----------------------------------*/
  50 +.ui-widget { font-family: Segoe UI, Arial, sans-serif; font-size: 1.1em; }
  51 +.ui-widget .ui-widget { font-size: 1em; }
  52 +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Segoe UI, Arial, sans-serif; font-size: 1em; }
  53 +.ui-widget-content { border: 1px solid #8e846b; background: #feeebd url(images/ui-bg_highlight-soft_100_feeebd_1x100.png) 50% top repeat-x; color: #383838; }
  54 +.ui-widget-content a { color: #383838; }
  55 +.ui-widget-header { border: 1px solid #494437; background: #817865 url(images/ui-bg_gloss-wave_45_817865_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
  56 +.ui-widget-header a { color: #ffffff; }
  57 +
  58 +/* Interaction states
  59 +----------------------------------*/
  60 +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d19405; background: #fece2f url(images/ui-bg_gloss-wave_60_fece2f_500x100.png) 50% 50% repeat-x; font-weight: bold; color: #4c3000; }
  61 +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #4c3000; text-decoration: none; }
  62 +.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 #a45b13; background: #ffdd57 url(images/ui-bg_gloss-wave_70_ffdd57_500x100.png) 50% 50% repeat-x; font-weight: bold; color: #381f00; }
  63 +.ui-state-hover a, .ui-state-hover a:hover { color: #381f00; text-decoration: none; }
  64 +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #655e4e; background: #ffffff url(images/ui-bg_inset-soft_30_ffffff_1x100.png) 50% 50% repeat-x; font-weight: bold; color: #0074c7; }
  65 +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #0074c7; text-decoration: none; }
  66 +.ui-widget :active { outline: none; }
  67 +
  68 +/* Interaction Cues
  69 +----------------------------------*/
  70 +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #eeb420; background: #fff9e5 url(images/ui-bg_gloss-wave_90_fff9e5_500x100.png) 50% top repeat-x; color: #1f1f1f; }
  71 +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #1f1f1f; }
  72 +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #ffb73d; background: #d34d17 url(images/ui-bg_diagonals-medium_20_d34d17_40x40.png) 50% 50% repeat; color: #ffffff; }
  73 +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; }
  74 +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; }
  75 +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
  76 +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
  77 +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
  78 +
  79 +/* Icons
  80 +----------------------------------*/
  81 +
  82 +/* states and images */
  83 +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_d19405_256x240.png); }
  84 +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_d19405_256x240.png); }
  85 +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_fadc7a_256x240.png); }
  86 +.ui-state-default .ui-icon { background-image: url(images/ui-icons_3d3d3d_256x240.png); }
  87 +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_bd7b00_256x240.png); }
  88 +.ui-state-active .ui-icon {background-image: url(images/ui-icons_eb990f_256x240.png); }
  89 +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_ed9f26_256x240.png); }
  90 +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffe180_256x240.png); }
  91 +
  92 +/* positioning */
  93 +.ui-icon-carat-1-n { background-position: 0 0; }
  94 +.ui-icon-carat-1-ne { background-position: -16px 0; }
  95 +.ui-icon-carat-1-e { background-position: -32px 0; }
  96 +.ui-icon-carat-1-se { background-position: -48px 0; }
  97 +.ui-icon-carat-1-s { background-position: -64px 0; }
  98 +.ui-icon-carat-1-sw { background-position: -80px 0; }
  99 +.ui-icon-carat-1-w { background-position: -96px 0; }
  100 +.ui-icon-carat-1-nw { background-position: -112px 0; }
  101 +.ui-icon-carat-2-n-s { background-position: -128px 0; }
  102 +.ui-icon-carat-2-e-w { background-position: -144px 0; }
  103 +.ui-icon-triangle-1-n { background-position: 0 -16px; }
  104 +.ui-icon-triangle-1-ne { background-position: -16px -16px; }
  105 +.ui-icon-triangle-1-e { background-position: -32px -16px; }
  106 +.ui-icon-triangle-1-se { background-position: -48px -16px; }
  107 +.ui-icon-triangle-1-s { background-position: -64px -16px; }
  108 +.ui-icon-triangle-1-sw { background-position: -80px -16px; }
  109 +.ui-icon-triangle-1-w { background-position: -96px -16px; }
  110 +.ui-icon-triangle-1-nw { background-position: -112px -16px; }
  111 +.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
  112 +.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
  113 +.ui-icon-arrow-1-n { background-position: 0 -32px; }
  114 +.ui-icon-arrow-1-ne { background-position: -16px -32px; }
  115 +.ui-icon-arrow-1-e { background-position: -32px -32px; }
  116 +.ui-icon-arrow-1-se { background-position: -48px -32px; }
  117 +.ui-icon-arrow-1-s { background-position: -64px -32px; }
  118 +.ui-icon-arrow-1-sw { background-position: -80px -32px; }
  119 +.ui-icon-arrow-1-w { background-position: -96px -32px; }
  120 +.ui-icon-arrow-1-nw { background-position: -112px -32px; }
  121 +.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
  122 +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
  123 +.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
  124 +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
  125 +.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
  126 +.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
  127 +.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
  128 +.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
  129 +.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
  130 +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
  131 +.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
  132 +.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
  133 +.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
  134 +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
  135 +.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
  136 +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
  137 +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
  138 +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
  139 +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
  140 +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
  141 +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
  142 +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
  143 +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
  144 +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
  145 +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
  146 +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
  147 +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
  148 +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
  149 +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
  150 +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
  151 +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
  152 +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
  153 +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
  154 +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
  155 +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
  156 +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
  157 +.ui-icon-arrow-4 { background-position: 0 -80px; }
  158 +.ui-icon-arrow-4-diag { background-position: -16px -80px; }
  159 +.ui-icon-extlink { background-position: -32px -80px; }
  160 +.ui-icon-newwin { background-position: -48px -80px; }
  161 +.ui-icon-refresh { background-position: -64px -80px; }
  162 +.ui-icon-shuffle { background-position: -80px -80px; }
  163 +.ui-icon-transfer-e-w { background-position: -96px -80px; }
  164 +.ui-icon-transferthick-e-w { background-position: -112px -80px; }
  165 +.ui-icon-folder-collapsed { background-position: 0 -96px; }
  166 +.ui-icon-folder-open { background-position: -16px -96px; }
  167 +.ui-icon-document { background-position: -32px -96px; }
  168 +.ui-icon-document-b { background-position: -48px -96px; }
  169 +.ui-icon-note { background-position: -64px -96px; }
  170 +.ui-icon-mail-closed { background-position: -80px -96px; }
  171 +.ui-icon-mail-open { background-position: -96px -96px; }
  172 +.ui-icon-suitcase { background-position: -112px -96px; }
  173 +.ui-icon-comment { background-position: -128px -96px; }
  174 +.ui-icon-person { background-position: -144px -96px; }
  175 +.ui-icon-print { background-position: -160px -96px; }
  176 +.ui-icon-trash { background-position: -176px -96px; }
  177 +.ui-icon-locked { background-position: -192px -96px; }
  178 +.ui-icon-unlocked { background-position: -208px -96px; }
  179 +.ui-icon-bookmark { background-position: -224px -96px; }
  180 +.ui-icon-tag { background-position: -240px -96px; }
  181 +.ui-icon-home { background-position: 0 -112px; }
  182 +.ui-icon-flag { background-position: -16px -112px; }
  183 +.ui-icon-calendar { background-position: -32px -112px; }
  184 +.ui-icon-cart { background-position: -48px -112px; }
  185 +.ui-icon-pencil { background-position: -64px -112px; }
  186 +.ui-icon-clock { background-position: -80px -112px; }
  187 +.ui-icon-disk { background-position: -96px -112px; }
  188 +.ui-icon-calculator { background-position: -112px -112px; }
  189 +.ui-icon-zoomin { background-position: -128px -112px; }
  190 +.ui-icon-zoomout { background-position: -144px -112px; }
  191 +.ui-icon-search { background-position: -160px -112px; }
  192 +.ui-icon-wrench { background-position: -176px -112px; }
  193 +.ui-icon-gear { background-position: -192px -112px; }
  194 +.ui-icon-heart { background-position: -208px -112px; }
  195 +.ui-icon-star { background-position: -224px -112px; }
  196 +.ui-icon-link { background-position: -240px -112px; }
  197 +.ui-icon-cancel { background-position: 0 -128px; }
  198 +.ui-icon-plus { background-position: -16px -128px; }
  199 +.ui-icon-plusthick { background-position: -32px -128px; }
  200 +.ui-icon-minus { background-position: -48px -128px; }
  201 +.ui-icon-minusthick { background-position: -64px -128px; }
  202 +.ui-icon-close { background-position: -80px -128px; }
  203 +.ui-icon-closethick { background-position: -96px -128px; }
  204 +.ui-icon-key { background-position: -112px -128px; }
  205 +.ui-icon-lightbulb { background-position: -128px -128px; }
  206 +.ui-icon-scissors { background-position: -144px -128px; }
  207 +.ui-icon-clipboard { background-position: -160px -128px; }
  208 +.ui-icon-copy { background-position: -176px -128px; }
  209 +.ui-icon-contact { background-position: -192px -128px; }
  210 +.ui-icon-image { background-position: -208px -128px; }
  211 +.ui-icon-video { background-position: -224px -128px; }
  212 +.ui-icon-script { background-position: -240px -128px; }
  213 +.ui-icon-alert { background-position: 0 -144px; }
  214 +.ui-icon-info { background-position: -16px -144px; }
  215 +.ui-icon-notice { background-position: -32px -144px; }
  216 +.ui-icon-help { background-position: -48px -144px; }
  217 +.ui-icon-check { background-position: -64px -144px; }
  218 +.ui-icon-bullet { background-position: -80px -144px; }
  219 +.ui-icon-radio-off { background-position: -96px -144px; }
  220 +.ui-icon-radio-on { background-position: -112px -144px; }
  221 +.ui-icon-pin-w { background-position: -128px -144px; }
  222 +.ui-icon-pin-s { background-position: -144px -144px; }
  223 +.ui-icon-play { background-position: 0 -160px; }
  224 +.ui-icon-pause { background-position: -16px -160px; }
  225 +.ui-icon-seek-next { background-position: -32px -160px; }
  226 +.ui-icon-seek-prev { background-position: -48px -160px; }
  227 +.ui-icon-seek-end { background-position: -64px -160px; }
  228 +.ui-icon-seek-start { background-position: -80px -160px; }
  229 +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
  230 +.ui-icon-seek-first { background-position: -80px -160px; }
  231 +.ui-icon-stop { background-position: -96px -160px; }
  232 +.ui-icon-eject { background-position: -112px -160px; }
  233 +.ui-icon-volume-off { background-position: -128px -160px; }
  234 +.ui-icon-volume-on { background-position: -144px -160px; }
  235 +.ui-icon-power { background-position: 0 -176px; }
  236 +.ui-icon-signal-diag { background-position: -16px -176px; }
  237 +.ui-icon-signal { background-position: -32px -176px; }
  238 +.ui-icon-battery-0 { background-position: -48px -176px; }
  239 +.ui-icon-battery-1 { background-position: -64px -176px; }
  240 +.ui-icon-battery-2 { background-position: -80px -176px; }
  241 +.ui-icon-battery-3 { background-position: -96px -176px; }
  242 +.ui-icon-circle-plus { background-position: 0 -192px; }
  243 +.ui-icon-circle-minus { background-position: -16px -192px; }
  244 +.ui-icon-circle-close { background-position: -32px -192px; }
  245 +.ui-icon-circle-triangle-e { background-position: -48px -192px; }
  246 +.ui-icon-circle-triangle-s { background-position: -64px -192px; }
  247 +.ui-icon-circle-triangle-w { background-position: -80px -192px; }
  248 +.ui-icon-circle-triangle-n { background-position: -96px -192px; }
  249 +.ui-icon-circle-arrow-e { background-position: -112px -192px; }
  250 +.ui-icon-circle-arrow-s { background-position: -128px -192px; }
  251 +.ui-icon-circle-arrow-w { background-position: -144px -192px; }
  252 +.ui-icon-circle-arrow-n { background-position: -160px -192px; }
  253 +.ui-icon-circle-zoomin { background-position: -176px -192px; }
  254 +.ui-icon-circle-zoomout { background-position: -192px -192px; }
  255 +.ui-icon-circle-check { background-position: -208px -192px; }
  256 +.ui-icon-circlesmall-plus { background-position: 0 -208px; }
  257 +.ui-icon-circlesmall-minus { background-position: -16px -208px; }
  258 +.ui-icon-circlesmall-close { background-position: -32px -208px; }
  259 +.ui-icon-squaresmall-plus { background-position: -48px -208px; }
  260 +.ui-icon-squaresmall-minus { background-position: -64px -208px; }
  261 +.ui-icon-squaresmall-close { background-position: -80px -208px; }
  262 +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
  263 +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
  264 +.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
  265 +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
  266 +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
  267 +.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
  268 +
  269 +
  270 +/* Misc visuals
  271 +----------------------------------*/
  272 +
  273 +/* Corner radius */
  274 +.ui-corner-tl { -moz-border-radius-topleft: 8px; -webkit-border-top-left-radius: 8px; border-top-left-radius: 8px; }
  275 +.ui-corner-tr { -moz-border-radius-topright: 8px; -webkit-border-top-right-radius: 8px; border-top-right-radius: 8px; }
  276 +.ui-corner-bl { -moz-border-radius-bottomleft: 8px; -webkit-border-bottom-left-radius: 8px; border-bottom-left-radius: 8px; }
  277 +.ui-corner-br { -moz-border-radius-bottomright: 8px; -webkit-border-bottom-right-radius: 8px; border-bottom-right-radius: 8px; }
  278 +.ui-corner-top { -moz-border-radius-topleft: 8px; -webkit-border-top-left-radius: 8px; border-top-left-radius: 8px; -moz-border-radius-topright: 8px; -webkit-border-top-right-radius: 8px; border-top-right-radius: 8px; }
  279 +.ui-corner-bottom { -moz-border-radius-bottomleft: 8px; -webkit-border-bottom-left-radius: 8px; border-bottom-left-radius: 8px; -moz-border-radius-bottomright: 8px; -webkit-border-bottom-right-radius: 8px; border-bottom-right-radius: 8px; }
  280 +.ui-corner-right { -moz-border-radius-topright: 8px; -webkit-border-top-right-radius: 8px; border-top-right-radius: 8px; -moz-border-radius-bottomright: 8px; -webkit-border-bottom-right-radius: 8px; border-bottom-right-radius: 8px; }
  281 +.ui-corner-left { -moz-border-radius-topleft: 8px; -webkit-border-top-left-radius: 8px; border-top-left-radius: 8px; -moz-border-radius-bottomleft: 8px; -webkit-border-bottom-left-radius: 8px; border-bottom-left-radius: 8px; }
  282 +.ui-corner-all { -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }
  283 +
  284 +/* Overlays */
  285 +.ui-widget-overlay { background: #5c5c5c url(images/ui-bg_flat_50_5c5c5c_40x100.png) 50% 50% repeat-x; opacity: .80;filter:Alpha(Opacity=80); }
  286 +.ui-widget-shadow { margin: -7px 0 0 -7px; padding: 7px; background: #cccccc url(images/ui-bg_flat_30_cccccc_40x100.png) 50% 50% repeat-x; opacity: .60;filter:Alpha(Opacity=60); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/* Resizable
  287 +----------------------------------*/
  288 +.ui-resizable { position: relative;}
  289 +.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
  290 +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
  291 +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
  292 +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
  293 +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
  294 +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
  295 +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
  296 +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
  297 +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
  298 +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Selectable
  299 +----------------------------------*/
  300 +.ui-selectable-helper { border:1px dotted black }
  301 +/* Accordion
  302 +----------------------------------*/
  303 +.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
  304 +.ui-accordion .ui-accordion-li-fix { display: inline; }
  305 +.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
  306 +.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
  307 +/* IE7-/Win - Fix extra vertical space in lists */
  308 +.ui-accordion a { zoom: 1; }
  309 +.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
  310 +.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
  311 +.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
  312 +.ui-accordion .ui-accordion-content-active { display: block; }/* Autocomplete
  313 +----------------------------------*/
  314 +.ui-autocomplete { position: absolute; cursor: default; }
  315 +.ui-autocomplete-loading { background: white url('images/ui-anim_basic_16x16.gif') right center no-repeat; }
  316 +
  317 +/* workarounds */
  318 +* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
  319 +
  320 +/* Menu
  321 +----------------------------------*/
  322 +.ui-menu {
  323 + list-style:none;
  324 + padding: 2px;
  325 + margin: 0;
  326 + display:block;
  327 +}
  328 +.ui-menu .ui-menu {
  329 + margin-top: -3px;
  330 +}
  331 +.ui-menu .ui-menu-item {
  332 + margin:0;
  333 + padding: 0;
  334 + zoom: 1;
  335 + float: left;
  336 + clear: left;
  337 + width: 100%;
  338 +}
  339 +.ui-menu .ui-menu-item a {
  340 + text-decoration:none;
  341 + display:block;
  342 + padding:.2em .4em;
  343 + line-height:1.5;
  344 + zoom:1;
  345 +}
  346 +.ui-menu .ui-menu-item a.ui-state-hover,
  347 +.ui-menu .ui-menu-item a.ui-state-active {
  348 + font-weight: normal;
  349 + margin: -1px;
  350 +}
  351 +/* Button
  352 +----------------------------------*/
  353 +
  354 +.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
  355 +.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
  356 +button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
  357 +.ui-button-icons-only { width: 3.4em; }
  358 +button.ui-button-icons-only { width: 3.7em; }
  359 +
  360 +/*button text element */
  361 +.ui-button .ui-button-text { display: block; line-height: 1.4; }
  362 +.ui-button-text-only .ui-button-text { padding: .4em 1em; }
  363 +.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
  364 +.ui-button-text-icon .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
  365 +.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
  366 +/* no icon support for input elements, provide padding by default */
  367 +input.ui-button { padding: .4em 1em; }
  368 +
  369 +/*button icon element(s) */
  370 +.ui-button-icon-only .ui-icon, .ui-button-text-icon .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
  371 +.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
  372 +.ui-button-text-icon .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
  373 +.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
  374 +
  375 +/*button sets*/
  376 +.ui-buttonset { margin-right: 7px; }
  377 +.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
  378 +
  379 +/* workarounds */
  380 +button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
  381 +
  382 +
  383 +
  384 +
  385 +
  386 +/* Dialog
  387 +----------------------------------*/
  388 +.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
  389 +.ui-dialog .ui-dialog-titlebar { padding: .5em 1em .3em; position: relative; }
  390 +.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; }
  391 +.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
  392 +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
  393 +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
  394 +.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
  395 +.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
  396 +.ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
  397 +.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
  398 +.ui-draggable .ui-dialog-titlebar { cursor: move; }
  399 +/* Slider
  400 +----------------------------------*/
  401 +.ui-slider { position: relative; text-align: left; }
  402 +.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
  403 +.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
  404 +
  405 +.ui-slider-horizontal { height: .8em; }
  406 +.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
  407 +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
  408 +.ui-slider-horizontal .ui-slider-range-min { left: 0; }
  409 +.ui-slider-horizontal .ui-slider-range-max { right: 0; }
  410 +
  411 +.ui-slider-vertical { width: .8em; height: 100px; }
  412 +.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
  413 +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
  414 +.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
  415 +.ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs
  416 +----------------------------------*/
  417 +.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
  418 +.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
  419 +.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
  420 +.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
  421 +.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
  422 +.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
  423 +.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
  424 +.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
  425 +.ui-tabs .ui-tabs-hide { display: none !important; }
  426 +/* Datepicker
  427 +----------------------------------*/
  428 +.ui-datepicker { width: 17em; padding: .2em .2em 0; }
  429 +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
  430 +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
  431 +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
  432 +.ui-datepicker .ui-datepicker-prev { left:2px; }
  433 +.ui-datepicker .ui-datepicker-next { right:2px; }
  434 +.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
  435 +.ui-datepicker .ui-datepicker-next-hover { right:1px; }
  436 +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
  437 +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
  438 +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
  439 +.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
  440 +.ui-datepicker select.ui-datepicker-month,
  441 +.ui-datepicker select.ui-datepicker-year { width: 49%;}
  442 +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
  443 +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
  444 +.ui-datepicker td { border: 0; padding: 1px; }
  445 +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
  446 +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
  447 +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
  448 +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
  449 +
  450 +/* with multiple calendars */
  451 +.ui-datepicker.ui-datepicker-multi { width:auto; }
  452 +.ui-datepicker-multi .ui-datepicker-group { float:left; }
  453 +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
  454 +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
  455 +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
  456 +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
  457 +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
  458 +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
  459 +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
  460 +.ui-datepicker-row-break { clear:both; width:100%; }
  461 +
  462 +/* RTL support */
  463 +.ui-datepicker-rtl { direction: rtl; }
  464 +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
  465 +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
  466 +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
  467 +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
  468 +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
  469 +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
  470 +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
  471 +.ui-datepicker-rtl .ui-datepicker-group { float:right; }
  472 +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
  473 +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
  474 +
  475 +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
  476 +.ui-datepicker-cover {
  477 + display: none; /*sorry for IE5*/
  478 + display/**/: block; /*sorry for IE5*/
  479 + position: absolute; /*must have*/
  480 + z-index: -1; /*must have*/
  481 + filter: mask(); /*must have*/
  482 + top: -4px; /*must have*/
  483 + left: -4px; /*must have*/
  484 + width: 200px; /*must have*/
  485 + height: 200px; /*must have*/
  486 +}/* Progressbar
  487 +----------------------------------*/
  488 +.ui-progressbar { height:2em; text-align: left; }
  489 +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }
0 \ No newline at end of file 490 \ No newline at end of file
test/functional/catalog_controller_test.rb
@@ -39,31 +39,6 @@ class CatalogControllerTest &lt; Test::Unit::TestCase @@ -39,31 +39,6 @@ class CatalogControllerTest &lt; Test::Unit::TestCase
39 assert_kind_of Array, assigns(:products) 39 assert_kind_of Array, assigns(:products)
40 end 40 end
41 41
42 - should 'show product of enterprise' do  
43 - prod = @enterprise.products.create!(:name => 'Product test', :product_category => @product_category)  
44 - get :show, :id => prod.id, :profile => @enterprise.identifier  
45 - assert_tag :tag => 'h1', :content => /#{prod.name}/  
46 - end  
47 -  
48 - should 'link back to index from product show' do  
49 - enterprise = Enterprise.create!(:name => 'test_enterprise_1', :identifier => 'test_enterprise_1', :environment => Environment.default)  
50 - prod = enterprise.products.create!(:name => 'Product test', :product_category => @product_category)  
51 - get :show, :id => prod.id, :profile => enterprise.identifier  
52 - assert_tag({  
53 - :tag => 'div',  
54 - :attributes => {  
55 - :class => /main-block/  
56 - },  
57 - :descendant => {  
58 - :tag => 'a',  
59 - :attributes => {  
60 - :href => "/catalog/#{enterprise.identifier}"  
61 - }  
62 - }  
63 - })  
64 -  
65 - end  
66 -  
67 should 'not give access if environment do not let' do 42 should 'not give access if environment do not let' do
68 env = Environment.default 43 env = Environment.default
69 env.enable('disable_products_for_enterprises') 44 env.enable('disable_products_for_enterprises')
@@ -86,20 +61,6 @@ class CatalogControllerTest &lt; Test::Unit::TestCase @@ -86,20 +61,6 @@ class CatalogControllerTest &lt; Test::Unit::TestCase
86 assert_tag :tag => 'li', :attributes => { :class => 'product_price' }, :content => /Price:/ 61 assert_tag :tag => 'li', :attributes => { :class => 'product_price' }, :content => /Price:/
87 end 62 end
88 63
89 - should 'not show product price when showing product if not informed' do  
90 - prod = @enterprise.products.create!(:name => 'Product test', :product_category => @product_category)  
91 - get :show, :id => prod.id, :profile => @enterprise.identifier  
92 -  
93 - assert_no_tag :tag => 'p', :attributes => { :class => 'product_price' }, :content => /Price:/  
94 - end  
95 -  
96 - should 'show product price when showing product if informed' do  
97 - prod = @enterprise.products.create!(:name => 'Product test', :price => 50.00, :product_category => @product_category)  
98 - get :show, :id => prod.id, :profile => @enterprise.identifier  
99 -  
100 - assert_tag :tag => 'p', :attributes => { :class => 'product_price' }, :content => /Price:/  
101 - end  
102 -  
103 should 'link to assets products wiht product category in the link to product category on index' do 64 should 'link to assets products wiht product category in the link to product category on index' do
104 pc = ProductCategory.create!(:name => 'some product', :environment => enterprise.environment) 65 pc = ProductCategory.create!(:name => 'some product', :environment => enterprise.environment)
105 prod = enterprise.products.create!(:name => 'Product test', :price => 50.00, :product_category => pc) 66 prod = enterprise.products.create!(:name => 'Product test', :price => 50.00, :product_category => pc)
@@ -108,12 +69,4 @@ class CatalogControllerTest &lt; Test::Unit::TestCase @@ -108,12 +69,4 @@ class CatalogControllerTest &lt; Test::Unit::TestCase
108 assert_tag :tag => 'a', :attributes => {:href => /assets\/products\?product_category=#{pc.id}/} 69 assert_tag :tag => 'a', :attributes => {:href => /assets\/products\?product_category=#{pc.id}/}
109 end 70 end
110 71
111 - should 'link to assets products wiht product category in the link to product category on show' do  
112 - pc = ProductCategory.create!(:name => 'some product', :environment => enterprise.environment)  
113 - prod = enterprise.products.create!(:name => 'Product test', :price => 50.00, :product_category => pc)  
114 -  
115 - get :show, :id => prod.id, :profile => enterprise.identifier  
116 - assert_tag :tag => 'a', :attributes => {:href => /assets\/products\?product_category=#{pc.id}/}  
117 - end  
118 -  
119 end 72 end
test/functional/manage_products_controller_test.rb
@@ -65,27 +65,60 @@ class ManageProductsControllerTest &lt; Test::Unit::TestCase @@ -65,27 +65,60 @@ class ManageProductsControllerTest &lt; Test::Unit::TestCase
65 end 65 end
66 end 66 end
67 67
68 - should "get edit form" do  
69 - p = @enterprise.products.create!(:name => 'test product', :product_category => @product_category)  
70 - get 'edit', :profile => @enterprise.identifier, :id => p.id 68 + should "get edit name form" do
  69 + product = fast_create(Product, :name => 'test product', :enterprise_id => @enterprise.id, :product_category_id => @product_category.id)
  70 + get 'edit', :profile => @enterprise.identifier, :id => product.id, :field => 'name'
71 assert_response :success 71 assert_response :success
72 assert assigns(:product) 72 assert assigns(:product)
73 - assert_template 'edit'  
74 - assert_tag :tag => 'form', :attributes => { :action => /edit/ } 73 + assert_tag :tag => 'form', :attributes => { :action => /myprofile\/#{@enterprise.identifier}\/manage_products\/edit\/#{product.id}\?field=name/ }
75 end 74 end
76 75
77 - should "edit product" do  
78 - p = @enterprise.products.create!(:name => 'test product', :product_category => @product_category)  
79 - post 'edit', :profile => @enterprise.identifier, :product => {:name => 'new test product'}, :id => p.id  
80 - assert_response :redirect 76 + should "get edit info form" do
  77 + product = fast_create(Product, :name => 'test product', :enterprise_id => @enterprise.id, :product_category_id => @product_category.id)
  78 + get 'edit', :profile => @enterprise.identifier, :id => product.id, :field => 'info'
  79 + assert_response :success
  80 + assert assigns(:product)
  81 + assert_tag :tag => 'form', :attributes => { :action => /myprofile\/#{@enterprise.identifier}\/manage_products\/edit\/#{product.id}\?field=info/ }
  82 + end
  83 +
  84 + should "get edit image form" do
  85 + product = fast_create(Product, :name => 'test product', :enterprise_id => @enterprise.id, :product_category_id => @product_category.id)
  86 + get 'edit', :profile => @enterprise.identifier, :id => product.id, :field => 'image'
  87 + assert_response :success
  88 + assert assigns(:product)
  89 + assert_tag :tag => 'form', :attributes => { :action => /myprofile\/#{@enterprise.identifier}\/manage_products\/edit\/#{product.id}\?field=image/ }
  90 + end
  91 +
  92 + should "edit product name" do
  93 + product = fast_create(Product, :name => 'test product', :enterprise_id => @enterprise.id, :product_category_id => @product_category.id)
  94 + post :edit, :profile => @enterprise.identifier, :product => {:name => 'new test product'}, :id => product.id, :field => 'name'
  95 + assert_response :success
81 assert assigns(:product) 96 assert assigns(:product)
82 assert ! assigns(:product).new_record? 97 assert ! assigns(:product).new_record?
83 - assert_equal p, Product.find_by_name('new test product') 98 + assert_equal product, Product.find_by_name('new test product')
  99 + end
  100 +
  101 + should "edit product description" do
  102 + product = fast_create(Product, :name => 'test product', :enterprise_id => @enterprise.id, :product_category_id => @product_category.id, :description => 'My product is very good')
  103 + post :edit, :profile => @enterprise.identifier, :product => {:description => 'A very good product!'}, :id => product.id, :field => 'info'
  104 + assert_response :success
  105 + assert assigns(:product)
  106 + assert ! assigns(:product).new_record?
  107 + assert_equal 'A very good product!', Product.find_by_name('test product').description
  108 + end
  109 +
  110 + should "edit product image" do
  111 + product = fast_create(Product, :name => 'test product', :enterprise_id => @enterprise.id, :product_category_id => @product_category.id)
  112 + post :edit, :profile => @enterprise.identifier, :product => { :image_builder => { :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png') } }, :id => product.id, :field => 'image'
  113 + assert_response :success
  114 + assert assigns(:product)
  115 + assert ! assigns(:product).new_record?
  116 + assert_equal 'rails.png', Product.find_by_name('test product').image.filename
84 end 117 end
85 118
86 should "not edit to invalid parameters" do 119 should "not edit to invalid parameters" do
87 - p = @enterprise.products.create!(:name => 'test product', :product_category => @product_category)  
88 - post 'edit', :profile => @enterprise.identifier, :product => {:product_category => nil}, :id => p.id 120 + product = fast_create(Product, :name => 'test product', :enterprise_id => @enterprise.id, :product_category_id => @product_category.id)
  121 + post 'edit_category', :profile => @enterprise.identifier, :product => {:product_category => nil}, :id => product.id
89 assert_response :success 122 assert_response :success
90 assert assigns(:product) 123 assert assigns(:product)
91 assert ! assigns(:product).valid? 124 assert ! assigns(:product).valid?
@@ -131,32 +164,16 @@ class ManageProductsControllerTest &lt; Test::Unit::TestCase @@ -131,32 +164,16 @@ class ManageProductsControllerTest &lt; Test::Unit::TestCase
131 end 164 end
132 end 165 end
133 166
134 - should 'show thumbnail image when edit product' do  
135 - p = @enterprise.products.create!(:name => 'test product1', :product_category => @product_category, :image_builder => {  
136 - :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')  
137 - })  
138 - get 'edit', :profile => @enterprise.identifier, :id => p.id  
139 - assert_tag :tag => 'img', :attributes => { :src => /#{p.image.public_filename(:thumb)}/ }  
140 - end  
141 -  
142 - should 'show change image link above thumbnail image' do  
143 - p = @enterprise.products.create!(:name => 'test product1', :product_category => @product_category, :image_builder => {  
144 - :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')  
145 - })  
146 - get 'edit', :profile => @enterprise.identifier, :id => p.id  
147 - assert_tag :tag => 'a', :attributes => { :href => '#' }, :content => 'Change image'  
148 - end  
149 -  
150 should 'filter html from name of product' do 167 should 'filter html from name of product' do
151 category = fast_create(ProductCategory, :name => 'Category 1') 168 category = fast_create(ProductCategory, :name => 'Category 1')
152 post 'new', :profile => @enterprise.identifier, :product => { :name => "<b id='html_name'>name bold</b>", :product_category_id => category.id } 169 post 'new', :profile => @enterprise.identifier, :product => { :name => "<b id='html_name'>name bold</b>", :product_category_id => category.id }
153 assert_sanitized assigns(:product).name 170 assert_sanitized assigns(:product).name
154 end 171 end
155 172
156 - should 'filter html from description of product' do 173 + should 'filter html with whit list from description of product' do
157 category = fast_create(ProductCategory, :name => 'Category 1') 174 category = fast_create(ProductCategory, :name => 'Category 1')
158 post 'new', :profile => @enterprise.identifier, :product => { :name => 'name', :description => "<b id='html_descr'>descr bold</b>", :product_category_id => category.id } 175 post 'new', :profile => @enterprise.identifier, :product => { :name => 'name', :description => "<b id='html_descr'>descr bold</b>", :product_category_id => category.id }
159 - assert_sanitized assigns(:product).description 176 + assert_equal "<b>descr bold</b>", assigns(:product).description
160 end 177 end
161 178
162 should 'not let users in if environment do not let' do 179 should 'not let users in if environment do not let' do
@@ -179,15 +196,6 @@ class ManageProductsControllerTest &lt; Test::Unit::TestCase @@ -179,15 +196,6 @@ class ManageProductsControllerTest &lt; Test::Unit::TestCase
179 assert_equivalent ProductCategory.top_level_for(pc1.environment), assigns(:categories) 196 assert_equivalent ProductCategory.top_level_for(pc1.environment), assigns(:categories)
180 end 197 end
181 198
182 - should 'links to products asset for product category link when showing' do  
183 - pc = fast_create(ProductCategory, :name => 'test_category')  
184 - p = @enterprise.products.create!(:name => 'test product', :product_category => pc)  
185 -  
186 - get :show, :profile => @enterprise.identifier, :id => p.id  
187 -  
188 - assert_tag :tag => 'a', :attributes => {:href => /assets\/products\?product_category=#{pc.id}/}  
189 - end  
190 -  
191 should 'increase level while navigate on hierarchy categories' do 199 should 'increase level while navigate on hierarchy categories' do
192 category_level0 = fast_create(ProductCategory, :name => 'Products', :environment_id => @environment.id) 200 category_level0 = fast_create(ProductCategory, :name => 'Products', :environment_id => @environment.id)
193 category_level1 = fast_create(ProductCategory, :parent_id => category_level0.id, :name => 'Shoes', :environment_id => @environment.id) 201 category_level1 = fast_create(ProductCategory, :parent_id => category_level0.id, :name => 'Shoes', :environment_id => @environment.id)
@@ -232,4 +240,58 @@ class ManageProductsControllerTest &lt; Test::Unit::TestCase @@ -232,4 +240,58 @@ class ManageProductsControllerTest &lt; Test::Unit::TestCase
232 end 240 end
233 end 241 end
234 242
  243 + should 'show product of enterprise' do
  244 + prod = @enterprise.products.create!(:name => 'Product test', :product_category => @product_category)
  245 + get :show, :id => prod.id, :profile => @enterprise.identifier
  246 + assert_tag :tag => 'h2', :content => /#{prod.name}/
  247 + end
  248 +
  249 + should 'link back to index from product show' do
  250 + enterprise = Enterprise.create!(:name => 'test_enterprise_1', :identifier => 'test_enterprise_1', :environment => Environment.default)
  251 + prod = enterprise.products.create!(:name => 'Product test', :product_category => @product_category)
  252 + get :show, :id => prod.id, :profile => enterprise.identifier
  253 + assert_tag({
  254 + :tag => 'div',
  255 + :attributes => {
  256 + :class => /main-block/
  257 + },
  258 + :descendant => {
  259 + :tag => 'a',
  260 + :attributes => {
  261 + :href => "/myprofile/#{enterprise.identifier}/manage_products",
  262 + },
  263 + :content => /Back to the product listing/
  264 + }
  265 + })
  266 + end
  267 +
  268 + should 'not show product price when showing product if not informed' do
  269 + product = fast_create(Product, :name => 'test product', :enterprise_id => @enterprise.id, :product_category_id => @product_category.id)
  270 + get :show, :id => product.id, :profile => @enterprise.identifier
  271 +
  272 + assert_no_tag :tag => 'span', :attributes => { :class => 'product_price' }, :content => /Price:/
  273 + end
  274 +
  275 + should 'show product price when showing product if unit was informed' do
  276 + prod = @enterprise.products.create!(:name => 'Product test', :price => 50.00, :unit => 'unit', :product_category => @product_category)
  277 + get :show, :id => prod.id, :profile => @enterprise.identifier
  278 +
  279 + assert_tag :tag => 'span', :attributes => { :class => 'field-name' }, :content => /Price:/
  280 + end
  281 +
  282 + should 'show product price when showing product if discount was informed' do
  283 + prod = @enterprise.products.create!(:name => 'Product test', :price => 50.00, :discount => 3.50, :unit => 'unit', :product_category => @product_category)
  284 + get :show, :id => prod.id, :profile => @enterprise.identifier
  285 +
  286 + assert_tag :tag => 'span', :attributes => { :class => 'field-name' }, :content => /List price:/
  287 + assert_tag :tag => 'span', :attributes => { :class => 'field-name' }, :content => /On sale:/
  288 + end
  289 +
  290 + should 'not show product price when showing product if unit not informed' do
  291 + prod = @enterprise.products.create!(:name => 'Product test', :price => 50.00, :product_category => @product_category)
  292 + get :show, :id => prod.id, :profile => @enterprise.identifier
  293 +
  294 + assert_no_tag :tag => 'span', :attributes => { :class => 'product_price' }, :content => /Price:/
  295 + end
  296 +
235 end 297 end
test/integration/routing_test.rb
@@ -160,12 +160,6 @@ class RoutingTest &lt; ActionController::IntegrationTest @@ -160,12 +160,6 @@ class RoutingTest &lt; ActionController::IntegrationTest
160 160
161 def test_catalog_routing 161 def test_catalog_routing
162 assert_routing('/catalog/colivre', :controller => 'catalog', :action => 'index', :profile => 'colivre') 162 assert_routing('/catalog/colivre', :controller => 'catalog', :action => 'index', :profile => 'colivre')
163 - assert_routing('/catalog/colivre/1234', :controller => 'catalog', :action => 'show', :profile => 'colivre', :id => '1234')  
164 - end  
165 -  
166 - def test_catalog_with_dot_routing  
167 - assert_routing('/catalog/profile.withdot', :controller => 'catalog', :action => 'index', :profile => 'profile.withdot')  
168 - assert_routing('/catalog/profile.withdot/1234', :controller => 'catalog', :action => 'show', :profile => 'profile.withdot', :id => '1234')  
169 end 163 end
170 164
171 def test_hosted_domain_routing 165 def test_hosted_domain_routing
test/unit/application_helper_test.rb
@@ -579,11 +579,6 @@ class ApplicationHelperTest &lt; Test::Unit::TestCase @@ -579,11 +579,6 @@ class ApplicationHelperTest &lt; Test::Unit::TestCase
579 assert_equal 'filename.mp3', short_filename('filename.mp3') 579 assert_equal 'filename.mp3', short_filename('filename.mp3')
580 end 580 end
581 581
582 - include ActionView::Helpers::NumberHelper  
583 - should 'format float to money as Brazillian currency' do  
584 - assert_equal 'R$10,00', float_to_currency(10.0)  
585 - end  
586 -  
587 protected 582 protected
588 583
589 def url_for(args = {}) 584 def url_for(args = {})
test/unit/certifier_test.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class CertifierTest < Test::Unit::TestCase
  4 +
  5 + should 'have link' do
  6 + certifier = Certifier.new
  7 +
  8 + assert_equal '', certifier.link
  9 +
  10 + certifier.link = 'http://noosfero.org'
  11 + assert_equal 'http://noosfero.org', certifier.link
  12 + end
  13 +
  14 +end
test/unit/enterprise_homepage_test.rb
@@ -52,7 +52,7 @@ class EnterpriseHomepageTest &lt; Test::Unit::TestCase @@ -52,7 +52,7 @@ class EnterpriseHomepageTest &lt; Test::Unit::TestCase
52 a = EnterpriseHomepage.new(:name => 'article homepage') 52 a = EnterpriseHomepage.new(:name => 'article homepage')
53 ent.articles << a 53 ent.articles << a
54 result = a.to_html 54 result = a.to_html
55 - assert_match /catalog\/test_enterprise\/#{prod.id}/, result 55 + assert_match /\/test_enterprise\/manage_products\/show\/#{prod.id}/, result
56 end 56 end
57 57
58 should 'can display hits' do 58 should 'can display hits' do
test/unit/environment_test.rb
@@ -948,4 +948,28 @@ class EnvironmentTest &lt; Test::Unit::TestCase @@ -948,4 +948,28 @@ class EnvironmentTest &lt; Test::Unit::TestCase
948 assert !v.has_terms_of_use? 948 assert !v.has_terms_of_use?
949 end 949 end
950 950
  951 + should 'have currency unit attribute' do
  952 + env = Environment.new
  953 + assert_equal env.currency_unit, '$'
  954 +
  955 + env.currency_unit = 'R$'
  956 + assert_equal 'R$', env.currency_unit
  957 + end
  958 +
  959 + should 'have currency separator attribute' do
  960 + env = Environment.new
  961 + assert_equal env.currency_separator, '.'
  962 +
  963 + env.currency_separator = ','
  964 + assert_equal ',', env.currency_separator
  965 + end
  966 +
  967 + should 'have currency delimiter attribute' do
  968 + env = Environment.new
  969 + assert_equal env.currency_delimiter, ','
  970 +
  971 + env.currency_delimiter = '.'
  972 + assert_equal '.', env.currency_delimiter
  973 + end
  974 +
951 end 975 end
test/unit/manage_products_helper_test.rb
@@ -38,6 +38,63 @@ class ManageProductsHelperTest &lt; Test::Unit::TestCase @@ -38,6 +38,63 @@ class ManageProductsHelperTest &lt; Test::Unit::TestCase
38 assert_tag_in_string select_for_categories(category_1.children(true), 1), :tag => 'select', :attributes => {:id => 'category_id'} 38 assert_tag_in_string select_for_categories(category_1.children(true), 1), :tag => 'select', :attributes => {:id => 'category_id'}
39 end 39 end
40 40
  41 + include ActionView::Helpers::NumberHelper
  42 + should 'format price to environment currency' do
  43 + @environment.currency_unit = "R$"
  44 + @environment.currency_separator = ","
  45 + @environment.currency_delimiter = "."
  46 + @environment.save
  47 + assert_equal 'R$ 10,00', float_to_currency(10.0)
  48 + end
  49 +
  50 + should 'not display link to edit product when user does not have permission' do
  51 + user = mock
  52 + user.expects(:has_permission?).with(anything, anything).returns(false)
  53 + @controller = mock
  54 + @controller.expects(:user).returns(user).at_least_once
  55 + @controller.expects(:profile).returns(mock)
  56 + category = fast_create(ProductCategory, :name => 'Category 1', :environment_id => @environment.id)
  57 + product = fast_create(Product, :product_category_id => category.id)
  58 + assert_equal '', edit_product_link(product, 'field', 'link to edit')
  59 + end
  60 +
  61 + should 'display link to edit product when user has permission' do
  62 + user = mock
  63 + user.expects(:has_permission?).with(anything, anything).returns(true)
  64 + @controller = mock
  65 + @controller.expects(:user).returns(user).at_least_once
  66 + @controller.expects(:profile).returns(mock)
  67 + category = fast_create(ProductCategory, :name => 'Category 1', :environment_id => @environment.id)
  68 + product = fast_create(Product, :product_category_id => category.id)
  69 +
  70 + expects(:link_to_remote).with('link to edit', {:update => "product-name", :url => {:controller => 'manage_products', :action => 'edit', :id => product.id, :field => 'name'}, :method => :get}, anything).returns('LINK')
  71 +
  72 + assert_equal 'LINK', edit_product_link(product, 'name', 'link to edit')
  73 + end
  74 +
  75 + should 'not display link to edit product category when user does not have permission' do
  76 + user = mock
  77 + user.expects(:has_permission?).with(anything, anything).returns(false)
  78 + @controller = mock
  79 + @controller.expects(:user).returns(user).at_least_once
  80 + @controller.expects(:profile).returns(mock)
  81 + category = fast_create(ProductCategory, :name => 'Category 1', :environment_id => @environment.id)
  82 + product = fast_create(Product, :product_category_id => category.id)
  83 + assert_equal '', edit_product_category_link(product)
  84 + end
  85 +
  86 + should 'display link to edit product category when user has permission' do
  87 + user = mock
  88 + user.expects(:has_permission?).with(anything, anything).returns(true)
  89 + @controller = mock
  90 + @controller.expects(:user).returns(user).at_least_once
  91 + @controller.expects(:profile).returns(mock)
  92 + category = fast_create(ProductCategory, :name => 'Category 1', :environment_id => @environment.id)
  93 + product = fast_create(Product, :product_category_id => category.id)
  94 +
  95 + assert_tag_in_string edit_product_category_link(product), {:tag => 'a', :content => 'Change category'}
  96 + end
  97 +
41 protected 98 protected
42 include NoosferoTestHelper 99 include NoosferoTestHelper
43 100
test/unit/product_test.rb
@@ -20,6 +20,27 @@ class ProductTest &lt; Test::Unit::TestCase @@ -20,6 +20,27 @@ class ProductTest &lt; Test::Unit::TestCase
20 end 20 end
21 end 21 end
22 22
  23 + should 'display category name if name is nil' do
  24 + p = fast_create(Product, :name => nil)
  25 + p.expects(:category_name).returns('Software')
  26 + assert_equal 'Software', p.name
  27 + end
  28 +
  29 + should 'display category name if name is blank' do
  30 + p = fast_create(Product, :name => '')
  31 + p.expects(:category_name).returns('Software')
  32 + assert_equal 'Software', p.name
  33 + end
  34 +
  35 + should 'set nil to name if name is equal to category name' do
  36 + p = fast_create(Product)
  37 + p.expects(:category_name).returns('Software').at_least_once
  38 + p.name = 'Software'
  39 + p.save
  40 + assert_equal 'Software', p.name
  41 + assert_equal nil, p[:name]
  42 + end
  43 +
23 should 'list recent products' do 44 should 'list recent products' do
24 enterprise = fast_create(Enterprise, :name => "My enterprise", :identifier => 'my-enterprise') 45 enterprise = fast_create(Enterprise, :name => "My enterprise", :identifier => 'my-enterprise')
25 Product.delete_all 46 Product.delete_all
@@ -113,7 +134,7 @@ class ProductTest &lt; Test::Unit::TestCase @@ -113,7 +134,7 @@ class ProductTest &lt; Test::Unit::TestCase
113 134
114 product.expects(:id).returns(999) 135 product.expects(:id).returns(999)
115 product.expects(:enterprise).returns(enterprise) 136 product.expects(:enterprise).returns(enterprise)
116 - assert_equal({:controller => 'catalog', :action => 'show', :id => 999}, product.url) 137 + assert_equal({:controller => 'manage_products', :action => 'show', :id => 999}, product.url)
117 end 138 end
118 139
119 should 'categorize also with product categorization' do 140 should 'categorize also with product categorization' do
@@ -178,12 +199,27 @@ class ProductTest &lt; Test::Unit::TestCase @@ -178,12 +199,27 @@ class ProductTest &lt; Test::Unit::TestCase
178 end 199 end
179 end 200 end
180 201
  202 + should 'accept discount in american\'s or brazilian\'s currency format' do
  203 + [
  204 + [12.34, 12.34],
  205 + ["12.34", 12.34],
  206 + ["12,34", 12.34],
  207 + ["12.345.678,90", 12345678.90],
  208 + ["12,345,678.90", 12345678.90],
  209 + ["12.345.678", 12345678.00],
  210 + ["12,345,678", 12345678.00]
  211 + ].each do |input, output|
  212 + product = Product.new(:discount => input)
  213 + assert_equal output, product.discount
  214 + end
  215 + end
  216 +
181 should 'strip name with malformed HTML when sanitize' do 217 should 'strip name with malformed HTML when sanitize' do
182 product = Product.new(:product_category => @product_category) 218 product = Product.new(:product_category => @product_category)
183 product.name = "<h1 Bla </h1>" 219 product.name = "<h1 Bla </h1>"
184 product.valid? 220 product.valid?
185 221
186 - assert_equal '', product.name 222 + assert_equal @product_category.name, product.name
187 end 223 end
188 224
189 should 'escape malformed html tags' do 225 should 'escape malformed html tags' do
@@ -208,4 +244,23 @@ class ProductTest &lt; Test::Unit::TestCase @@ -208,4 +244,23 @@ class ProductTest &lt; Test::Unit::TestCase
208 assert product.errors.invalid?(:product_category) 244 assert product.errors.invalid?(:product_category)
209 end 245 end
210 246
  247 + should 'format values to float with 2 decimals' do
  248 + ent = fast_create(Enterprise, :name => 'test ent 1', :identifier => 'test_ent1')
  249 + product = fast_create(Product, :enterprise_id => ent.id, :price => 12.994, :discount => 1.994)
  250 +
  251 + assert_equal "12.99", product.formatted_value(:price)
  252 + assert_equal "1.99", product.formatted_value(:discount)
  253 + end
  254 +
  255 + should 'calculate price with discount' do
  256 + ent = fast_create(Enterprise, :name => 'test ent 1', :identifier => 'test_ent1')
  257 + product = fast_create(Product, :enterprise_id => ent.id, :price => 12.994, :discount => 1.994)
  258 +
  259 + assert_equal 11.00, product.price_with_discount
  260 + end
  261 +
  262 + should 'have default image' do
  263 + product = Product.new
  264 + assert_equal '/images/icons-app/product-default-pic-thumb.png', product.default_image
  265 + end
211 end 266 end