staugaard-comatose 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +176 -0
- data/INSTALL +19 -0
- data/LICENSE +20 -0
- data/MANIFEST +91 -0
- data/README.rdoc +148 -0
- data/Rakefile +122 -0
- data/SPECS +61 -0
- data/about.yml +7 -0
- data/bin/comatose +109 -0
- data/comatose.gemspec +113 -0
- data/generators/comatose_migration/USAGE +15 -0
- data/generators/comatose_migration/comatose_migration_generator.rb +59 -0
- data/generators/comatose_migration/templates/migration.rb +35 -0
- data/generators/comatose_migration/templates/v4_upgrade.rb +15 -0
- data/generators/comatose_migration/templates/v6_upgrade.rb +23 -0
- data/generators/comatose_migration/templates/v7_upgrade.rb +22 -0
- data/init.rb +2 -0
- data/install.rb +16 -0
- data/lib/acts_as_versioned.rb +543 -0
- data/lib/comatose.rb +33 -0
- data/lib/comatose/comatose_drop.rb +79 -0
- data/lib/comatose/configuration.rb +68 -0
- data/lib/comatose/page_wrapper.rb +119 -0
- data/lib/comatose/processing_context.rb +69 -0
- data/lib/comatose/tasks/admin.rb +60 -0
- data/lib/comatose/tasks/data.rb +82 -0
- data/lib/comatose/tasks/setup.rb +52 -0
- data/lib/comatose/version.rb +4 -0
- data/lib/comatose_admin_controller.rb +349 -0
- data/lib/comatose_admin_helper.rb +37 -0
- data/lib/comatose_controller.rb +142 -0
- data/lib/comatose_helper.rb +3 -0
- data/lib/comatose_page.rb +141 -0
- data/lib/liquid.rb +52 -0
- data/lib/liquid/block.rb +96 -0
- data/lib/liquid/context.rb +190 -0
- data/lib/liquid/document.rb +17 -0
- data/lib/liquid/drop.rb +48 -0
- data/lib/liquid/errors.rb +7 -0
- data/lib/liquid/extensions.rb +53 -0
- data/lib/liquid/file_system.rb +62 -0
- data/lib/liquid/htmltags.rb +64 -0
- data/lib/liquid/standardfilters.rb +111 -0
- data/lib/liquid/standardtags.rb +399 -0
- data/lib/liquid/strainer.rb +42 -0
- data/lib/liquid/tag.rb +25 -0
- data/lib/liquid/template.rb +88 -0
- data/lib/liquid/variable.rb +39 -0
- data/lib/redcloth.rb +1129 -0
- data/lib/support/class_options.rb +36 -0
- data/lib/support/inline_rendering.rb +48 -0
- data/lib/support/route_mapper.rb +50 -0
- data/lib/text_filters.rb +138 -0
- data/lib/text_filters/markdown.rb +14 -0
- data/lib/text_filters/markdown_smartypants.rb +15 -0
- data/lib/text_filters/none.rb +8 -0
- data/lib/text_filters/rdoc.rb +13 -0
- data/lib/text_filters/simple.rb +8 -0
- data/lib/text_filters/textile.rb +15 -0
- data/rails/init.rb +3 -0
- data/resources/layouts/comatose_admin_template.html.erb +28 -0
- data/resources/public/images/collapsed.gif +0 -0
- data/resources/public/images/expanded.gif +0 -0
- data/resources/public/images/no-children.gif +0 -0
- data/resources/public/images/page.gif +0 -0
- data/resources/public/images/spinner.gif +0 -0
- data/resources/public/images/title-hover-bg.gif +0 -0
- data/resources/public/javascripts/comatose_admin.js +401 -0
- data/resources/public/stylesheets/comatose_admin.css +381 -0
- data/tasks/comatose.rake +9 -0
- data/test/behaviors.rb +106 -0
- data/test/fixtures/comatose_pages.yml +96 -0
- data/test/functional/comatose_admin_controller_test.rb +112 -0
- data/test/functional/comatose_controller_test.rb +44 -0
- data/test/javascripts/test.html +26 -0
- data/test/javascripts/test_runner.js +307 -0
- data/test/test_helper.rb +43 -0
- data/test/unit/class_options_test.rb +52 -0
- data/test/unit/comatose_page_test.rb +128 -0
- data/test/unit/processing_context_test.rb +108 -0
- data/test/unit/text_filters_test.rb +52 -0
- data/views/comatose_admin/_form.html.erb +96 -0
- data/views/comatose_admin/_page_list_item.html.erb +60 -0
- data/views/comatose_admin/delete.html.erb +18 -0
- data/views/comatose_admin/edit.html.erb +5 -0
- data/views/comatose_admin/index.html.erb +18 -0
- data/views/comatose_admin/new.html.erb +5 -0
- data/views/comatose_admin/reorder.html.erb +30 -0
- data/views/comatose_admin/versions.html.erb +40 -0
- data/views/layouts/comatose_admin.html.erb +814 -0
- data/views/layouts/comatose_admin_customize.html.erb +28 -0
- data/views/layouts/comatose_content.html.erb +17 -0
- metadata +147 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
class Class
|
2
|
+
def define_option(name, default=nil)
|
3
|
+
sym = name.to_sym
|
4
|
+
cattr_reader(sym)
|
5
|
+
cattr_writer(sym)
|
6
|
+
send("#{name.to_s}=", default)
|
7
|
+
end
|
8
|
+
|
9
|
+
def blockable_attr_accessor(sym)
|
10
|
+
module_eval(<<-EVAL, __FILE__, __LINE__)
|
11
|
+
def #{sym}(&block)
|
12
|
+
if block_given?
|
13
|
+
@#{sym} = block
|
14
|
+
else
|
15
|
+
@#{sym}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
def #{sym}=(value)
|
19
|
+
@#{sym} = value
|
20
|
+
end
|
21
|
+
EVAL
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Module
|
26
|
+
def attr_accessor_with_default(sym, default = nil, &block)
|
27
|
+
raise 'Default value or block required' unless !default.nil? || block
|
28
|
+
define_method(sym, block_given? ? block : Proc.new { default })
|
29
|
+
module_eval(<<-EVAL, __FILE__, __LINE__)
|
30
|
+
def #{sym}=(value)
|
31
|
+
class << self; attr_reader :#{sym} end
|
32
|
+
@#{sym} = value
|
33
|
+
end
|
34
|
+
EVAL
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Extends the view to support rendering inline comatose pages...
|
2
|
+
ActionView::Base.class_eval do
|
3
|
+
alias_method :render_otherwise, :render
|
4
|
+
|
5
|
+
def render(options = {}, old_local_assigns = {}, &block) #:nodoc:
|
6
|
+
if options.is_a?(Hash) && page_name = options.delete(:comatose)
|
7
|
+
render_comatose(page_name, options[:params] || options)
|
8
|
+
else
|
9
|
+
render_otherwise(options, old_local_assigns, &block)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def render_comatose(page_path, params = {})
|
14
|
+
params = {
|
15
|
+
:silent => false,
|
16
|
+
:use_cache => true,
|
17
|
+
:locals => {}
|
18
|
+
}.merge(params)
|
19
|
+
if params[:use_cache] and params[:locals].empty?
|
20
|
+
render_cached_comatose_page(page_path, params)
|
21
|
+
else
|
22
|
+
render_comatose_page(page_path, params)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def render_cached_comatose_page(page_path, params)
|
29
|
+
key = page_path.gsub(/\//, '+')
|
30
|
+
unless html = controller.read_fragment(key)
|
31
|
+
html = render_comatose_page( page_path, params )
|
32
|
+
controller.write_fragment(key, html) unless Comatose.config.disable_caching
|
33
|
+
end
|
34
|
+
html
|
35
|
+
end
|
36
|
+
|
37
|
+
def render_comatose_page(page_path, params)
|
38
|
+
if page = ComatosePage.find_by_path(page_path)
|
39
|
+
# Add the request params to the context...
|
40
|
+
params[:locals]['params'] = controller.params
|
41
|
+
html = page.to_html( params[:locals] )
|
42
|
+
else
|
43
|
+
html = params[:silent] ? '' : "<p><tt>#{page_path}</tt> not found</p>"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# For use with 'Edge Rails'
|
2
|
+
class ActionController::Routing::RouteSet::Mapper
|
3
|
+
|
4
|
+
# For mounting a page to a path
|
5
|
+
def comatose_root( path, options={} )
|
6
|
+
opts = {
|
7
|
+
:index => '',
|
8
|
+
:layout => 'comatose_content.html.erb',
|
9
|
+
:use_cache => 'true',
|
10
|
+
:cache_path => nil,
|
11
|
+
:named_route=> nil
|
12
|
+
}.merge(options)
|
13
|
+
# Ensure the controller is aware of the mount point...
|
14
|
+
Comatose.add_mount_point(path, opts)
|
15
|
+
# Add the route...
|
16
|
+
opts[:controller] = 'comatose'
|
17
|
+
opts[:action] ='show'
|
18
|
+
route_name = opts.delete(:named_route)
|
19
|
+
unless route_name.nil?
|
20
|
+
named_route( route_name, "#{path}/*page", opts )
|
21
|
+
else
|
22
|
+
if opts[:index] == '' # if it maps to the root site URI, name it comatose_root
|
23
|
+
named_route( 'comatose_root', "#{path}/*page", opts )
|
24
|
+
else
|
25
|
+
connect( "#{path}/*page", opts )
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# For mounting the admin
|
31
|
+
def comatose_admin( path='comatose_admin', options={} )
|
32
|
+
opts = {
|
33
|
+
:controller => 'comatose_admin',
|
34
|
+
:named_route => 'comatose_admin'
|
35
|
+
}.merge(options)
|
36
|
+
route_name = opts.delete(:named_route)
|
37
|
+
named_route( route_name, "#{path}/:action/:id", opts )
|
38
|
+
end
|
39
|
+
|
40
|
+
def method_missing( name, *args, &proc )
|
41
|
+
if name.to_s.starts_with?( 'comatose_' )
|
42
|
+
opts = (args.last.is_a?(Hash)) ? args.pop : {}
|
43
|
+
opts[:named_route] = name.to_s #[9..-1]
|
44
|
+
comatose_root( *(args << opts) )
|
45
|
+
else
|
46
|
+
super unless args.length >= 1 && proc.nil?
|
47
|
+
@set.add_named_route(name, *args)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/text_filters.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'support/class_options'
|
2
|
+
|
3
|
+
class TextFilters
|
4
|
+
|
5
|
+
define_option :default_processor, :liquid
|
6
|
+
define_option :default_filter, :textile
|
7
|
+
define_option :logger, nil
|
8
|
+
|
9
|
+
@registered_filters = {}
|
10
|
+
@registered_titles = {}
|
11
|
+
|
12
|
+
attr_reader :name
|
13
|
+
attr_reader :title
|
14
|
+
def initialize(name, title)
|
15
|
+
@name = name
|
16
|
+
@title = title
|
17
|
+
end
|
18
|
+
|
19
|
+
# The default create_link method...
|
20
|
+
# Override for your specific filter, if needed, in the #define block
|
21
|
+
def create_link(title, url)
|
22
|
+
%Q|<a href="#{url}">#{title}</a>|
|
23
|
+
end
|
24
|
+
|
25
|
+
# Process the text with using the specified context
|
26
|
+
def process_text(text, context, processor=nil)
|
27
|
+
case (processor || TextFilters.default_processor)
|
28
|
+
when :erb then process_with_erb(text, context)
|
29
|
+
when :liquid then process_with_liquid(text, context)
|
30
|
+
else raise "Unknown Text Processor '#{processor.to_s}'"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class << self
|
35
|
+
private :new
|
36
|
+
attr_reader :registered_filters
|
37
|
+
attr_reader :registered_titles
|
38
|
+
|
39
|
+
# Use this to create and register your TextFilters
|
40
|
+
def define(name, title, &block)
|
41
|
+
begin
|
42
|
+
p = new(name, title)
|
43
|
+
p.instance_eval(&block)
|
44
|
+
if p.respond_to? :render_text
|
45
|
+
registered_titles[title] = name
|
46
|
+
registered_filters[name] = p
|
47
|
+
else
|
48
|
+
raise "#render_text isn't implemented in this class"
|
49
|
+
end
|
50
|
+
rescue LoadError
|
51
|
+
TextFilters.logger.debug "Filter '#{name}' was not included: #{$!}" unless TextFilters.logger.nil?
|
52
|
+
rescue
|
53
|
+
TextFilters.logger.debug "Filter '#{name}' was not included: #{$!}" unless TextFilters.logger.nil?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_filter(name)
|
58
|
+
name = TextFilters.default_filter if name.nil?
|
59
|
+
name = registered_titles[name] if name.is_a? String
|
60
|
+
registered_filters[name]
|
61
|
+
end
|
62
|
+
|
63
|
+
def [](name)
|
64
|
+
get_filter(name)
|
65
|
+
end
|
66
|
+
|
67
|
+
def all
|
68
|
+
registered_filters
|
69
|
+
end
|
70
|
+
|
71
|
+
def all_titles
|
72
|
+
registered_titles.keys
|
73
|
+
end
|
74
|
+
|
75
|
+
def render_text(text, name=nil)
|
76
|
+
get_filter(name).render_text(text)
|
77
|
+
end
|
78
|
+
|
79
|
+
def process_text(text, context=nil, name=nil, processor=nil)
|
80
|
+
get_filter(name).process_text(text, context, processor)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Call
|
84
|
+
def transform(text, context=nil, name=nil, processor=nil)
|
85
|
+
render_text( process_text(text, context, name, processor), name )
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# This is an instance method so that it won't puke on requiring
|
92
|
+
# a non-existant library until it's being registered -- the only
|
93
|
+
# place I can really capture the LoadError
|
94
|
+
def require(name)
|
95
|
+
Kernel.require name
|
96
|
+
end
|
97
|
+
|
98
|
+
# Use ERB to process text...
|
99
|
+
def process_with_erb(text, context)
|
100
|
+
begin
|
101
|
+
ERB.new( text ).result( context.get_binding )
|
102
|
+
rescue
|
103
|
+
raise "ERB Error: #{$!}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Use Liquid to process text...
|
108
|
+
def process_with_liquid(text, context={})
|
109
|
+
begin
|
110
|
+
context = context.stringify_keys if context.respond_to? :stringify_keys
|
111
|
+
Liquid::Template.parse(text).render(context)
|
112
|
+
rescue
|
113
|
+
raise "Liquid Error: #{$!}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
class Hash
|
120
|
+
# Having the method_missing catchall in conjunction with get_binding
|
121
|
+
# allows us to use the hash as a Context for ERB
|
122
|
+
def method_missing(meth, *arg)
|
123
|
+
if self.has_key? meth.to_s
|
124
|
+
self[meth.to_s]
|
125
|
+
else
|
126
|
+
super
|
127
|
+
end
|
128
|
+
end
|
129
|
+
# Gets the binding object for use with ERB
|
130
|
+
def get_binding
|
131
|
+
binding
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
Dir[File.join(File.dirname(__FILE__), 'text_filters', '*.rb')].each do |path|
|
137
|
+
require "text_filters/#{File.basename(path)}"
|
138
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#
|
2
|
+
# MARKDOWN + SMARTYPANTS
|
3
|
+
#
|
4
|
+
TextFilters.define :markdown_smartypants, "Markdown + SmartyPants" do
|
5
|
+
require 'bluecloth'
|
6
|
+
require 'rubypants'
|
7
|
+
|
8
|
+
def render_text(text)
|
9
|
+
RubyPants.new( BlueCloth.new(text).to_html ).to_html
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_link(title, url)
|
13
|
+
"[#{title}](#{url})"
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#
|
2
|
+
# TEXTILE
|
3
|
+
#
|
4
|
+
TextFilters.define :textile, "Textile" do
|
5
|
+
require 'redcloth'
|
6
|
+
|
7
|
+
def render_text(text)
|
8
|
+
RedCloth.new(text).to_html(:refs_markdown, :textile, :markdown)
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_link(title, url)
|
12
|
+
%Q|"#{title}":#{url}|
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html class="noscript" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
+
<head>
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=<%%= Comatose.config.content_type %>"/>
|
6
|
+
<title>Comatose Admin</title>
|
7
|
+
<%= style_contents %>
|
8
|
+
<%%= javascript_include_tag :defaults %>
|
9
|
+
<%= script_contents %>
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
<div id="page-container">
|
13
|
+
<div id="header">
|
14
|
+
<h1><%%= link_to Comatose.config.admin_title, :controller=>controller.controller_name, :action=>'index' %></h1>
|
15
|
+
<div id="flash">
|
16
|
+
<span id="flash-content"><%%= flash[:notice] %></span>
|
17
|
+
</div>
|
18
|
+
<h5><%%= Comatose.config.admin_sub_title %></h5>
|
19
|
+
</div>
|
20
|
+
<div id="content">
|
21
|
+
<%%= yield %>
|
22
|
+
</div>
|
23
|
+
<div id="footer">
|
24
|
+
Powered by <a href="http://comatose.rubyforge.org" target="_new_window">Comatose <%%= Comatose::VERSION_STRING %></a>, created by <a href="http://www.mattmccray.com" target="_new_window">M@ McCray</a>.
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
</body>
|
28
|
+
</html>
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,401 @@
|
|
1
|
+
// CSS Browser Selector v0.2.3b (M@: added noscript support)
|
2
|
+
// Documentation: http://rafael.adm.br/css_browser_selector
|
3
|
+
// License: http://creativecommons.org/licenses/by/2.5/
|
4
|
+
// Author: Rafael Lima (http://rafael.adm.br)
|
5
|
+
// Contributors: http://rafael.adm.br/css_browser_selector#contributors
|
6
|
+
var css_browser_selector = function() {
|
7
|
+
var
|
8
|
+
ua = navigator.userAgent.toLowerCase(),
|
9
|
+
is = function(t){ return ua.indexOf(t) != -1; },
|
10
|
+
h = document.getElementsByTagName('html')[0],
|
11
|
+
b = (!(/opera|webtv/i.test(ua)) && /msie (\d)/.test(ua)) ? ((is('mac') ? 'ieMac ' : '') + 'ie ie' + RegExp.$1)
|
12
|
+
: is('gecko/') ? 'gecko' : is('opera') ? 'opera' : is('konqueror') ? 'konqueror' : is('applewebkit/') ? 'webkit safari' : is('mozilla/') ? 'gecko' : '',
|
13
|
+
os = (is('x11') || is('linux')) ? ' linux' : is('mac') ? ' mac' : is('win') ? ' win' : '';
|
14
|
+
var c = b+os+' js';
|
15
|
+
h.className = h.className.replace('noscript', '') + h.className?' '+c:c;
|
16
|
+
}();
|
17
|
+
|
18
|
+
// List View Functions
|
19
|
+
var ComatoseList = {
|
20
|
+
save_node_state: true,
|
21
|
+
state_store: 'cookie', // Only 'cookie' for now
|
22
|
+
state_key: 'ComatoseTreeState',
|
23
|
+
|
24
|
+
init: function() {
|
25
|
+
var items = ComatoseList._read_state();
|
26
|
+
items.each(function(node){
|
27
|
+
ComatoseList.expand_node(node.replace('page_controller_', ''))
|
28
|
+
});
|
29
|
+
},
|
30
|
+
|
31
|
+
toggle_tree_nodes : function(img, id) {
|
32
|
+
if(/expanded/.test(img.src)) {
|
33
|
+
$('page_list_'+ id).addClassName('collapsed');
|
34
|
+
img.src = img.src.replace(/expanded/, 'collapsed')
|
35
|
+
if(ComatoseList.save_node_state) {
|
36
|
+
var items = ComatoseList._read_state();
|
37
|
+
items = items.select(function(id){ return id != img.id; })
|
38
|
+
ComatoseList._write_state(items);
|
39
|
+
}
|
40
|
+
} else {
|
41
|
+
$('page_list_'+ id).removeClassName('collapsed');
|
42
|
+
img.src = img.src.replace(/collapsed/, 'expanded')
|
43
|
+
if(ComatoseList.save_node_state) {
|
44
|
+
var items = ComatoseList._read_state();
|
45
|
+
items.push(img.id);
|
46
|
+
ComatoseList._write_state(items);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
},
|
50
|
+
|
51
|
+
expand_node: function(id) {
|
52
|
+
$('page_list_'+ id).removeClassName('collapsed');
|
53
|
+
$('page_controller_'+ id).src = $('page_controller_'+ id).src.replace(/collapsed/, 'expanded')
|
54
|
+
},
|
55
|
+
|
56
|
+
collapse_node: function(id) {
|
57
|
+
$('page_list_'+ id).addClassName('collapsed');
|
58
|
+
$('page_controller_'+ id).src = $('page_controller_'+ id).src.replace(/expanded/, 'collapsed')
|
59
|
+
},
|
60
|
+
|
61
|
+
item_hover : function(node, state, is_delete) {
|
62
|
+
if( state == 'over') {
|
63
|
+
$(node).addClassName( (is_delete) ? 'hover-delete' : 'hover' );
|
64
|
+
} else {
|
65
|
+
$(node).removeClassName( (is_delete) ? 'hover-delete' : 'hover' );
|
66
|
+
}
|
67
|
+
},
|
68
|
+
|
69
|
+
toggle_reorder: function(node, anc, id) {
|
70
|
+
if( $(node).hasClassName('do-reorder') ) {
|
71
|
+
$(node).removeClassName( 'do-reorder' );
|
72
|
+
$(anc).removeClassName('reordering');
|
73
|
+
$(anc).innerHTML = "reorder children";
|
74
|
+
} else {
|
75
|
+
$(node).addClassName( 'do-reorder' );
|
76
|
+
$(anc).addClassName('reordering');
|
77
|
+
$(anc).innerHTML = "finished reordering";
|
78
|
+
// Make sure the children are visible...
|
79
|
+
ComatoseList.expand_node(id);
|
80
|
+
}
|
81
|
+
},
|
82
|
+
|
83
|
+
_write_state: function(items) {
|
84
|
+
var cookie = {}; var options = {}; var expiration = new Date();
|
85
|
+
cookie[ ComatoseList.state_key ] = items.join(',');
|
86
|
+
expiration.setDate(expiration.getDate()+30)
|
87
|
+
options['expires'] = expiration;
|
88
|
+
Cookie.write( cookie, options );
|
89
|
+
},
|
90
|
+
|
91
|
+
_read_state: function() {
|
92
|
+
var state = Cookie.read( ComatoseList.state_key );
|
93
|
+
return (state != "" && state != null) ? state.split(',') : [];
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
// Edit Form Functions
|
98
|
+
var ComatoseEditForm = {
|
99
|
+
|
100
|
+
default_data: {},
|
101
|
+
last_preview: {},
|
102
|
+
last_title_slug: '',
|
103
|
+
mode : null,
|
104
|
+
liquid_horiz: true,
|
105
|
+
width_offset: 325,
|
106
|
+
|
107
|
+
// Initialize the page...
|
108
|
+
init : function(mode) {
|
109
|
+
this.mode = mode;
|
110
|
+
this.default_data = Form.serialize(document.forms[0]);
|
111
|
+
if(mode == 'new') {
|
112
|
+
this.last_title_slug = $('page_title').value.toSlug();
|
113
|
+
Event.observe('page_title', 'blur', ComatoseEditForm.title_updated_aggressive);
|
114
|
+
} else {
|
115
|
+
Event.observe('page_title', 'blur', ComatoseEditForm.title_updated);
|
116
|
+
}
|
117
|
+
$('page_title').focus();
|
118
|
+
Hide.these(
|
119
|
+
'preview-area',
|
120
|
+
'slug_row',
|
121
|
+
'parent_row',
|
122
|
+
'keywords_row',
|
123
|
+
'filter_row',
|
124
|
+
'created_row'
|
125
|
+
);
|
126
|
+
$('page_title').select();
|
127
|
+
// Create the horizontal liquidity of the fields
|
128
|
+
if(this.liquid_horiz) {
|
129
|
+
xOffset = this.width_offset;
|
130
|
+
new Layout.LiquidHoriz((xOffset + 50), 'page_title');
|
131
|
+
new Layout.LiquidHoriz(xOffset, 'page_slug','page_keywords','page_parent','page_body');
|
132
|
+
}
|
133
|
+
},
|
134
|
+
// For use when updating an existing page...
|
135
|
+
title_updated : function() {
|
136
|
+
slug = $('page_slug');
|
137
|
+
if(slug.value == "") {
|
138
|
+
title = $('page_title');
|
139
|
+
slug.value = title.value.toSlug();
|
140
|
+
}
|
141
|
+
},
|
142
|
+
// For use when creating a new page...
|
143
|
+
title_updated_aggressive : function() {
|
144
|
+
slug = $('page_slug');
|
145
|
+
title = $('page_title');
|
146
|
+
if(slug.value == "" || slug.value == this.last_title ) {
|
147
|
+
slug.value = title.value.toSlug();
|
148
|
+
}
|
149
|
+
this.last_title = slug.value;
|
150
|
+
},
|
151
|
+
// Todo: Make the meta fields remember their visibility?
|
152
|
+
toggle_extra_fields : function(anchor) {
|
153
|
+
if(anchor.innerHTML == "More...") {
|
154
|
+
Show.these(
|
155
|
+
'slug_row',
|
156
|
+
'keywords_row',
|
157
|
+
'parent_row',
|
158
|
+
'filter_row',
|
159
|
+
'created_row'
|
160
|
+
);
|
161
|
+
anchor.innerHTML = 'Less...';
|
162
|
+
} else {
|
163
|
+
Hide.these(
|
164
|
+
'slug_row',
|
165
|
+
'keywords_row',
|
166
|
+
'parent_row',
|
167
|
+
'filter_row',
|
168
|
+
'created_row'
|
169
|
+
);
|
170
|
+
anchor.innerHTML = 'More...';
|
171
|
+
}
|
172
|
+
},
|
173
|
+
// Uses server to create preview of content...
|
174
|
+
preview_content : function(preview_url) {
|
175
|
+
$('preview-area').show();
|
176
|
+
var params = Form.serialize(document.forms[0]);
|
177
|
+
if( params != this.last_preview ) {
|
178
|
+
$('preview-panel').innerHTML = "<span style='color:blue;'>Loading Preview...</span>";
|
179
|
+
new Ajax.Updater(
|
180
|
+
'preview-panel',
|
181
|
+
preview_url,
|
182
|
+
{ parameters: params }
|
183
|
+
);
|
184
|
+
}
|
185
|
+
this.last_preview = params;
|
186
|
+
},
|
187
|
+
cancel : function(url) {
|
188
|
+
var current_data = Form.serialize(document.forms[0]);
|
189
|
+
var data_changed = (this.default_data != current_data)
|
190
|
+
if(data_changed) {
|
191
|
+
if( confirm('Changes detected. You will lose all the updates you have made if you proceed...') ) {
|
192
|
+
location.href = url;
|
193
|
+
}
|
194
|
+
} else {
|
195
|
+
location.href = url;
|
196
|
+
}
|
197
|
+
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
var Hide = {
|
202
|
+
these : function() {
|
203
|
+
for (var i = 0; i < arguments.length; i++) {
|
204
|
+
try {
|
205
|
+
$(arguments[i]).hide();
|
206
|
+
} catch (e) {}
|
207
|
+
}
|
208
|
+
}
|
209
|
+
}
|
210
|
+
|
211
|
+
var Show = {
|
212
|
+
these : function() {
|
213
|
+
for (var i = 0; i < arguments.length; i++) {
|
214
|
+
try {
|
215
|
+
$(arguments[i]).show();
|
216
|
+
} catch (e) {}
|
217
|
+
}
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
221
|
+
// Layout namespace
|
222
|
+
var Layout = {};
|
223
|
+
|
224
|
+
// This class allows dom objects to stretch with the browser
|
225
|
+
// (for when a good, cross-browser, CSS approach can't be found)
|
226
|
+
Layout.LiquidBase = Class.create();
|
227
|
+
// Base class for all Liquid* layouts...
|
228
|
+
Object.extend(Layout.LiquidBase.prototype, {
|
229
|
+
enabled: true,
|
230
|
+
elems: [],
|
231
|
+
offset: null,
|
232
|
+
// Constructor is (offset, **array_of_elements)
|
233
|
+
initialize: function() {
|
234
|
+
args = $A(arguments)
|
235
|
+
this.offset = args.shift();
|
236
|
+
this.elems = args.select( function(elem){ return ($(elem) != null) } );
|
237
|
+
if( this.elems.length > 0 ) {
|
238
|
+
this.on_resize(); // Initial size
|
239
|
+
Event.observe(window, 'resize', this.on_resize.bind(this) );
|
240
|
+
Event.observe(window, 'load', this.on_resize.bind(this) );
|
241
|
+
}
|
242
|
+
},
|
243
|
+
resize_in: function(timeout) {
|
244
|
+
setTimeout( this.on_resize.bind(this), timeout );
|
245
|
+
},
|
246
|
+
on_resize: function() {
|
247
|
+
// Need to override!
|
248
|
+
alert('Override on_resize, please!');
|
249
|
+
}
|
250
|
+
});
|
251
|
+
|
252
|
+
|
253
|
+
// Liquid vertical layout
|
254
|
+
Layout.LiquidVert = Class.create();
|
255
|
+
Object.extend(Layout.LiquidVert.prototype, Object.extend(Layout.LiquidBase.prototype, {
|
256
|
+
on_resize: function() {
|
257
|
+
if( this.offset != null && this.enabled ) {
|
258
|
+
var new_height = ((window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight) - this.offset) +"px";
|
259
|
+
this.elems.each(function(e){ $(e).style.height = new_height; })
|
260
|
+
}
|
261
|
+
}
|
262
|
+
}) );
|
263
|
+
|
264
|
+
|
265
|
+
// Liquid horizontal layout
|
266
|
+
Layout.LiquidHoriz = Class.create();
|
267
|
+
Object.extend(Layout.LiquidHoriz.prototype, Object.extend(Layout.LiquidBase.prototype, {
|
268
|
+
on_resize: function() {
|
269
|
+
if( this.offset != null && this.enabled ) {
|
270
|
+
var new_width = ((window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) - this.offset) +"px";
|
271
|
+
this.elems.each( function(e){ $(e).style.width = new_width; })
|
272
|
+
}
|
273
|
+
}
|
274
|
+
}) );
|
275
|
+
|
276
|
+
// String Extensions... Yes, these are from Radiant! ;-)
|
277
|
+
Object.extend(String.prototype, {
|
278
|
+
upcase: function() {
|
279
|
+
return this.toUpperCase();
|
280
|
+
},
|
281
|
+
downcase: function() {
|
282
|
+
return this.toLowerCase();
|
283
|
+
},
|
284
|
+
strip: function() {
|
285
|
+
return this.replace(/^\s+/, '').replace(/\s+$/, '');
|
286
|
+
},
|
287
|
+
toInteger: function() {
|
288
|
+
return parseInt(this);
|
289
|
+
},
|
290
|
+
toSlug: function() {
|
291
|
+
// M@: Modified from Radiant's version, removes multple --'s next to each other
|
292
|
+
// This is the same RegExp as the one on the page model...
|
293
|
+
return this.strip().downcase().replace(/[^-a-z0-9~\s\.:;+=_]/g, '').replace(/[\s\.:;=_+]+/g, '-').replace(/[\-]{2,}/g, '-');
|
294
|
+
}
|
295
|
+
});
|
296
|
+
|
297
|
+
// Run a spinner when an AJAX request in running...
|
298
|
+
var ComatoseAJAXSpinner = {
|
299
|
+
busy : function () {
|
300
|
+
if($('spinner') && Ajax.activeRequestCount > 0) {
|
301
|
+
Effect.Appear('spinner',{duration:0.5,queue:'end'});
|
302
|
+
}
|
303
|
+
},
|
304
|
+
|
305
|
+
notBusy: function() {
|
306
|
+
if($('spinner') && Ajax.activeRequestCount == 0) {
|
307
|
+
Effect.Fade('spinner',{duration:0.5,queue:'end'});
|
308
|
+
}
|
309
|
+
}
|
310
|
+
}
|
311
|
+
// Register it with Prototype...
|
312
|
+
Ajax.Responders.register({
|
313
|
+
onCreate: ComatoseAJAXSpinner.busy,
|
314
|
+
onComplete: ComatoseAJAXSpinner.notBusy
|
315
|
+
});
|
316
|
+
|
317
|
+
|
318
|
+
if(!window.Cookie)
|
319
|
+
(function (){
|
320
|
+
// From Mephisto!
|
321
|
+
window.Cookie = {
|
322
|
+
version: '0.7',
|
323
|
+
cookies: {},
|
324
|
+
_each: function(iterator) {
|
325
|
+
$H(this.cookies).each(iterator);
|
326
|
+
},
|
327
|
+
|
328
|
+
getAll: function() {
|
329
|
+
this.cookies = {};
|
330
|
+
$A(document.cookie.split('; ')).each(function(cookie) {
|
331
|
+
var seperator = cookie.indexOf('=');
|
332
|
+
this.cookies[cookie.substring(0, seperator)] =
|
333
|
+
unescape(cookie.substring(seperator + 1, cookie.length));
|
334
|
+
}.bind(this));
|
335
|
+
return this.cookies;
|
336
|
+
},
|
337
|
+
|
338
|
+
read: function() {
|
339
|
+
var cookies = $A(arguments), results = [];
|
340
|
+
this.getAll();
|
341
|
+
cookies.each(function(name) {
|
342
|
+
if (this.cookies[name]) results.push(this.cookies[name]);
|
343
|
+
else results.push(null);
|
344
|
+
}.bind(this));
|
345
|
+
return results.length > 1 ? results : results[0];
|
346
|
+
},
|
347
|
+
|
348
|
+
write: function(cookies, options) {
|
349
|
+
if (cookies.constructor == Object && cookies.name) cookies = [cookies];
|
350
|
+
if (cookies.constructor == Array) {
|
351
|
+
$A(cookies).each(function(cookie) {
|
352
|
+
this._write(cookie.name, cookie.value, cookie.expires,
|
353
|
+
cookie.path, cookie.domain);
|
354
|
+
}.bind(this));
|
355
|
+
} else {
|
356
|
+
options = options || {expires: false, path: '', domain: ''};
|
357
|
+
for (name in cookies){
|
358
|
+
this._write(name, cookies[name],
|
359
|
+
options.expires, options.path, options.domain);
|
360
|
+
}
|
361
|
+
}
|
362
|
+
},
|
363
|
+
|
364
|
+
_write: function(name, value, expires, path, domain) {
|
365
|
+
if (name.indexOf('=') != -1) return;
|
366
|
+
var cookieString = name + '=' + escape(value);
|
367
|
+
if (expires) cookieString += '; expires=' + expires.toGMTString();
|
368
|
+
if (path) cookieString += '; path=' + path;
|
369
|
+
if (domain) cookieString += '; domain=' + domain;
|
370
|
+
document.cookie = cookieString;
|
371
|
+
},
|
372
|
+
|
373
|
+
erase: function(cookies) {
|
374
|
+
var cookiesToErase = {};
|
375
|
+
$A(arguments).each(function(cookie) {
|
376
|
+
cookiesToErase[cookie] = '';
|
377
|
+
});
|
378
|
+
|
379
|
+
this.write(cookiesToErase, {expires: (new Date((new Date()).getTime() - 1e11))});
|
380
|
+
this.getAll();
|
381
|
+
},
|
382
|
+
|
383
|
+
eraseAll: function() {
|
384
|
+
this.erase.apply(this, $H(this.getAll()).keys());
|
385
|
+
}
|
386
|
+
};
|
387
|
+
|
388
|
+
Object.extend(Cookie, {
|
389
|
+
get: Cookie.read,
|
390
|
+
set: Cookie.write,
|
391
|
+
|
392
|
+
add: Cookie.read,
|
393
|
+
remove: Cookie.erase,
|
394
|
+
removeAll: Cookie.eraseAll,
|
395
|
+
|
396
|
+
wipe: Cookie.erase,
|
397
|
+
wipeAll: Cookie.eraseAll,
|
398
|
+
destroy: Cookie.erase,
|
399
|
+
destroyAll: Cookie.eraseAll
|
400
|
+
});
|
401
|
+
})();
|