creating page in progress
@ -12,6 +12,18 @@ class Admin::BaseController < ::ApplicationController
|
||||
|
||||
protected
|
||||
|
||||
def flash_success!
|
||||
flash[:success] = translate_flash_msg(:successful)
|
||||
end
|
||||
|
||||
def flash_error!
|
||||
flash[:error] = translate_flash_msg(:failed)
|
||||
end
|
||||
|
||||
def translate_flash_msg(kind)
|
||||
t("#{kind.to_s}_#{action_name}", :scope => [:admin, controller_name.underscore.gsub('/', '.'), :messages])
|
||||
end
|
||||
|
||||
def self.sections(main, sub = nil)
|
||||
write_inheritable_attribute(:sections, { :main => main, :sub => sub })
|
||||
end
|
||||
|
@ -3,6 +3,52 @@ class Admin::PagesController < Admin::BaseController
|
||||
sections 'contents'
|
||||
|
||||
def index
|
||||
@pages = Page.all
|
||||
end
|
||||
|
||||
def new
|
||||
@page = current_site.pages.build
|
||||
end
|
||||
|
||||
def edit
|
||||
@page = current_site.pages.find(params[:id])
|
||||
end
|
||||
|
||||
def create
|
||||
@page = current_site.pages.build(params[:page])
|
||||
|
||||
if @page.save
|
||||
flash_success!
|
||||
redirect_to edit_admin_page_url(@page)
|
||||
else
|
||||
flash_error!
|
||||
render :action => 'new'
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@page = current_site.pages.find(params[:id])
|
||||
|
||||
if @page.update_attributes(params[:page])
|
||||
flash_success!
|
||||
redirect_to edit_admin_page_url(@page)
|
||||
else
|
||||
flash_error!
|
||||
render :action => "edit"
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@page = current_site.pages.find(params[:id])
|
||||
|
||||
begin
|
||||
@page.destroy
|
||||
flash_success!
|
||||
rescue Exception => e
|
||||
flash[:error] = e.to_s
|
||||
end
|
||||
|
||||
redirect_to admin_pages_url
|
||||
end
|
||||
|
||||
end
|
@ -1,8 +1,15 @@
|
||||
module Admin::BaseHelper
|
||||
|
||||
|
||||
def admin_menu_item(name, url)
|
||||
label = content_tag(:em) + escape_once(' ') + t("admin.shared.menu.#{name}")
|
||||
content_tag(:li, link_to(label, url), :class => name.dasherize)
|
||||
end
|
||||
|
||||
def admin_button_tag(text, url, options = {})
|
||||
text = text.is_a?(Symbol) ? t(".#{text}") : text
|
||||
link_to(url, options) do
|
||||
content_tag(:span, text)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -19,6 +19,16 @@ module ApplicationHelper
|
||||
end
|
||||
end
|
||||
|
||||
def growl_message
|
||||
if not flash.empty?
|
||||
%{
|
||||
$(document).ready(function() {
|
||||
$.growl("#{flash.keys.first}", "#{flash.values.first}");
|
||||
});
|
||||
}.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def nocoffee_tag
|
||||
link_to content_tag(:em, 'no') + 'Coffee', 'http://www.nocoffee.fr', :id => 'nocoffee'
|
||||
end
|
||||
|
37
app/models/page.rb
Normal file
@ -0,0 +1,37 @@
|
||||
class Page
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
|
||||
## fields ##
|
||||
field :title
|
||||
field :path
|
||||
field :published, :type => Boolean, :default => false
|
||||
field :keywords
|
||||
field :description
|
||||
|
||||
## associations ##
|
||||
belongs_to_related :site
|
||||
embeds_many :parts, :class_name => 'PagePart'
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :site, :title, :path
|
||||
validates_uniqueness_of :path, :scope => :site_id
|
||||
validate :path_must_not_begin_with_reserverd_keywords
|
||||
|
||||
## callbacks ##
|
||||
before_create :add_body_part
|
||||
|
||||
## named scopes ##
|
||||
|
||||
## methods ##
|
||||
|
||||
def add_body_part
|
||||
self.parts.build :name => 'body', :value => '---body here---'
|
||||
end
|
||||
|
||||
def path_must_not_begin_with_reserverd_keywords
|
||||
if (self.path =~ /^(#{Locomotive.config.forbidden_paths.join('|')})\//) == 0
|
||||
errors.add(:path, :reserved_keywords)
|
||||
end
|
||||
end
|
||||
end
|
20
app/models/page_part.rb
Normal file
@ -0,0 +1,20 @@
|
||||
class PagePart
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
|
||||
## fields ##
|
||||
field :name, :type => String
|
||||
field :slug, :type => String
|
||||
field :value, :type => String
|
||||
field :disabled, :type => Boolean, :default => false
|
||||
|
||||
## associations ##
|
||||
embedded_in :page, :inverse_of => :parts
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :name, :slug
|
||||
|
||||
## callbacks ##
|
||||
before_validate { |p| p.slug ||= p.name.slugify if p.name.present? }
|
||||
|
||||
end
|
@ -8,6 +8,9 @@ class Site
|
||||
field :domains, :type => Array, :default => []
|
||||
field :account_ids, :type => Array, :default => []
|
||||
|
||||
## associations ##
|
||||
has_many_related :pages
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :name, :subdomain
|
||||
validates_uniqueness_of :subdomain
|
||||
|
@ -0,0 +1,69 @@
|
||||
= f.foldable_inputs :name => :information do
|
||||
|
||||
= f.input :title
|
||||
|
||||
= f.custom_input :path, :css => 'path' do
|
||||
& /#{f.text_field :path}.html
|
||||
|
||||
= f.custom_input :published, :css => 'toggle' do
|
||||
= f.check_box :published
|
||||
|
||||
= f.foldable_inputs :name => :meta do
|
||||
|
||||
= f.input :keywords
|
||||
= f.input :description
|
||||
|
||||
|
||||
|
||||
|
||||
/ <% f.foldable_inputs :name => :information do %>
|
||||
/ <%= f.input :title, :required => false %>
|
||||
/
|
||||
/ <% f.custom_input :path, :css => 'path' do %>
|
||||
/ /<%= f.text_field :path %>.html
|
||||
/ <% end %>
|
||||
/
|
||||
/ <% f.custom_input :layout do %>
|
||||
/ <%= f.select :layout_id, current_site.layouts.all.collect { |l| [l.name, l.id] }, :include_blank => true %>
|
||||
/ <% end %>
|
||||
/
|
||||
/ <%= f.input :keywords, :required => false %>
|
||||
/ <%= f.input :description, :required => false %>
|
||||
/
|
||||
/ <% f.custom_input :cache_expires_in do %>
|
||||
/ <%= f.select :cache_expires_in, options_for_page_cache_expiration %>
|
||||
/ <% end %>
|
||||
/
|
||||
/ <% f.custom_input :visible, :css => 'checkbox' do %>
|
||||
/ <%= f.check_box :visible %>
|
||||
/ <% end %>
|
||||
/ <% end %>
|
||||
/
|
||||
/ <% unless f.object.new_record? %>
|
||||
/ <div id="page-parts">
|
||||
/ <div class="jcarousel-control">
|
||||
/ <% if body_part? %>
|
||||
/ <a class="part-0 on"><%= t('admin.pages.form.body') %></a><span></span><% end -%><% @page.parts.active.each_with_index do |p, index| -%><a class="part-<%= index + (body_part? ? 1 : 0) %> <%= 'on' if !body_part? && index == 0 %>"><%= p.slug.humanize %></a><span></span>
|
||||
/ <% end %>
|
||||
/ </div>
|
||||
/
|
||||
/ <div class="wrapper">
|
||||
/ <ul>
|
||||
/ <% if body_part? %>
|
||||
/ <li><code><%= f.text_area :body %></code></li>
|
||||
/ <% end %>
|
||||
/
|
||||
/ <% f.fields_for :parts do |g| %>
|
||||
/ <% unless g.object.disabled? %>
|
||||
/ <li><code><%= g.text_area :body, :class => 'big' %></code></li>
|
||||
/ <% end %>
|
||||
/ <% end %>
|
||||
/ </ul>
|
||||
/ </div>
|
||||
/ </div>
|
||||
/
|
||||
/ <% content_for :head do %>
|
||||
/ <%= javascript_include_tag 'admin/plugins/iphoneSwitch', 'admin/plugins/checkbox', 'admin/plugins/carousel', 'codemirror/codemirror', 'admin/pages' %>
|
||||
/ <%= stylesheet_link_tag 'carousel', 'admin/page_parts' %>
|
||||
/ <% end %>
|
||||
/ <% end %>
|
@ -0,0 +1,52 @@
|
||||
- title link_to(@page.title.blank? ? @page.title_was : @page.title, '#', :rel => 'page_title', :title => t('.ask_for_title'), :class => 'editable')
|
||||
|
||||
- content_for :submenu do
|
||||
= render 'admin/shared/contents_menu'
|
||||
|
||||
- content_for :buttons do
|
||||
= admin_button_tag :show, '#', :class => 'show'
|
||||
|
||||
%p= t('.help')
|
||||
|
||||
= semantic_form_for @page, :url => admin_page_url(@page), :html => { :class => 'shortcut-enabled' } do |form|
|
||||
|
||||
= render 'form', :f => form
|
||||
|
||||
= render 'admin/shared/form_actions', :back_url => admin_pages_url, :button_label => :update
|
||||
|
||||
|
||||
/ <% content_for :title do %>
|
||||
/ <%= link_to @page.title.blank? ? @page.title_was : @page.title, '#', :class => 'editable' %>
|
||||
/ <% end %>
|
||||
/
|
||||
/ <% content_for :buttons do %>
|
||||
/ <% show_page_button(@page) %>
|
||||
/ <% end %>
|
||||
/
|
||||
/ <p><%= t('admin.pages.edit.help') %></p>
|
||||
/
|
||||
/ <% semantic_form_for @page, :url => admin_page_url(@page), :html => { :class => 'save-shortcut' } do |form| %>
|
||||
/
|
||||
/ <%= render :partial => 'form', :locals => { :f => form } %>
|
||||
/
|
||||
/ <div class="actions">
|
||||
/ <div class="span-12">
|
||||
/ <p>
|
||||
/ <%= link_to '← ' + t('admin.common.buttons.back'), admin_pages_url %>
|
||||
/ </p>
|
||||
/ </div>
|
||||
/
|
||||
/ <div class="span-12 last">
|
||||
/ <p>
|
||||
/ <button class="button light" type="submit">
|
||||
/ <span><%= t('admin.common.buttons.update') %></span>
|
||||
/ </button>
|
||||
/ </p>
|
||||
/ </div>
|
||||
/ <div class="clear"></div>
|
||||
/ </div>
|
||||
/ <% end %>
|
||||
/
|
||||
/ <% content_for :submenu do %>
|
||||
/ <%= render :partial => '/shared/admin/contents_menu' %>
|
||||
/ <% end %>
|
@ -1,5 +1,17 @@
|
||||
- title t('.title')
|
||||
|
||||
|
||||
- content_for :submenu do
|
||||
= render 'admin/shared/contents_menu'
|
||||
|
||||
- content_for :buttons do
|
||||
= admin_button_tag :new, new_admin_page_url, :class => 'add'
|
||||
|
||||
%p= t('.help')
|
||||
|
||||
- if @pages.empty?
|
||||
%p.no-items= t('.no_items', :url => new_admin_page_url)
|
||||
- else
|
||||
%ul#pages-list
|
||||
- @pages.each do |page|
|
||||
%li
|
||||
= link_to page.title, edit_admin_page_url(page)
|
@ -0,0 +1,12 @@
|
||||
- title t('.title')
|
||||
|
||||
- content_for :submenu do
|
||||
= render 'admin/shared/contents_menu'
|
||||
|
||||
%p= t('.help')
|
||||
|
||||
= semantic_form_for @page, :url => admin_pages_url do |form|
|
||||
|
||||
= render 'form', :f => form
|
||||
|
||||
= render 'admin/shared/form_actions', :back_url => admin_pages_url, :button_label => :create
|
11
app/views/admin/shared/_form_actions.html.haml
Normal file
@ -0,0 +1,11 @@
|
||||
.actions
|
||||
.span-12
|
||||
%p
|
||||
= link_to escape_once('← ') + t('.back'), back_url
|
||||
|
||||
.span-12.last
|
||||
%p
|
||||
%button.button.light{ :type => 'submit' }
|
||||
%span= button_label.is_a?(Symbol) ? t(".#{button_label}") : button_label
|
||||
|
||||
.clear
|
@ -2,12 +2,17 @@
|
||||
|
||||
= csrf_meta_tag
|
||||
|
||||
%meta{ :name => 'locale', :content => I18n.locale }
|
||||
|
||||
= stylesheet_link_tag 'blueprint/screen', :media => 'screen'
|
||||
/ [if IE]
|
||||
= stylesheet_link_tag('blueprint/ie', :media => 'screen')
|
||||
|
||||
= stylesheet_link_tag 'admin/layout', 'admin/menu', 'admin/buttons', 'admin/formtastic', 'admin/formtastic_changes', 'admin/application', :media => 'screen', :cache => Rails.env.production?
|
||||
= stylesheet_link_tag 'admin/layout', 'admin/plugins/toggle', 'admin/menu', 'admin/buttons', 'admin/formtastic', 'admin/formtastic_changes', 'admin/application', :media => 'screen', :cache => Rails.env.production?
|
||||
|
||||
= javascript_include_tag 'jquery', 'jquery.ui', 'rails', :cache => Rails.env.production?
|
||||
= javascript_include_tag 'jquery', 'jquery.ui', 'rails', 'admin/plugins/toggle', 'admin/plugins/growl', 'admin/application', :cache => Rails.env.production?
|
||||
|
||||
%script{ :type => 'text/javascript' }
|
||||
= find_and_preserve(growl_message)
|
||||
|
||||
= yield :head
|
@ -1 +1,4 @@
|
||||
Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true
|
||||
Formtastic::SemanticFormHelper.builder = MiscFormBuilder
|
||||
|
||||
Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
require 'lib/locomotive.rb'
|
||||
require 'lib/core_ext.rb'
|
||||
|
||||
Locomotive.configure do |config|
|
||||
config.default_domain = 'example.com'
|
||||
|
@ -11,3 +11,19 @@ Mongoid.configure do |config|
|
||||
# Mongo::Connection.new(host, @settings["slave_two"]["port"], :slave_ok => true).db(name)
|
||||
# ]
|
||||
end
|
||||
|
||||
## various patches
|
||||
|
||||
# Enabling scope in validates_uniqueness_of validation
|
||||
module Mongoid #:nodoc:
|
||||
module Validations #:nodoc:
|
||||
class UniquenessValidator < ActiveModel::EachValidator
|
||||
def validate_each(document, attribute, value, scope = nil)
|
||||
criteria = { attribute => value, :_id.ne => document._id }
|
||||
criteria[scope] = document.send(scope) if scope
|
||||
return if document.class.where(criteria).empty?
|
||||
document.errors.add(attribute, :taken, :default => options[:message], :value => value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -33,11 +33,26 @@ en:
|
||||
footer:
|
||||
developed_by: Developed by
|
||||
powered_by: and Powered by
|
||||
form_actions:
|
||||
back: Back without saving
|
||||
create: Create
|
||||
update: Update
|
||||
|
||||
pages:
|
||||
index:
|
||||
title: Listing pages
|
||||
no_items: "There are no pages for now. Just click <a href=\"{{url}}\">here</a> to create the first one."
|
||||
new: new page
|
||||
|
||||
formtastic:
|
||||
titles:
|
||||
information: General information
|
||||
meta: Meta
|
||||
hints:
|
||||
page:
|
||||
keywords: "Meta keywords used within the head tag of the page. They are separeted by an empty space. Required for SEO."
|
||||
description: "Meta description used within the head tag of the page. Required for SEO."
|
||||
|
||||
buttons:
|
||||
login: Log in
|
||||
send_password: Send
|
||||
|
3
doc/NICE_TO_HAVE
Normal file
@ -0,0 +1,3 @@
|
||||
- 2 modes for editing a page
|
||||
- admin mode => pure html / liquid
|
||||
- editor: only "editable" tags can be edited !
|
24
lib/core_ext.rb
Normal file
@ -0,0 +1,24 @@
|
||||
## String
|
||||
class String
|
||||
# def perma_string(sep = '_')
|
||||
# ActiveSupport::Inflector.parameterize(self, sep).to_s
|
||||
# end
|
||||
|
||||
def slugify(options = {})
|
||||
options = { :sep => '_', :without_extension => false }.merge(options)
|
||||
# replace accented chars with ther ascii equivalents
|
||||
s = ActiveSupport::Inflector.transliterate(self).to_s
|
||||
# No more than one slash in a row
|
||||
s.gsub!(/(\/[\/]+)/, '/')
|
||||
# Remove leading or trailing space
|
||||
s.strip!
|
||||
# Remove leading or trailing slash
|
||||
s.gsub! /(^[\/]+)|([\/]+$)/, ''
|
||||
# Remove extensions
|
||||
s.gsub! /(\.[a-zA-Z]{2,})/, '' if options[:without_extension]
|
||||
# Turn unwanted chars into the seperator
|
||||
s.gsub!(/[^a-zA-Z0-9\-_\+\/]+/i, options[:sep])
|
||||
s
|
||||
end
|
||||
|
||||
end
|
@ -17,67 +17,4 @@ module Locomotive
|
||||
yield(self.config)
|
||||
end
|
||||
|
||||
# class Configuration
|
||||
#
|
||||
# @@defaults = {
|
||||
# :name => 'LocomotiveApp'
|
||||
# :default_domain => 'rails.local.fr',
|
||||
# :reserved_subdomains => %w{www admin email blog webmail mail support help site sites}
|
||||
# }
|
||||
#
|
||||
# cattr_accessor :settings
|
||||
#
|
||||
# def initialize
|
||||
# @@settings = self.class.get_from_hash(@@defaults)
|
||||
# end
|
||||
#
|
||||
# def self.settings
|
||||
# @@settings
|
||||
# end
|
||||
#
|
||||
# def method_missing(name, *args, &block)
|
||||
# self.settings.send(name, *args, &block)
|
||||
# end
|
||||
#
|
||||
# protected
|
||||
#
|
||||
# # converts a hash map into a ConfigurationHash
|
||||
# def self.get_from_hash(hash)
|
||||
# config = ConfigurationHash.new
|
||||
#
|
||||
# hash.each_pair do |key, value|
|
||||
# config[key] = value.is_a?(Hash) ? self.get_from_hash(value) : value
|
||||
# end
|
||||
#
|
||||
# config
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # specialized hash for storing configuration settings
|
||||
# class ConfigurationHash < Hash
|
||||
# # ensure that default entries always produce
|
||||
# # instances of the ConfigurationHash class
|
||||
# def default(key=nil)
|
||||
# include?(key) ? self[key] : self[key] = self.class.new
|
||||
# end
|
||||
#
|
||||
# # retrieves the specified key and yields it
|
||||
# # if a block is provided
|
||||
# def [](key, &block)
|
||||
# block_given? ? yield(super(key)) : super(key)
|
||||
# end
|
||||
#
|
||||
# # provides member-based access to keys
|
||||
# # i.e. params.id === params[:id]
|
||||
# # note: all keys are converted to symbols
|
||||
# def method_missing(name, *args, &block)
|
||||
# if name.to_s.ends_with? '='
|
||||
# send :[]=, name.to_s.chomp('=').to_sym, *args
|
||||
# else
|
||||
# send(:[], name.to_sym, &block)
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
|
||||
end
|
@ -5,7 +5,8 @@ module Locomotive
|
||||
@@defaults = {
|
||||
:name => 'LocomotiveApp',
|
||||
:default_domain => 'rails.local.fr',
|
||||
:reserved_subdomains => %w{www admin email blog webmail mail support help site sites}
|
||||
:reserved_subdomains => %w{www admin email blog webmail mail support help site sites},
|
||||
:forbidden_paths => %w{layouts snippets stylesheets javascripts assets admin system api}
|
||||
}
|
||||
|
||||
cattr_accessor :settings
|
||||
|
26
lib/misc_form_builder.rb
Normal file
@ -0,0 +1,26 @@
|
||||
class MiscFormBuilder < Formtastic::SemanticFormBuilder
|
||||
|
||||
@@all_fields_required_by_default = false
|
||||
|
||||
def foldable_inputs(*args, &block)
|
||||
opts = args.extract_options!
|
||||
|
||||
unfolded = !(opts[:class] || '').index('off').nil? || @object.new_record? || !@object.errors.empty?
|
||||
|
||||
opts[:class] = (opts[:class] || '') + " inputs foldable #{'folded' unless unfolded}"
|
||||
args.push(opts)
|
||||
self.inputs(*args, &block)
|
||||
end
|
||||
|
||||
def custom_input(name, options = {}, &block)
|
||||
default_options = { :css => '', :with_label => true, :label => nil }
|
||||
options = default_options.merge(options)
|
||||
|
||||
html = options[:with_label] ? self.label(options[:label] || name) : ''
|
||||
html += template.capture(&block) || ''
|
||||
html += self.errors_on(name) || ''
|
||||
|
||||
template.content_tag(:li, html, :class => "#{options[:css]} #{'error' unless @object.errors[name].empty?}")
|
||||
end
|
||||
|
||||
end
|
BIN
public/images/admin/form/big_item.png
Normal file
After (image error) Size: 3.8 KiB |
BIN
public/images/admin/form/error-arrow.png
Normal file
After (image error) Size: 3.0 KiB |
BIN
public/images/admin/form/field.png
Normal file
After (image error) Size: 2.8 KiB |
BIN
public/images/admin/form/folded-arrow-off.png
Normal file
After (image error) Size: 2.8 KiB |
BIN
public/images/admin/form/folded-arrow-on.png
Normal file
After (image error) Size: 2.8 KiB |
BIN
public/images/admin/form/folded.png
Normal file
After (image error) Size: 3.4 KiB |
BIN
public/images/admin/form/footer.png
Normal file
After (image error) Size: 3.0 KiB |
BIN
public/images/admin/form/growl-error.png
Normal file
After (image error) Size: 2.9 KiB |
BIN
public/images/admin/form/growl-notice.png
Normal file
After (image error) Size: 2.9 KiB |
BIN
public/images/admin/form/header-first-on.png
Normal file
After (image error) Size: 3.0 KiB |
BIN
public/images/admin/form/header-on.png
Normal file
After (image error) Size: 2.8 KiB |
BIN
public/images/admin/form/header.png
Normal file
After (image error) Size: 3.2 KiB |
BIN
public/images/admin/form/icons/drag.png
Normal file
After (image error) Size: 3.1 KiB |
BIN
public/images/admin/form/icons/spinner.gif
Normal file
After (image error) Size: 847 B |
BIN
public/images/admin/form/icons/trash.png
Normal file
After (image error) Size: 3.0 KiB |
BIN
public/images/admin/form/item.png
Normal file
After (image error) Size: 3.7 KiB |
BIN
public/images/admin/form/pen.png
Normal file
After (image error) Size: 3.0 KiB |
BIN
public/images/admin/list/empty.png
Normal file
After (image error) Size: 2.8 KiB |
BIN
public/images/admin/list/icons/drag.png
Normal file
After (image error) Size: 3.0 KiB |
BIN
public/images/admin/list/item-left.png
Normal file
After (image error) Size: 3.3 KiB |
BIN
public/images/admin/list/item-right.png
Normal file
After (image error) Size: 3.4 KiB |
BIN
public/images/admin/list/item.png
Normal file
After (image error) Size: 3.3 KiB |
BIN
public/images/admin/list/none.png
Normal file
After (image error) Size: 4.1 KiB |
BIN
public/images/admin/list/thumb.png
Normal file
After (image error) Size: 3.4 KiB |
BIN
public/images/admin/plugins/toggle_handle-bg.png
Normal file
After (image error) Size: 355 B |
BIN
public/images/admin/plugins/toggle_handle_left-bg.png
Normal file
After (image error) Size: 698 B |
BIN
public/images/admin/plugins/toggle_handle_right-bg.png
Normal file
After (image error) Size: 727 B |
BIN
public/images/admin/plugins/toggle_shadow-bg.png
Normal file
After (image error) Size: 344 B |
63
public/javascripts/admin/application.js
Normal file
@ -0,0 +1,63 @@
|
||||
var I18nLocale = null;
|
||||
|
||||
/* ___ growl ___ */
|
||||
|
||||
$.growl.settings.noticeTemplate = '' +
|
||||
'<div class="notice %title%">' +
|
||||
' <p>%message%</p>' +
|
||||
'</div>';
|
||||
|
||||
$.growl.settings.dockCss = {
|
||||
position: 'fixed',
|
||||
bottom: '20px',
|
||||
left: '0px',
|
||||
width: '100%',
|
||||
zIndex: 50000
|
||||
};
|
||||
|
||||
/* ___ global ___ */
|
||||
|
||||
$(document).ready(function() {
|
||||
I18nLocale = $('meta[name=locale]').attr('content');
|
||||
|
||||
// form
|
||||
$('.formtastic li input, .formtastic li textarea').focus(function() {
|
||||
$('.formtastic li.error p.inline-errors').fadeOut(200);
|
||||
if ($(this).parent().hasClass('error')) {
|
||||
$(this).nextAll('p.inline-errors').show();
|
||||
}
|
||||
});
|
||||
$('.formtastic li.error input').eq(0).focus();
|
||||
|
||||
// editable title (page, ...etc)
|
||||
$('#content h2 a.editable').each(function() {
|
||||
var target = $('#' + $(this).attr('rel')),
|
||||
hint = $(this).attr('title');
|
||||
|
||||
target.parent().hide();
|
||||
|
||||
$(this).click(function(event) {
|
||||
var newValue = prompt(hint, $(this).html());
|
||||
if (newValue && newValue != '') {
|
||||
$(this).html(newValue);
|
||||
target.val(newValue);
|
||||
}
|
||||
event.preventDefault();
|
||||
});
|
||||
});
|
||||
|
||||
// foldable
|
||||
$('.formtastic fieldset.foldable legend span').append('<em> </em>');
|
||||
$('.formtastic fieldset.foldable.folded ol').hide();
|
||||
$('.formtastic fieldset.foldable legend').click(function() {
|
||||
var parent = $(this).parent(), content = $(this).next();
|
||||
if (parent.hasClass('folded')) {
|
||||
parent.removeClass('folded');
|
||||
content.slideDown(400, function() { });
|
||||
} else
|
||||
content.slideUp(400, function() { parent.addClass('folded'); });
|
||||
});
|
||||
|
||||
// nifty checkboxes
|
||||
$('.formtastic li.toggle input[type=checkbox]').checkToggle();
|
||||
});
|
143
public/javascripts/admin/plugins/growl.js
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* jQuery Growl plugin
|
||||
* Version 1.0.1 (10/27/2008)
|
||||
* @requires jQuery v1.2.3 or later
|
||||
*
|
||||
* Examples at: http://fragmentedcode.com/jquery-growl
|
||||
* Copyright (c) 2008 David Higgins
|
||||
*
|
||||
* Special thanks to Daniel Mota for inspiration:
|
||||
* http://icebeat.bitacoras.com/mootools/growl/
|
||||
*/
|
||||
|
||||
/*
|
||||
USAGE:
|
||||
|
||||
$.growl(title, msg);
|
||||
$.growl(title, msg, image);
|
||||
$.growl(title, msg, image, priority);
|
||||
|
||||
THEME/SKIN:
|
||||
|
||||
You can override the default look and feel by updating these objects:
|
||||
$.growl.settings.displayTimeout = 4000;
|
||||
$.growl.settings.noticeTemplate = ''
|
||||
+ '<div>'
|
||||
+ '<div style="float: right; background-image: url(my.growlTheme/normalTop.png); position: relative; width: 259px; height: 16px; margin: 0pt;"></div>'
|
||||
+ '<div style="float: right; background-image: url(my.growlTheme/normalBackground.png); position: relative; display: block; color: #ffffff; font-family: Arial; font-size: 12px; line-height: 14px; width: 259px; margin: 0pt;">'
|
||||
+ ' <img style="margin: 14px; margin-top: 0px; float: left;" src="%image%" />'
|
||||
+ ' <h3 style="margin: 0pt; margin-left: 77px; padding-bottom: 10px; font-size: 13px;">%title%</h3>'
|
||||
+ ' <p style="margin: 0pt 14px; margin-left: 77px; font-size: 12px;">%message%</p>'
|
||||
+ '</div>'
|
||||
+ '<div style="float: right; background-image: url(my.growlTheme/normalBottom.png); position: relative; width: 259px; height: 16px; margin-bottom: 10px;"></div>'
|
||||
+ '</div>';
|
||||
$.growl.settings.noticeCss = {
|
||||
position: 'relative'
|
||||
};
|
||||
|
||||
To change the 'dock' look, and position:
|
||||
|
||||
$.growl.settings.dockTemplate = '<div></div>';
|
||||
$.growl.settings.dockCss = {
|
||||
position: 'absolute',
|
||||
top: '10px',
|
||||
right: '10px',
|
||||
width: '300px'
|
||||
};
|
||||
|
||||
The dockCss will allow you to 'dock' the notifications to a specific area
|
||||
on the page, such as TopRight (the default) or TopLeft, perhaps even in a
|
||||
smaller area with "overflow: scroll" enabled?
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
$.growl = function(title,message,image,priority) { notify(title,message,image,priority); }
|
||||
$.growl.version = "1.0.0-b2";
|
||||
|
||||
function create(rebuild) {
|
||||
var instance = document.getElementById('growlDock');
|
||||
if(!instance || rebuild) {
|
||||
instance = $(jQuery.growl.settings.dockTemplate).attr('id', 'growlDock').addClass('growl');
|
||||
if(jQuery.growl.settings.defaultStylesheet) {
|
||||
$('head').append('<link rel="stylesheet" type="text/css" href="' + jQuery.growl.settings.defaultStylesheet + '" />');
|
||||
}
|
||||
|
||||
} else {
|
||||
instance = $(instance);
|
||||
}
|
||||
$('body').append(instance.css(jQuery.growl.settings.dockCss));
|
||||
return instance;
|
||||
};
|
||||
|
||||
function r(text, expr, val) {
|
||||
while(expr.test(text)) {
|
||||
text = text.replace(expr, val);
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
function notify(title,message,image,priority) {
|
||||
var instance = create();
|
||||
var html = jQuery.growl.settings.noticeTemplate;
|
||||
if(typeof(html) == 'object') html = $(html).html();
|
||||
html = r(html, /%message%/, (message?message:''));
|
||||
html = r(html, /%title%/, (title?title:''));
|
||||
html = r(html, /%image%/, (image?image:jQuery.growl.settings.defaultImage));
|
||||
html = r(html, /%priority%/, (priority?priority:'normal'));
|
||||
|
||||
var notice = $(html)
|
||||
.hide()
|
||||
.css(jQuery.growl.settings.noticeCss)
|
||||
.fadeIn(jQuery.growl.settings.notice);;
|
||||
|
||||
$.growl.settings.noticeDisplay(notice);
|
||||
instance.append(notice);
|
||||
$('a[@rel="close"]', notice).click(function() {
|
||||
notice.remove();
|
||||
});
|
||||
if ($.growl.settings.displayTimeout > 0) {
|
||||
setTimeout(function(){
|
||||
jQuery.growl.settings.noticeRemove(notice, function(){
|
||||
notice.remove();
|
||||
});
|
||||
}, jQuery.growl.settings.displayTimeout);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// default settings
|
||||
$.growl.settings = {
|
||||
dockTemplate: '<div></div>',
|
||||
dockCss: {
|
||||
position: 'fixed',
|
||||
top: '10px',
|
||||
right: '10px',
|
||||
width: '300px',
|
||||
zIndex: 50000
|
||||
},
|
||||
noticeTemplate:
|
||||
'<div class="notice">' +
|
||||
' <h3 style="margin-top: 15px">%title%</h3>' +
|
||||
' <p>%message%</p>' +
|
||||
'</div>',
|
||||
noticeCss: {
|
||||
opacity: 1,
|
||||
backgroundColor: 'transparent',
|
||||
color: '#ffffff'
|
||||
},
|
||||
noticeDisplay: function(notice) {
|
||||
notice.css({'opacity':'0'}).fadeIn(jQuery.growl.settings.noticeFadeTimeout);
|
||||
},
|
||||
noticeRemove: function(notice, callback) {
|
||||
notice.animate({opacity: '0', height: '0px'}, {duration:jQuery.growl.settings.noticeFadeTimeout, complete: callback});
|
||||
},
|
||||
noticeFadeTimeout: 'slow',
|
||||
displayTimeout: 3500,
|
||||
defaultImage: 'growl.jpg',
|
||||
defaultStylesheet: null,
|
||||
noticeElement: function(el) {
|
||||
$.growl.settings.noticeTemplate = $(el);
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
109
public/javascripts/admin/plugins/toggle.js
Normal file
@ -0,0 +1,109 @@
|
||||
/**
|
||||
*
|
||||
* Copyright (c) 2009 Tony Dewan (http://www.tonydewan.com/)
|
||||
* Licensed under the MIT License:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Project home:
|
||||
* http://www.tonydewan.com/code/checkToggle/
|
||||
*
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
/**
|
||||
* Version 1.0
|
||||
* Replaces checkboxes with a toggle switch.
|
||||
* usage: $("input[type='checkbox']").checkToggle(settings);
|
||||
*
|
||||
* @name checkToggle
|
||||
* @type jquery
|
||||
* @param Hash settings Settings
|
||||
* @param String settings[on_label] Text used for the left-side (on) label. Defaults to "On"
|
||||
* @param String settings[off_label] Text used for the right-side (off) label. Defaults to "Off"
|
||||
* @param String settings[on_bg_color] Hex background color for On state
|
||||
* @param String settings[off_bg_color] Hex background color for Off state
|
||||
* @param String settings[skin_dir] Document relative (or absolute) path to the skin directory
|
||||
* @param Bool settings[bypass_skin] Flags whether to bypass the inclusion of the skin.css file. Used if you've included the skin styles somewhere else already.
|
||||
*/
|
||||
|
||||
$.fn.checkToggle = function(settings) {
|
||||
|
||||
settings = $.extend({
|
||||
on_label : 'Yes',
|
||||
on_bg_color : '#8FE38D',
|
||||
off_label : 'No',
|
||||
off_bg_color: '#F8837C',
|
||||
skin_dir : "skin/",
|
||||
bypass_skin : false
|
||||
}, settings);
|
||||
|
||||
// FIXME (Didier Lafforgue) it works but it doesn't scale if we handle another locale
|
||||
if (typeof I18nLocale != 'undefined' && I18nLocale == 'fr') {
|
||||
settings.on_label = 'Oui';
|
||||
settings.off_label = 'Non';
|
||||
}
|
||||
|
||||
// append the skin styles
|
||||
// if(settings.bypass_skin == false){
|
||||
// $("head").append('<link type="text/css" rel="stylesheet" href="'+settings.skin_dir+'skin.css" media="screen" />');
|
||||
// }
|
||||
|
||||
function toggle(element){
|
||||
|
||||
var checked = $(element).parent().parent().prev().attr("checked");
|
||||
|
||||
// if it's set to on
|
||||
if(checked){
|
||||
|
||||
$(element).animate({marginLeft: '34px'}, 100,
|
||||
|
||||
// callback function
|
||||
function(){
|
||||
$(element).parent().prev().css("color","#cccccc");
|
||||
$(element).parent().next().css("color","#333333");
|
||||
$(element).parent().css("background-color", settings.off_bg_color);
|
||||
$(element).parent().parent().prev().removeAttr("checked");
|
||||
$(element).removeClass("left").addClass("right");
|
||||
});
|
||||
|
||||
}else{
|
||||
|
||||
$(element).animate({marginLeft: '0em'}, 100,
|
||||
|
||||
// callback function
|
||||
function(){
|
||||
$(element).parent().prev().css("color","#333333");
|
||||
$(element).parent().next().css("color","#cccccc");
|
||||
$(element).parent().css("background-color", settings.on_bg_color);
|
||||
$(element).parent().parent().prev().attr("checked","checked");
|
||||
$(element).removeClass("right").addClass("left");
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return this.each(function () {
|
||||
|
||||
if ($(this).hasClass('simple')) return;
|
||||
|
||||
// hide the checkbox
|
||||
$(this).css('display','none');
|
||||
|
||||
// insert the new toggle markup
|
||||
if($(this).attr("checked") == true){
|
||||
$(this).after('<div class="toggleSwitch"><span class="leftLabel">'+settings.on_label+'<\/span><div class="switchArea" style="background-color: '+settings.on_bg_color+'"><span class="switchHandle left" style="margin-left: 0em;"><\/span><\/div><span class="rightLabel" style="color:#cccccc">'+settings.off_label+'<\/span><\/div>');
|
||||
}else{
|
||||
$(this).after('<div class="toggleSwitch"><span class="leftLabel" style="color:#cccccc;">'+settings.on_label+'<\/span><div class="switchArea" style="background-color: '+settings.off_bg_color+'"><span class="switchHandle right" style="margin-left:34px"><\/span><\/div><span class="rightLabel">'+settings.off_label+'<\/span><\/div>');
|
||||
}
|
||||
|
||||
// Bind the switchHandle click events to the internal toggle function
|
||||
$(this).next().find('div.switchArea').bind("click", function () {
|
||||
toggle($(this).find('.switchHandle')); })
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
})(jQuery);
|
@ -1,2 +0,0 @@
|
||||
// Place your application-specific JavaScript functions and classes here
|
||||
// This file is automatically included by javascript_include_tag :defaults
|
@ -0,0 +1,243 @@
|
||||
/* ___ application messages ___ */
|
||||
|
||||
div.notice {
|
||||
background: transparent url(/images/admin/form/growl-notice.png) repeat-x 0 0;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
div.notice.error {
|
||||
background-image: url(/images/admin/form/growl-error.png);
|
||||
}
|
||||
|
||||
div.notice p {
|
||||
position: relative;
|
||||
top: 35px;
|
||||
margin: 0px;
|
||||
text-align: center;
|
||||
font-size: 1.5em;
|
||||
text-shadow: 1px 1px 1px #333;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* ___ list ___ */
|
||||
|
||||
p.no-items {
|
||||
padding: 15px 0px;
|
||||
background: transparent url(/images/admin/list/none.png) no-repeat 0 0;
|
||||
text-align: center;
|
||||
color: #9d8963 !important;
|
||||
font-size: 1.1em !important;
|
||||
}
|
||||
|
||||
p.no-items a {
|
||||
color: #ff2900;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
p.no-items a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
ul.list {
|
||||
list-style: none;
|
||||
margin: 0px 0 20px 0;
|
||||
background: white;
|
||||
}
|
||||
|
||||
ul.list li {
|
||||
height: 31px;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
clear: both;
|
||||
background: transparent url(/images/admin/list/item.png) no-repeat 0 0;
|
||||
}
|
||||
|
||||
ul.list li strong a {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
left: 15px;
|
||||
text-decoration: none;
|
||||
color: #1f82bc;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
ul.list.sortable li strong a { left: 10px; }
|
||||
|
||||
ul.list li strong a:hover { text-decoration: underline; }
|
||||
|
||||
ul.list li div.more {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
right: 15px;
|
||||
font-size: 0.7em;
|
||||
color: #8b8d9a;
|
||||
}
|
||||
|
||||
ul.list li div.more a {
|
||||
margin-left: 10px;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
ul.list li span.handle {
|
||||
position: relative;
|
||||
top: 5px;
|
||||
margin: 0 0 0 15px;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
/* ___ assets ___ */
|
||||
|
||||
ul.assets {
|
||||
list-style: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
ul.assets li.asset {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 139px;
|
||||
height: 140px;
|
||||
background: transparent url(/images/admin/list/thumb.png) no-repeat 0 0;
|
||||
margin: 0 17px 17px 0;
|
||||
}
|
||||
|
||||
ul.assets li.asset.last {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
ul.assets li.asset h4 { margin: 0px; height: 30px; }
|
||||
|
||||
ul.assets li.asset h4 a {
|
||||
position: relative;
|
||||
top: 6px;
|
||||
left: 12px;
|
||||
font-weight: bold;
|
||||
font-size: 0.7em;
|
||||
color: #1f82bc;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
ul.assets li.asset h4 a:hover { text-decoration: underline; }
|
||||
|
||||
ul.assets li.asset div.image {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 4px solid #fff;
|
||||
margin: 10px 0 0 24px;
|
||||
background: transparent url(/images/admin/list/empty.png) repeat 0 0;
|
||||
}
|
||||
|
||||
ul.assets li.asset div.image div.inside {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
ul.assets li.asset div.actions {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
/* ___ asset collections ___ */
|
||||
|
||||
div#asset-uploader { display: inline-block; margin-left: 10px; }
|
||||
div#asset-uploader span.spinner { position: relative; top: -3px; display: none; }
|
||||
div#uploadAssetsInputQueue { display: none; }
|
||||
|
||||
|
||||
/* ___ pages ___ */
|
||||
|
||||
#pages-list {
|
||||
list-style: none;
|
||||
margin: 0px 0 20px 0;
|
||||
background: white;
|
||||
}
|
||||
|
||||
#pages-list li {
|
||||
height: 31px;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
#pages-list li em {
|
||||
display: block;
|
||||
float: left;
|
||||
background: transparent url(/images/admin/list/item-left.png) no-repeat left 0;
|
||||
height: 31px;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
#pages-list li .toggler {
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: -15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#pages-list li strong {
|
||||
margin-left: 18px;
|
||||
display: block;
|
||||
height: 31px;
|
||||
background: transparent url(/images/admin/list/item-right.png) no-repeat right 0;
|
||||
}
|
||||
|
||||
#pages-list li strong a {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
text-decoration: none;
|
||||
color: #1f82bc;
|
||||
font-size: 0.9em;
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
#pages-list li strong a:hover { text-decoration: underline; }
|
||||
|
||||
#pages-list li.hidden strong a { font-style: italic; font-weight: normal; }
|
||||
|
||||
#pages-list li .more {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
right: 20px;
|
||||
font-size: 0.7em;
|
||||
color: #8b8d9a;
|
||||
}
|
||||
|
||||
#pages-list li .more a {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
margin-left: 10px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#pages-list li.error .more { top: 16px; }
|
||||
|
||||
|
||||
#pages-list li.depth-1 { margin-left: 40px; }
|
||||
#pages-list li.depth-1.index { margin-left: 0px; }
|
||||
#pages-list li.depth-1.error { border-top: 1px dotted #bbbbbd; padding-top: 10px; margin-left: 0px; }
|
||||
|
||||
#pages-list li.depth-2 { margin-left: 80px; }
|
||||
#pages-list li.depth-2.index { margin-left: 40px; }
|
||||
|
||||
#pages-list li.depth-3 { margin-left: 120px; }
|
||||
#pages-list li.depth-3.index { margin-left: 80px; }
|
||||
|
||||
#pages-list li.depth-4 { margin-left: 160px; }
|
||||
#pages-list li.depth-4.index { margin-left: 120px; }
|
||||
|
||||
#pages-list li.depth-5 { margin-left: 200px; }
|
||||
#pages-list li.depth-5.index { margin-left: 160px; }
|
||||
|
||||
/* ___ Progress bar ___ */
|
||||
|
||||
#progressbar-wrapper { margin: 40px 0; height: 30px; }
|
||||
#progressbar-wrapper #progressbar { height: 100%; }
|
||||
|
@ -0,0 +1,62 @@
|
||||
.button {
|
||||
display: inline-block;
|
||||
background: transparent url(/images/admin/buttons/dark-gray-left.png) no-repeat 0 0;
|
||||
padding: 0px 0px 0px 2px;
|
||||
font-size: 0.9em;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
height: 31px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.button span {
|
||||
display: inline-block;
|
||||
background: transparent url(/images/admin/buttons/dark-gray-right.png) no-repeat right top;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
padding: 3px 9px 9px 4px;
|
||||
line-height: 21px;
|
||||
text-shadow: 1px 1px 1px #000;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
.button { padding-left: 5px; }
|
||||
.button span { top: 0px; }
|
||||
}
|
||||
|
||||
.button.light {
|
||||
background-image: url(/images/admin/buttons/light-gray-left.png);
|
||||
color: #787a89;
|
||||
}
|
||||
|
||||
.button.light span {
|
||||
background-image: url(/images/admin/buttons/light-gray-right.png);
|
||||
text-shadow: 1px 1px 1px #fff;
|
||||
}
|
||||
|
||||
.button.small {
|
||||
background: transparent url(/images/admin/buttons/action-left.png) no-repeat left -40px;
|
||||
color: #787a89;
|
||||
height: 20px;
|
||||
font-size: 0.7em;
|
||||
padding: 0px 0px 0px 12px;
|
||||
color: #8B8D9A !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button.small span {
|
||||
background-image: url(/images/admin/buttons/action-right.png);
|
||||
text-shadow: 1px 1px 1px #fff;
|
||||
padding: 0px 12px 10px 0px;
|
||||
top: 0px;
|
||||
color: #8B8D9A;
|
||||
}
|
||||
|
||||
.button.remove, .button.remove span {
|
||||
color: #ff092c !important;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.button.remove:hover span { text-decoration: underline; }
|
@ -0,0 +1,137 @@
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
|
||||
It's *strongly* suggested that you don't modify this file. Instead, load a new stylesheet after
|
||||
this one in your layouts (eg formtastic_changes.css) and override the styles to suit your needs.
|
||||
This will allow you to update formtastic.css with new releases without clobbering your own changes.
|
||||
|
||||
This stylesheet forms part of the Formtastic Rails Plugin
|
||||
(c) 2008 Justin French
|
||||
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
/* NORMALIZE AND RESET - obviously inspired by Yahoo's reset.css, but scoped to just form.formtastic
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
form.formtastic, form.formtastic ul, form.formtastic ol, form.formtastic li, form.formtastic fieldset, form.formtastic legend, form.formtastic input, form.formtastic textarea, form.formtastic select, form.formtastic p { margin:0; padding:0; }
|
||||
form.formtastic fieldset { border:0; }
|
||||
form.formtastic em, form.formtastic strong { font-style:normal; font-weight:normal; }
|
||||
form.formtastic ol, form.formtastic ul { list-style:none; }
|
||||
form.formtastic abbr, form.formtastic acronym { border:0; font-variant:normal; }
|
||||
form.formtastic input, form.formtastic textarea, form.formtastic select { font-family:inherit; font-size:inherit; font-weight:inherit; }
|
||||
form.formtastic input, form.formtastic textarea, form.formtastic select { font-size:100%; }
|
||||
form.formtastic legend { color:#000; }
|
||||
|
||||
|
||||
/* FIELDSETS & LISTS
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
form.formtastic fieldset { }
|
||||
form.formtastic fieldset.inputs { }
|
||||
form.formtastic fieldset.buttons { padding-left:25%; }
|
||||
form.formtastic fieldset ol { }
|
||||
form.formtastic fieldset.buttons li { float:left; padding-right:0.5em; }
|
||||
|
||||
/* clearfixing the fieldsets */
|
||||
form.formtastic fieldset { display: inline-block; }
|
||||
form.formtastic fieldset:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
|
||||
html[xmlns] form.formtastic fieldset { display: block; }
|
||||
* html form.formtastic fieldset { height: 1%; }
|
||||
|
||||
|
||||
/* INPUT LIs
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
form.formtastic fieldset ol li { margin-bottom:1.5em; }
|
||||
|
||||
/* clearfixing the li's */
|
||||
form.formtastic fieldset ol li { display: inline-block; }
|
||||
form.formtastic fieldset ol li:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
|
||||
html[xmlns] form.formtastic fieldset ol li { display: block; }
|
||||
* html form.formtastic fieldset ol li { height: 1%; }
|
||||
|
||||
form.formtastic fieldset ol li.required { }
|
||||
form.formtastic fieldset ol li.optional { }
|
||||
form.formtastic fieldset ol li.error { }
|
||||
|
||||
|
||||
/* LABELS
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
form.formtastic fieldset ol li label { display:block; width:25%; float:left; padding-top:.2em; }
|
||||
form.formtastic fieldset ol li li label { line-height:100%; padding-top:0; }
|
||||
form.formtastic fieldset ol li li label input { line-height:100%; vertical-align:middle; margin-top:-0.1em;}
|
||||
|
||||
|
||||
/* NESTED FIELDSETS AND LEGENDS (radio, check boxes and date/time inputs use nested fieldsets)
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
form.formtastic fieldset ol li fieldset { position:relative; }
|
||||
form.formtastic fieldset ol li fieldset legend { position:absolute; width:25%; padding-top:0.1em; }
|
||||
form.formtastic fieldset ol li fieldset legend span { position:absolute; }
|
||||
form.formtastic fieldset ol li fieldset ol { float:left; width:74%; margin:0; padding:0 0 0 25%; }
|
||||
form.formtastic fieldset ol li fieldset ol li { padding:0; border:0; }
|
||||
|
||||
|
||||
/* INLINE HINTS
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
form.formtastic fieldset ol li p.inline-hints { color:#666; margin:0.5em 0 0 25%; }
|
||||
|
||||
|
||||
/* INLINE ERRORS
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
form.formtastic fieldset ol li p.inline-errors { color:#cc0000; margin:0.5em 0 0 25%; }
|
||||
form.formtastic fieldset ol li ul.errors { color:#cc0000; margin:0.5em 0 0 25%; list-style:square; }
|
||||
form.formtastic fieldset ol li ul.errors li { padding:0; border:none; display:list-item; }
|
||||
|
||||
|
||||
/* STRING & NUMERIC OVERRIDES
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
form.formtastic fieldset ol li.string input { width:74%; }
|
||||
form.formtastic fieldset ol li.password input { width:74%; }
|
||||
form.formtastic fieldset ol li.numeric input { width:74%; }
|
||||
|
||||
|
||||
/* TEXTAREA OVERRIDES
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
form.formtastic fieldset ol li.text textarea { width:74%; }
|
||||
|
||||
|
||||
/* HIDDEN OVERRIDES
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
form.formtastic fieldset ol li.hidden { display:none; }
|
||||
|
||||
|
||||
/* BOOLEAN OVERRIDES
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
form.formtastic fieldset ol li.boolean label { padding-left:25%; width:auto; }
|
||||
form.formtastic fieldset ol li.boolean label input { margin:0 0.5em 0 0.2em; }
|
||||
|
||||
|
||||
/* RADIO OVERRIDES
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
form.formtastic fieldset ol li.radio { }
|
||||
form.formtastic fieldset ol li.radio fieldset ol { margin-bottom:-0.6em; }
|
||||
form.formtastic fieldset ol li.radio fieldset ol li { margin:0.1em 0 0.5em 0; }
|
||||
form.formtastic fieldset ol li.radio fieldset ol li label { float:none; width:100%; }
|
||||
form.formtastic fieldset ol li.radio fieldset ol li label input { margin-right:0.2em; }
|
||||
|
||||
|
||||
/* CHECK BOXES (COLLECTION) OVERRIDES
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
form.formtastic fieldset ol li.check_boxes { }
|
||||
form.formtastic fieldset ol li.check_boxes fieldset ol { margin-bottom:-0.6em; }
|
||||
form.formtastic fieldset ol li.check_boxes fieldset ol li { margin:0.1em 0 0.5em 0; }
|
||||
form.formtastic fieldset ol li.check_boxes fieldset ol li label { float:none; width:100%; }
|
||||
form.formtastic fieldset ol li.check_boxes fieldset ol li label input { margin-right:0.2em; }
|
||||
|
||||
|
||||
|
||||
/* DATE & TIME OVERRIDES
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
form.formtastic fieldset ol li.date fieldset ol li,
|
||||
form.formtastic fieldset ol li.time fieldset ol li,
|
||||
form.formtastic fieldset ol li.datetime fieldset ol li { float:left; width:auto; margin:0 .3em 0 0; }
|
||||
|
||||
form.formtastic fieldset ol li.date fieldset ol li label,
|
||||
form.formtastic fieldset ol li.time fieldset ol li label,
|
||||
form.formtastic fieldset ol li.datetime fieldset ol li label { display:none; }
|
||||
|
||||
form.formtastic fieldset ol li.date fieldset ol li label input,
|
||||
form.formtastic fieldset ol li.time fieldset ol li label input,
|
||||
form.formtastic fieldset ol li.datetime fieldset ol li label input { display:inline; margin:0; padding:0; }
|
@ -0,0 +1,436 @@
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
|
||||
Load this stylesheet after formtastic.css in your layouts to override the CSS to suit your needs.
|
||||
This will allow you to update formtastic.css with new releases without clobbering your own changes.
|
||||
|
||||
For example, to make the inline hint paragraphs a little darker in color than the standard #666:
|
||||
|
||||
form.formtastic fieldset ol li p.inline-hints { color:#333; }
|
||||
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
form.formtastic legend {
|
||||
margin: 0;
|
||||
float: left;
|
||||
white-space: normal;
|
||||
*margin-left: -7px;
|
||||
}
|
||||
|
||||
form.formtastic legend span {
|
||||
display: block;
|
||||
width: 900px;
|
||||
height: 26px;
|
||||
background: transparent url(/images/admin/form/header.png) no-repeat 0 0px;
|
||||
color: #1e1f26;
|
||||
font-size: 0.7em;
|
||||
padding: 4px 0 0 20px;
|
||||
}
|
||||
|
||||
/* ___ enabling fold/unfold ___ */
|
||||
|
||||
form.formtastic fieldset.foldable legend span { cursor: pointer; }
|
||||
|
||||
form.formtastic fieldset.foldable legend span em {
|
||||
display: inline-block;
|
||||
width: 9px;
|
||||
height: 6px;
|
||||
position: relative;
|
||||
top: 8px;
|
||||
left: 10px;
|
||||
background: transparent url(/images/admin/form/folded-arrow-on.png) no-repeat 0 0px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.foldable.folded legend span { background-image: url(/images/admin/form/folded.png); }
|
||||
form.formtastic fieldset.foldable.folded legend span em {
|
||||
width: 6px;
|
||||
height: 9px;
|
||||
top: 6px;
|
||||
background-image: url(/images/admin/form/folded-arrow-off.png);
|
||||
}
|
||||
|
||||
form.formtastic fieldset.foldable ol {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.foldable.folded ol { display: none; }
|
||||
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
form.formtastic fieldset.foldable legend span em { top: 0px; }
|
||||
form.formtastic fieldset.foldable.folded legend span em { top: 0px; }
|
||||
}
|
||||
|
||||
/* ___ inputs ___ */
|
||||
|
||||
form.formtastic fieldset.inputs { min-height: 30px; width: 100%; margin-bottom: 20px; }
|
||||
|
||||
form.formtastic fieldset.inputs ol {
|
||||
margin: 30px 0 0 0;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 5px;
|
||||
background: #ebedf4 url(/images/admin/form/footer.png) no-repeat 0 bottom;
|
||||
}
|
||||
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
form.formtastic fieldset.inputs ol {
|
||||
margin-top: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
form.formtastic fieldset ol li { width: 100%; position: relative; margin-bottom: 1.3em; }
|
||||
|
||||
form.formtastic fieldset ol li label { text-align: left; padding: 0.3em 2em 0 20px; font-size: 0.8em; color: #17171b; width: 15%; }
|
||||
|
||||
form.formtastic fieldset ol li.string input,
|
||||
form.formtastic fieldset ol li.password input,
|
||||
form.formtastic fieldset ol li.numeric input,
|
||||
form.formtastic fieldset ol li.text textarea,
|
||||
form.formtastic fieldset ol li code textarea,
|
||||
form.formtastic fieldset ol li input[type=password] {
|
||||
padding: 4px;
|
||||
font-size: 0.9em;
|
||||
width: 45%;
|
||||
color: #787a89;
|
||||
background: white url(/images/admin/form/field.png) repeat-x 0 0;
|
||||
border: 1px solid #a6a8b8;
|
||||
}
|
||||
|
||||
form.formtastic fieldset ol li p.inline-hints { margin-left: 20%; }
|
||||
form.formtastic fieldset ol li p.inline-hints a { color: #1f82bc; }
|
||||
|
||||
form.formtastic fieldset ol li code { display: block; border: 1px solid #a6a8b8; margin: 10px 20px 0 20px; }
|
||||
form.formtastic fieldset ol li code.nude textarea {
|
||||
width: 870px;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
/*form.formtastic fieldset ol li code.html iframe { width: 46% !important; }*/
|
||||
|
||||
form.formtastic fieldset ol li select { font-size: 0.9em; position: relative; top: 2px; color: #787a89; }
|
||||
|
||||
form.formtastic fieldset ol li.error input,
|
||||
form.formtastic fieldset ol li.error textarea,
|
||||
form.formtastic fieldset ol li.error code iframe { border: 2px solid #ec3f48 !important; }
|
||||
form.formtastic fieldset ol li.error code { border: none; }
|
||||
form.formtastic fieldset ol li p.inline-errors {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 630px;
|
||||
width: 250px;
|
||||
margin: 0px;
|
||||
padding: 6px 5px 8px 25px;
|
||||
background: #ec3f48 url(/images/admin/form/error-arrow.png) no-repeat 0 0;
|
||||
color: #fff !important;
|
||||
font-size: 0.7em !important;
|
||||
}
|
||||
|
||||
|
||||
/*form.formtastic hr { border-top: 2px solid #ccc; }*/
|
||||
|
||||
/*form.formtastic fieldset.buttons { padding-left: 28%; padding-bottom: 20px; }*/
|
||||
|
||||
form.formtastic div.actions {
|
||||
position: relative;
|
||||
top: 27px;
|
||||
left: -15px;
|
||||
width: 950px;
|
||||
background: #8b8d9a;
|
||||
}
|
||||
|
||||
form.formtastic div.actions p {
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
form.formtastic div.actions a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-size: 0.8em;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
form.formtastic div.actions p a:hover { text-decoration: underline; }
|
||||
|
||||
form.formtastic div.actions .last p { text-align: right; }
|
||||
|
||||
|
||||
/* ___ pages ___ */
|
||||
|
||||
form.formtastic fieldset ol li.path em {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
form.formtastic fieldset ol li.path input {
|
||||
background: transparent;
|
||||
padding: 4px 4px 2px 4px;
|
||||
border: none;
|
||||
color: #787a89;
|
||||
border-bottom: 1px solid #b5b7c4;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
form.formtastic fieldset ol li.path.error input {
|
||||
border: none !important;
|
||||
border-bottom: 2px solid #ff092c !important;
|
||||
}
|
||||
|
||||
/* ___ sites ___ */
|
||||
|
||||
form.formtastic fieldset ol li.item {
|
||||
position: relative;
|
||||
background: transparent url(/images/admin/form/item.png) no-repeat 0 0;
|
||||
height: 25px;
|
||||
width: 861px;
|
||||
margin: 0px 0px 10px 20px;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset ol li.item strong {
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
color: #17171d;
|
||||
}
|
||||
|
||||
form.formtastic fieldset ol li.item strong a {
|
||||
color: #17171d;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
form.formtastic fieldset ol li.item strong a:hover { text-decoration: underline; }
|
||||
|
||||
form.formtastic fieldset ol li.item em {
|
||||
margin-left: 10px;
|
||||
font-size: 0.7em;
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
form.formtastic fieldset ol li.item span.actions {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 10px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
/* ___ editable-list (content type fields and validations) ___ */
|
||||
|
||||
form.formtastic fieldset.editable-list ol { padding-left: 20px; }
|
||||
|
||||
form.formtastic fieldset.editable-list ol li { margin-left: 0px !important; }
|
||||
|
||||
form.formtastic fieldset.editable-list ol li span.handle {
|
||||
cursor: move;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.added span.actions a.remove {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.added span.actions button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.added select {
|
||||
display: none;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.added em {
|
||||
color: #8b8d9a;
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
margin-left: 3px;
|
||||
}
|
||||
form.formtastic fieldset.editable-list ol li.added em { border: 1px solid transparent; padding: 2px 5px; }
|
||||
form.formtastic fieldset.editable-list ol li.added em:hover {
|
||||
background: #fffbe5;
|
||||
border: 1px dotted #efe4a5;
|
||||
cursor: pointer;
|
||||
color: #17171D;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.added input {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
padding: 1px 5px 2px 5px;
|
||||
color: #17171D;
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
cursor: normal;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.added input:hover {
|
||||
background: #fffbe5;
|
||||
border: 1px dotted #efe4a5;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.added input:focus {
|
||||
font-size: 0.9em;
|
||||
font-weight: normal;
|
||||
color: #787a89;
|
||||
background: white url(/images/admin/form/field.png) repeat-x 0 0;
|
||||
border: 1px solid #a6a8b8;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.new {
|
||||
height: 42px;
|
||||
background-image: url(/images/admin/form/big_item.png);
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.new input {
|
||||
display: inline;
|
||||
margin-left: 10px;
|
||||
padding: 4px;
|
||||
font-size: 0.9em;
|
||||
width: 180px;
|
||||
color: #787a89;
|
||||
background: white url(/images/admin/form/field.png) repeat-x 0 0;
|
||||
border: 1px solid #a6a8b8;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.new select {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.new span.handle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.new span.actions {
|
||||
width: auto;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.new span.actions a.remove {
|
||||
display: none;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.new span.actions button {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.editable-list ol li.new span.actions button span {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
/* ___ editable-list (content type validations) ___ */
|
||||
|
||||
form.formtastic fieldset.validations ol li.added em.key {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
padding: 1px 5px 2px 5px;
|
||||
color: #17171D;
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
margin-left: 5px;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
/* ___ my account ___ */
|
||||
|
||||
form.formtastic fieldset.language li.full span {
|
||||
margin: 0 20px;
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.language li.full span img {
|
||||
position: relative;
|
||||
top: 6px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.language li.full span input {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
/* ___ membership ___ */
|
||||
|
||||
form.formtastic fieldset.email li.full input {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
/* ___ assets ___ */
|
||||
|
||||
.selector {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.selector span.alt {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 20px;
|
||||
color: #787a89;
|
||||
font-size: 0.7em;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
form.formtastic fieldset.file li.full input {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
form.formtastic fieldset.file li.full p.inline-errors { display: block !important; }
|
||||
|
||||
|
||||
form.formtastic fieldset.preview { position: relative; }
|
||||
|
||||
form.formtastic fieldset.preview li { text-align: center; }
|
||||
|
||||
form.formtastic fieldset.preview li img { margin-top: 10px; border: 4px solid white; }
|
||||
|
||||
form.formtastic fieldset.preview div.size {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 20px;
|
||||
color: #787a89;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
form.formtastic fieldset.preview div.size {
|
||||
top: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ___ main error message ___ */
|
||||
|
||||
div.form-errors p {
|
||||
background: #FFE5E5;
|
||||
color: #CE2525;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
margin: 0px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.formError {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
display: inline;
|
||||
background: #CE2525 url(/images/admin/left_arrow_red.png) no-repeat 0px center;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
padding: 3px 10px 3px 20px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
div.fieldWithErrors { display: inline; }
|
||||
|
42
public/stylesheets/admin/plugins/toggle.css
Normal file
@ -0,0 +1,42 @@
|
||||
.toggleSwitch {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
div.toggleSwitch span.leftLabel{
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.toggleSwitch span.leftLabel, div.toggleSwitch span.rightLabel{
|
||||
line-height: 20px;
|
||||
padding: 0 5px;
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.toggleSwitch div.switchArea {
|
||||
float: left;
|
||||
background: transparent url("/images/admin/plugins/toggle_shadow-bg.png") top left no-repeat;
|
||||
width: 64px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.toggleSwitch span.switchHandle{
|
||||
display: block;
|
||||
background: #aaa;
|
||||
background: transparent url("/images/admin/plugins/toggle_handle-bg.png") top left no-repeat;
|
||||
width: 30px;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
cursor: hand;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
div.toggleSwitch span.switchHandle.left{
|
||||
background-image: url("/images/admin/plugins/toggle_handle_left-bg.png");
|
||||
}
|
||||
|
||||
div.toggleSwitch span.switchHandle.right{
|
||||
background-image: url("/images/admin/plugins/toggle_handle_right-bg.png");
|
||||
}
|
@ -14,3 +14,9 @@ Factory.define :account do |a|
|
||||
a.locale 'en'
|
||||
end
|
||||
|
||||
## Pages ##
|
||||
Factory.define :page do |p|
|
||||
p.association :site, :factory => :site
|
||||
p.title 'Home page'
|
||||
p.path 'index'
|
||||
end
|
42
spec/models/page_spec.rb
Normal file
@ -0,0 +1,42 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Page do
|
||||
|
||||
it 'should have a valid factory' do
|
||||
Factory.build(:page).should be_valid
|
||||
end
|
||||
|
||||
## Validations ##
|
||||
|
||||
%w{site title path}.each do |field|
|
||||
it "should validate presence of #{field}" do
|
||||
page = Factory.build(:page, field.to_sym => nil)
|
||||
page.should_not be_valid
|
||||
page.errors[field.to_sym].should == ["can't be blank"]
|
||||
end
|
||||
end
|
||||
|
||||
it 'should validate uniqueness of path' do
|
||||
page = Factory(:page)
|
||||
(page = Factory.build(:page, :site => page.site)).should_not be_valid
|
||||
page.errors[:path].should == ["is already taken"]
|
||||
end
|
||||
|
||||
## Named scopes ##
|
||||
|
||||
## Associations ##
|
||||
|
||||
## Methods ##
|
||||
|
||||
describe 'once created' do
|
||||
|
||||
it 'should add the body part' do
|
||||
page = Factory(:page)
|
||||
page.parts.should_not be_empty
|
||||
page.parts.first.name.should == 'body'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|