creating page in progress

This commit is contained in:
dinedine 2010-04-25 02:33:38 +02:00
parent 4ca9037d1b
commit e81b4a353a
62 changed files with 1674 additions and 71 deletions

View File

@ -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

View File

@ -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

View File

@ -1,8 +1,15 @@
module Admin::BaseHelper
def admin_menu_item(name, url)
label = content_tag(:em) + escape_once('&nbsp;') + 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

View File

@ -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
View 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
View 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

View File

@ -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

View File

@ -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 %>

View File

@ -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 '&larr;&nbsp;' + 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 %>

View File

@ -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)

View File

@ -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

View File

@ -0,0 +1,11 @@
.actions
.span-12
%p
= link_to escape_once('&larr;&nbsp;') + 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

View File

@ -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

View File

@ -1 +1,4 @@
Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true
Formtastic::SemanticFormHelper.builder = MiscFormBuilder
Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true

View File

@ -1,4 +1,5 @@
require 'lib/locomotive.rb'
require 'lib/core_ext.rb'
Locomotive.configure do |config|
config.default_domain = 'example.com'

View File

@ -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

View File

@ -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
View 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
View 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

View File

@ -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

View File

@ -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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 847 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

View 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>&nbsp;</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();
});

View 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);

View 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);

View File

@ -1,2 +0,0 @@
// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults

View File

@ -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%; }

View File

@ -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; }

View File

@ -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; }

View File

@ -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; }

View 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");
}

View File

@ -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
View 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