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.
Files changed (93) hide show
  1. data/CHANGELOG +176 -0
  2. data/INSTALL +19 -0
  3. data/LICENSE +20 -0
  4. data/MANIFEST +91 -0
  5. data/README.rdoc +148 -0
  6. data/Rakefile +122 -0
  7. data/SPECS +61 -0
  8. data/about.yml +7 -0
  9. data/bin/comatose +109 -0
  10. data/comatose.gemspec +113 -0
  11. data/generators/comatose_migration/USAGE +15 -0
  12. data/generators/comatose_migration/comatose_migration_generator.rb +59 -0
  13. data/generators/comatose_migration/templates/migration.rb +35 -0
  14. data/generators/comatose_migration/templates/v4_upgrade.rb +15 -0
  15. data/generators/comatose_migration/templates/v6_upgrade.rb +23 -0
  16. data/generators/comatose_migration/templates/v7_upgrade.rb +22 -0
  17. data/init.rb +2 -0
  18. data/install.rb +16 -0
  19. data/lib/acts_as_versioned.rb +543 -0
  20. data/lib/comatose.rb +33 -0
  21. data/lib/comatose/comatose_drop.rb +79 -0
  22. data/lib/comatose/configuration.rb +68 -0
  23. data/lib/comatose/page_wrapper.rb +119 -0
  24. data/lib/comatose/processing_context.rb +69 -0
  25. data/lib/comatose/tasks/admin.rb +60 -0
  26. data/lib/comatose/tasks/data.rb +82 -0
  27. data/lib/comatose/tasks/setup.rb +52 -0
  28. data/lib/comatose/version.rb +4 -0
  29. data/lib/comatose_admin_controller.rb +349 -0
  30. data/lib/comatose_admin_helper.rb +37 -0
  31. data/lib/comatose_controller.rb +142 -0
  32. data/lib/comatose_helper.rb +3 -0
  33. data/lib/comatose_page.rb +141 -0
  34. data/lib/liquid.rb +52 -0
  35. data/lib/liquid/block.rb +96 -0
  36. data/lib/liquid/context.rb +190 -0
  37. data/lib/liquid/document.rb +17 -0
  38. data/lib/liquid/drop.rb +48 -0
  39. data/lib/liquid/errors.rb +7 -0
  40. data/lib/liquid/extensions.rb +53 -0
  41. data/lib/liquid/file_system.rb +62 -0
  42. data/lib/liquid/htmltags.rb +64 -0
  43. data/lib/liquid/standardfilters.rb +111 -0
  44. data/lib/liquid/standardtags.rb +399 -0
  45. data/lib/liquid/strainer.rb +42 -0
  46. data/lib/liquid/tag.rb +25 -0
  47. data/lib/liquid/template.rb +88 -0
  48. data/lib/liquid/variable.rb +39 -0
  49. data/lib/redcloth.rb +1129 -0
  50. data/lib/support/class_options.rb +36 -0
  51. data/lib/support/inline_rendering.rb +48 -0
  52. data/lib/support/route_mapper.rb +50 -0
  53. data/lib/text_filters.rb +138 -0
  54. data/lib/text_filters/markdown.rb +14 -0
  55. data/lib/text_filters/markdown_smartypants.rb +15 -0
  56. data/lib/text_filters/none.rb +8 -0
  57. data/lib/text_filters/rdoc.rb +13 -0
  58. data/lib/text_filters/simple.rb +8 -0
  59. data/lib/text_filters/textile.rb +15 -0
  60. data/rails/init.rb +3 -0
  61. data/resources/layouts/comatose_admin_template.html.erb +28 -0
  62. data/resources/public/images/collapsed.gif +0 -0
  63. data/resources/public/images/expanded.gif +0 -0
  64. data/resources/public/images/no-children.gif +0 -0
  65. data/resources/public/images/page.gif +0 -0
  66. data/resources/public/images/spinner.gif +0 -0
  67. data/resources/public/images/title-hover-bg.gif +0 -0
  68. data/resources/public/javascripts/comatose_admin.js +401 -0
  69. data/resources/public/stylesheets/comatose_admin.css +381 -0
  70. data/tasks/comatose.rake +9 -0
  71. data/test/behaviors.rb +106 -0
  72. data/test/fixtures/comatose_pages.yml +96 -0
  73. data/test/functional/comatose_admin_controller_test.rb +112 -0
  74. data/test/functional/comatose_controller_test.rb +44 -0
  75. data/test/javascripts/test.html +26 -0
  76. data/test/javascripts/test_runner.js +307 -0
  77. data/test/test_helper.rb +43 -0
  78. data/test/unit/class_options_test.rb +52 -0
  79. data/test/unit/comatose_page_test.rb +128 -0
  80. data/test/unit/processing_context_test.rb +108 -0
  81. data/test/unit/text_filters_test.rb +52 -0
  82. data/views/comatose_admin/_form.html.erb +96 -0
  83. data/views/comatose_admin/_page_list_item.html.erb +60 -0
  84. data/views/comatose_admin/delete.html.erb +18 -0
  85. data/views/comatose_admin/edit.html.erb +5 -0
  86. data/views/comatose_admin/index.html.erb +18 -0
  87. data/views/comatose_admin/new.html.erb +5 -0
  88. data/views/comatose_admin/reorder.html.erb +30 -0
  89. data/views/comatose_admin/versions.html.erb +40 -0
  90. data/views/layouts/comatose_admin.html.erb +814 -0
  91. data/views/layouts/comatose_admin_customize.html.erb +28 -0
  92. data/views/layouts/comatose_content.html.erb +17 -0
  93. 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
@@ -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,14 @@
1
+ #
2
+ # MARKDOWN
3
+ #
4
+ TextFilters.define :markdown, "Markdown" do
5
+ require 'bluecloth'
6
+
7
+ def render_text(text)
8
+ BlueCloth.new(text).to_html
9
+ end
10
+
11
+ def create_link(title, url)
12
+ "[#{title}](#{url})"
13
+ end
14
+ 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,8 @@
1
+ #
2
+ # NONE
3
+ #
4
+ TextFilters.define :none, "[No Filter]" do
5
+ def render_text(text)
6
+ text
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ #
2
+ # RDOC
3
+ #
4
+ TextFilters.define :rdoc, "RDoc" do
5
+ require 'rdoc/markup/simple_markup'
6
+ require 'rdoc/markup/simple_markup/to_html'
7
+
8
+ def render_text(text)
9
+ p = SM::SimpleMarkup.new
10
+ h = SM::ToHtml.new
11
+ p.convert(text, h)
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ #
2
+ # SIMPLE
3
+ #
4
+ TextFilters.define :simple, "Simple" do
5
+ def render_text(text)
6
+ text.gsub("\n", '<br/>')
7
+ end
8
+ 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,3 @@
1
+ # Init for gem version of Comatose
2
+
3
+ require 'comatose'
@@ -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
@@ -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
+ })();