wontomedia 0.0.1

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 (193) hide show
  1. data/COPYING +661 -0
  2. data/COPYING.DOCUMENTATION +450 -0
  3. data/LICENSE +661 -0
  4. data/README.markdown +56 -0
  5. data/Rakefile +185 -0
  6. data/VERSION.yml +4 -0
  7. data/app/controllers/admin_controller.rb +144 -0
  8. data/app/controllers/application_controller.rb +29 -0
  9. data/app/controllers/connections_controller.rb +150 -0
  10. data/app/controllers/items_controller.rb +365 -0
  11. data/app/helpers/connections_helper.rb +67 -0
  12. data/app/helpers/format_helper.rb +154 -0
  13. data/app/helpers/items_helper.rb +105 -0
  14. data/app/models/category_item.rb +23 -0
  15. data/app/models/connection.rb +210 -0
  16. data/app/models/individual_item.rb +23 -0
  17. data/app/models/item.rb +86 -0
  18. data/app/models/property_item.rb +23 -0
  19. data/app/models/qualified_item.rb +23 -0
  20. data/app/views/admin/index.html.erb +74 -0
  21. data/app/views/connections/_index_outbound_links.html.erb +48 -0
  22. data/app/views/connections/_spo_select_controls.html.erb +151 -0
  23. data/app/views/connections/edit.html.erb +70 -0
  24. data/app/views/connections/index.html.erb +84 -0
  25. data/app/views/connections/new.html.erb +61 -0
  26. data/app/views/connections/show.html.erb +110 -0
  27. data/app/views/items/_active_content.html.erb +26 -0
  28. data/app/views/items/_content_examples.html.erb +55 -0
  29. data/app/views/items/_core_tasks.html.erb +23 -0
  30. data/app/views/items/_form_fields.html.erb +69 -0
  31. data/app/views/items/_list_outbound_links.html.erb +58 -0
  32. data/app/views/items/_screen_select.html.erb +24 -0
  33. data/app/views/items/_show_outbound_links.html.erb +70 -0
  34. data/app/views/items/_topic_list.html.erb +26 -0
  35. data/app/views/items/_type_select.html.erb +47 -0
  36. data/app/views/items/edit.html.erb +63 -0
  37. data/app/views/items/index.html.erb +74 -0
  38. data/app/views/items/new.html.erb +124 -0
  39. data/app/views/items/newpop.html.erb +58 -0
  40. data/app/views/items/show.html.erb +209 -0
  41. data/app/views/layouts/_amazon_ads.html.erb +55 -0
  42. data/app/views/layouts/_glossary_help_box.html.erb +43 -0
  43. data/app/views/layouts/_google_ads.html.erb +40 -0
  44. data/app/views/layouts/_language_select.html.erb +24 -0
  45. data/app/views/layouts/_login_controls.html.erb +24 -0
  46. data/app/views/layouts/_master_help.html.erb +24 -0
  47. data/app/views/layouts/_navigation_menu.html.erb +39 -0
  48. data/app/views/layouts/_search_box.html.erb +27 -0
  49. data/app/views/layouts/_status_msgs.html.erb +25 -0
  50. data/app/views/layouts/application.html.erb +67 -0
  51. data/app/views/layouts/base.html.erb +93 -0
  52. data/app/views/layouts/home.html.erb +67 -0
  53. data/app/views/layouts/popup.html.erb +23 -0
  54. data/assets/wontomedia-sample.rb +47 -0
  55. data/config/asset_packages.yml +44 -0
  56. data/config/boot.rb +110 -0
  57. data/config/cucumber.yml +65 -0
  58. data/config/database-mysql-development.yml +47 -0
  59. data/config/database-mysql.yml +26 -0
  60. data/config/environment.rb +94 -0
  61. data/config/environments/cucumber.rb +29 -0
  62. data/config/environments/development.rb +26 -0
  63. data/config/environments/production.rb +33 -0
  64. data/config/environments/test.rb +31 -0
  65. data/config/initializers/inflections.rb +19 -0
  66. data/config/initializers/mime_types.rb +14 -0
  67. data/config/initializers/new_rails_defaults.rb +26 -0
  68. data/config/locales/en.yml +14 -0
  69. data/config/routes.rb +37 -0
  70. data/db/fixtures/connections.yml +96 -0
  71. data/db/fixtures/items.yml +277 -0
  72. data/db/migrate/20090312212805_create_items.rb +31 -0
  73. data/db/migrate/20090406221320_create_connections.rb +32 -0
  74. data/db/migrate/20090411014503_add_type_for_item_subclasses.rb +29 -0
  75. data/db/migrate/20090415142152_rename_item_type.rb +31 -0
  76. data/db/migrate/20090518022918_rename_object_and_self.rb +33 -0
  77. data/db/migrate/20090529171442_add_flags_to_items.rb +27 -0
  78. data/db/migrate/20090529171508_add_flags_to_connections.rb +27 -0
  79. data/db/migrate/20090605213800_flags_columns_not_null.rb +29 -0
  80. data/db/migrate/20090605215028_flags_columns_default_zero.rb +29 -0
  81. data/db/schema.rb +30 -0
  82. data/default-custom/app/views/items/_home_extern_list.html.erb +32 -0
  83. data/default-custom/app/views/items/_home_introductory_text.html.erb +70 -0
  84. data/default-custom/app/views/items/home.html.erb +77 -0
  85. data/default-custom/public/images/logo.png +0 -0
  86. data/default-custom/public/images/logo.svg +74 -0
  87. data/default-custom/public/stylesheets/wm.css +301 -0
  88. data/doc/README.markdown +44 -0
  89. data/doc/README_FOR_APP +82 -0
  90. data/doc/customization.markdown +93 -0
  91. data/doc/scripts/rake-customize.markdown +83 -0
  92. data/lib/helpers/connection_helper.rb +41 -0
  93. data/lib/helpers/item_helper.rb +141 -0
  94. data/lib/helpers/tripple_navigation.rb +158 -0
  95. data/lib/tasks/cucumber.rake +83 -0
  96. data/lib/tasks/customize.rake +70 -0
  97. data/lib/tasks/db.rake +65 -0
  98. data/lib/tasks/javascript_testing_tasks.rake +176 -0
  99. data/public/404.html +50 -0
  100. data/public/422.html +50 -0
  101. data/public/500.html +53 -0
  102. data/public/dispatch.cgi +20 -0
  103. data/public/dispatch.fcgi +34 -0
  104. data/public/dispatch.rb +20 -0
  105. data/public/favicon.ico +0 -0
  106. data/public/images/blank_error_icon.png +0 -0
  107. data/public/images/blank_status_icon.png +0 -0
  108. data/public/images/error_error_icon.png +0 -0
  109. data/public/images/error_status_icon.png +0 -0
  110. data/public/images/good_status_icon.png +0 -0
  111. data/public/images/help_icon.png +0 -0
  112. data/public/images/twitter_icon.png +0 -0
  113. data/public/images/warn_error_icon.png +0 -0
  114. data/public/images/working_status_icon.gif +0 -0
  115. data/public/javascripts/application.js +56 -0
  116. data/public/javascripts/controls.js +963 -0
  117. data/public/javascripts/dragdrop.js +973 -0
  118. data/public/javascripts/effects.js +1128 -0
  119. data/public/javascripts/event.simulate.js +64 -0
  120. data/public/javascripts/fancybox.js +43 -0
  121. data/public/javascripts/forConnectionsForms.js +218 -0
  122. data/public/javascripts/forItemsForms.js +511 -0
  123. data/public/javascripts/itemCreatePopup.js +115 -0
  124. data/public/javascripts/itemTitleToName.js +119 -0
  125. data/public/javascripts/jquery.js +4376 -0
  126. data/public/javascripts/modalbox.js +502 -0
  127. data/public/javascripts/prototype.js +4320 -0
  128. data/public/javascripts/reconcileJQueryAndPrototype.js +19 -0
  129. data/public/robots.txt +7 -0
  130. data/public/stylesheets/blank.gif +0 -0
  131. data/public/stylesheets/fancy_close.png +0 -0
  132. data/public/stylesheets/fancy_loading.png +0 -0
  133. data/public/stylesheets/fancy_nav_left.png +0 -0
  134. data/public/stylesheets/fancy_nav_right.png +0 -0
  135. data/public/stylesheets/fancy_shadow_e.png +0 -0
  136. data/public/stylesheets/fancy_shadow_n.png +0 -0
  137. data/public/stylesheets/fancy_shadow_ne.png +0 -0
  138. data/public/stylesheets/fancy_shadow_nw.png +0 -0
  139. data/public/stylesheets/fancy_shadow_s.png +0 -0
  140. data/public/stylesheets/fancy_shadow_se.png +0 -0
  141. data/public/stylesheets/fancy_shadow_sw.png +0 -0
  142. data/public/stylesheets/fancy_shadow_w.png +0 -0
  143. data/public/stylesheets/fancy_title_left.png +0 -0
  144. data/public/stylesheets/fancy_title_main.png +0 -0
  145. data/public/stylesheets/fancy_title_over.png +0 -0
  146. data/public/stylesheets/fancy_title_right.png +0 -0
  147. data/public/stylesheets/fancybox.css +333 -0
  148. data/public/stylesheets/modalbox.css +110 -0
  149. data/public/stylesheets/scaffold.css +36 -0
  150. data/public/stylesheets/spinner.gif +0 -0
  151. data/script/about +4 -0
  152. data/script/console +3 -0
  153. data/script/cucumber +10 -0
  154. data/script/dbconsole +3 -0
  155. data/script/destroy +3 -0
  156. data/script/generate +3 -0
  157. data/script/performance/benchmarker +3 -0
  158. data/script/performance/profiler +3 -0
  159. data/script/performance/request +3 -0
  160. data/script/plugin +3 -0
  161. data/script/process/inspector +3 -0
  162. data/script/process/reaper +3 -0
  163. data/script/process/spawner +3 -0
  164. data/script/runner +3 -0
  165. data/script/server +3 -0
  166. data/vendor/plugins/asset_packager/CHANGELOG +122 -0
  167. data/vendor/plugins/asset_packager/README +178 -0
  168. data/vendor/plugins/asset_packager/Rakefile +22 -0
  169. data/vendor/plugins/asset_packager/about.yml +8 -0
  170. data/vendor/plugins/asset_packager/init.rb +2 -0
  171. data/vendor/plugins/asset_packager/install.rb +1 -0
  172. data/vendor/plugins/asset_packager/lib/jsmin.rb +205 -0
  173. data/vendor/plugins/asset_packager/lib/synthesis/asset_package.rb +212 -0
  174. data/vendor/plugins/asset_packager/lib/synthesis/asset_package_helper.rb +39 -0
  175. data/vendor/plugins/asset_packager/tasks/asset_packager_tasks.rake +23 -0
  176. data/vendor/plugins/asset_packager/test/asset_package_helper_development_test.rb +100 -0
  177. data/vendor/plugins/asset_packager/test/asset_package_helper_production_test.rb +140 -0
  178. data/vendor/plugins/asset_packager/test/asset_packager_test.rb +92 -0
  179. data/vendor/plugins/asset_packager/test/asset_packages.yml +20 -0
  180. data/vendor/plugins/asset_packager/test/assets/javascripts/application.js +2 -0
  181. data/vendor/plugins/asset_packager/test/assets/javascripts/bar.js +4 -0
  182. data/vendor/plugins/asset_packager/test/assets/javascripts/controls.js +815 -0
  183. data/vendor/plugins/asset_packager/test/assets/javascripts/dragdrop.js +913 -0
  184. data/vendor/plugins/asset_packager/test/assets/javascripts/effects.js +958 -0
  185. data/vendor/plugins/asset_packager/test/assets/javascripts/foo.js +4 -0
  186. data/vendor/plugins/asset_packager/test/assets/javascripts/prototype.js +2006 -0
  187. data/vendor/plugins/asset_packager/test/assets/stylesheets/bar.css +16 -0
  188. data/vendor/plugins/asset_packager/test/assets/stylesheets/foo.css +16 -0
  189. data/vendor/plugins/asset_packager/test/assets/stylesheets/header.css +16 -0
  190. data/vendor/plugins/asset_packager/test/assets/stylesheets/screen.css +16 -0
  191. data/vendor/plugins/asset_packager/test/assets/stylesheets/subdir/bar.css +16 -0
  192. data/vendor/plugins/asset_packager/test/assets/stylesheets/subdir/foo.css +16 -0
  193. metadata +265 -0
@@ -0,0 +1,23 @@
1
+ require 'yaml'
2
+ require File.dirname(__FILE__) + '/../lib/synthesis/asset_package'
3
+
4
+ namespace :asset do
5
+ namespace :packager do
6
+
7
+ desc "Merge and compress assets"
8
+ task :build_all do
9
+ Synthesis::AssetPackage.build_all
10
+ end
11
+
12
+ desc "Delete all asset builds"
13
+ task :delete_all do
14
+ Synthesis::AssetPackage.delete_all
15
+ end
16
+
17
+ desc "Generate asset_packages.yml from existing assets"
18
+ task :create_yml do
19
+ Synthesis::AssetPackage.create_yml
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,100 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ ENV['RAILS_ENV'] = "development"
4
+ require File.dirname(__FILE__) + '/../../../../config/environment'
5
+ require 'test/unit'
6
+ require 'rubygems'
7
+ require 'mocha'
8
+
9
+ require 'action_controller/test_process'
10
+
11
+ ActionController::Base.logger = nil
12
+ ActionController::Routing::Routes.reload rescue nil
13
+
14
+ $asset_packages_yml = YAML.load_file("#{RAILS_ROOT}/vendor/plugins/asset_packager/test/asset_packages.yml")
15
+ $asset_base_path = "#{RAILS_ROOT}/vendor/plugins/asset_packager/test/assets"
16
+
17
+ class AssetPackageHelperDevelopmentTest < Test::Unit::TestCase
18
+ include ActionView::Helpers::TagHelper
19
+ include ActionView::Helpers::AssetTagHelper
20
+ include Synthesis::AssetPackageHelper
21
+
22
+ def setup
23
+ Synthesis::AssetPackage.any_instance.stubs(:log)
24
+
25
+ @controller = Class.new do
26
+ def request
27
+ @request ||= ActionController::TestRequest.new
28
+ end
29
+ end.new
30
+ end
31
+
32
+ def build_js_expected_string(*sources)
33
+ sources.map {|s| javascript_include_tag(s) }.join("\n")
34
+ end
35
+
36
+ def build_css_expected_string(*sources)
37
+ sources.map {|s| stylesheet_link_tag(s) }.join("\n")
38
+ end
39
+
40
+ def test_js_basic
41
+ assert_dom_equal build_js_expected_string("prototype"),
42
+ javascript_include_merged("prototype")
43
+ end
44
+
45
+ def test_js_multiple_packages
46
+ assert_dom_equal build_js_expected_string("prototype", "foo"),
47
+ javascript_include_merged("prototype", "foo")
48
+ end
49
+
50
+ def test_js_unpackaged_file
51
+ assert_dom_equal build_js_expected_string("prototype", "foo", "not_part_of_a_package"),
52
+ javascript_include_merged("prototype", "foo", "not_part_of_a_package")
53
+ end
54
+
55
+ def test_js_multiple_from_same_package
56
+ assert_dom_equal build_js_expected_string("prototype", "effects", "controls", "not_part_of_a_package", "foo"),
57
+ javascript_include_merged("prototype", "effects", "controls", "not_part_of_a_package", "foo")
58
+ end
59
+
60
+ def test_js_by_package_name
61
+ assert_dom_equal build_js_expected_string("prototype", "effects", "controls", "dragdrop"),
62
+ javascript_include_merged(:base)
63
+ end
64
+
65
+ def test_js_multiple_package_names
66
+ assert_dom_equal build_js_expected_string("prototype", "effects", "controls", "dragdrop", "foo", "bar", "application"),
67
+ javascript_include_merged(:base, :secondary)
68
+ end
69
+
70
+ def test_css_basic
71
+ assert_dom_equal build_css_expected_string("screen"),
72
+ stylesheet_link_merged("screen")
73
+ end
74
+
75
+ def test_css_multiple_packages
76
+ assert_dom_equal build_css_expected_string("screen", "foo", "subdir/bar"),
77
+ stylesheet_link_merged("screen", "foo", "subdir/bar")
78
+ end
79
+
80
+ def test_css_unpackaged_file
81
+ assert_dom_equal build_css_expected_string("screen", "foo", "not_part_of_a_package", "subdir/bar"),
82
+ stylesheet_link_merged("screen", "foo", "not_part_of_a_package", "subdir/bar")
83
+ end
84
+
85
+ def test_css_multiple_from_same_package
86
+ assert_dom_equal build_css_expected_string("screen", "header", "not_part_of_a_package", "foo", "bar", "subdir/foo", "subdir/bar"),
87
+ stylesheet_link_merged("screen", "header", "not_part_of_a_package", "foo", "bar", "subdir/foo", "subdir/bar")
88
+ end
89
+
90
+ def test_css_by_package_name
91
+ assert_dom_equal build_css_expected_string("screen", "header"),
92
+ stylesheet_link_merged(:base)
93
+ end
94
+
95
+ def test_css_multiple_package_names
96
+ assert_dom_equal build_css_expected_string("screen", "header", "foo", "bar", "subdir/foo", "subdir/bar"),
97
+ stylesheet_link_merged(:base, :secondary, "subdir/styles")
98
+ end
99
+
100
+ end
@@ -0,0 +1,140 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require File.dirname(__FILE__) + '/../../../../config/environment'
4
+ require 'test/unit'
5
+ require 'rubygems'
6
+ require 'mocha'
7
+
8
+ require 'action_controller/test_process'
9
+
10
+ ActionController::Base.logger = nil
11
+ ActionController::Routing::Routes.reload rescue nil
12
+
13
+ $asset_packages_yml = YAML.load_file("#{RAILS_ROOT}/vendor/plugins/asset_packager/test/asset_packages.yml")
14
+ $asset_base_path = "#{RAILS_ROOT}/vendor/plugins/asset_packager/test/assets"
15
+
16
+ class AssetPackageHelperProductionTest < Test::Unit::TestCase
17
+ include ActionView::Helpers::TagHelper
18
+ include ActionView::Helpers::AssetTagHelper
19
+ include Synthesis::AssetPackageHelper
20
+
21
+ cattr_accessor :packages_built
22
+
23
+ def setup
24
+ Synthesis::AssetPackage.any_instance.stubs(:log)
25
+ self.stubs(:should_merge?).returns(true)
26
+
27
+ @controller = Class.new do
28
+ def request
29
+ @request ||= ActionController::TestRequest.new
30
+ end
31
+ end.new
32
+
33
+ build_packages_once
34
+ end
35
+
36
+ def build_packages_once
37
+ unless @@packages_built
38
+ Synthesis::AssetPackage.build_all
39
+ @@packages_built = true
40
+ end
41
+ end
42
+
43
+ def build_js_expected_string(*sources)
44
+ sources.map {|s| javascript_include_tag(s) }.join("\n")
45
+ end
46
+
47
+ def build_css_expected_string(*sources)
48
+ sources.map {|s| stylesheet_link_tag(s) }.join("\n")
49
+ end
50
+
51
+ def test_js_basic
52
+ current_file = Synthesis::AssetPackage.find_by_source("javascripts", "prototype").current_file
53
+ assert_dom_equal build_js_expected_string(current_file),
54
+ javascript_include_merged("prototype")
55
+ end
56
+
57
+ def test_js_multiple_packages
58
+ current_file1 = Synthesis::AssetPackage.find_by_source("javascripts", "prototype").current_file
59
+ current_file2 = Synthesis::AssetPackage.find_by_source("javascripts", "foo").current_file
60
+
61
+ assert_dom_equal build_js_expected_string(current_file1, current_file2),
62
+ javascript_include_merged("prototype", "foo")
63
+ end
64
+
65
+ def test_js_unpackaged_file
66
+ current_file1 = Synthesis::AssetPackage.find_by_source("javascripts", "prototype").current_file
67
+ current_file2 = Synthesis::AssetPackage.find_by_source("javascripts", "foo").current_file
68
+
69
+ assert_dom_equal build_js_expected_string(current_file1, current_file2, "not_part_of_a_package"),
70
+ javascript_include_merged("prototype", "foo", "not_part_of_a_package")
71
+ end
72
+
73
+ def test_js_multiple_from_same_package
74
+ current_file1 = Synthesis::AssetPackage.find_by_source("javascripts", "prototype").current_file
75
+ current_file2 = Synthesis::AssetPackage.find_by_source("javascripts", "foo").current_file
76
+
77
+ assert_dom_equal build_js_expected_string(current_file1, "not_part_of_a_package", current_file2),
78
+ javascript_include_merged("prototype", "effects", "controls", "not_part_of_a_package", "foo")
79
+ end
80
+
81
+ def test_js_by_package_name
82
+ package_name = Synthesis::AssetPackage.find_by_target("javascripts", "base").current_file
83
+ assert_dom_equal build_js_expected_string(package_name),
84
+ javascript_include_merged(:base)
85
+ end
86
+
87
+ def test_js_multiple_package_names
88
+ package_name1 = Synthesis::AssetPackage.find_by_target("javascripts", "base").current_file
89
+ package_name2 = Synthesis::AssetPackage.find_by_target("javascripts", "secondary").current_file
90
+ assert_dom_equal build_js_expected_string(package_name1, package_name2),
91
+ javascript_include_merged(:base, :secondary)
92
+ end
93
+
94
+ def test_css_basic
95
+ current_file = Synthesis::AssetPackage.find_by_source("stylesheets", "screen").current_file
96
+ assert_dom_equal build_css_expected_string(current_file),
97
+ stylesheet_link_merged("screen")
98
+ end
99
+
100
+ def test_css_multiple_packages
101
+ current_file1 = Synthesis::AssetPackage.find_by_source("stylesheets", "screen").current_file
102
+ current_file2 = Synthesis::AssetPackage.find_by_source("stylesheets", "foo").current_file
103
+ current_file3 = Synthesis::AssetPackage.find_by_source("stylesheets", "subdir/bar").current_file
104
+
105
+ assert_dom_equal build_css_expected_string(current_file1, current_file2, current_file3),
106
+ stylesheet_link_merged("screen", "foo", "subdir/bar")
107
+ end
108
+
109
+ def test_css_unpackaged_file
110
+ current_file1 = Synthesis::AssetPackage.find_by_source("stylesheets", "screen").current_file
111
+ current_file2 = Synthesis::AssetPackage.find_by_source("stylesheets", "foo").current_file
112
+
113
+ assert_dom_equal build_css_expected_string(current_file1, current_file2, "not_part_of_a_package"),
114
+ stylesheet_link_merged("screen", "foo", "not_part_of_a_package")
115
+ end
116
+
117
+ def test_css_multiple_from_same_package
118
+ current_file1 = Synthesis::AssetPackage.find_by_source("stylesheets", "screen").current_file
119
+ current_file2 = Synthesis::AssetPackage.find_by_source("stylesheets", "foo").current_file
120
+ current_file3 = Synthesis::AssetPackage.find_by_source("stylesheets", "subdir/bar").current_file
121
+
122
+ assert_dom_equal build_css_expected_string(current_file1, "not_part_of_a_package", current_file2, current_file3),
123
+ stylesheet_link_merged("screen", "header", "not_part_of_a_package", "foo", "bar", "subdir/foo", "subdir/bar")
124
+ end
125
+
126
+ def test_css_by_package_name
127
+ package_name = Synthesis::AssetPackage.find_by_target("stylesheets", "base").current_file
128
+ assert_dom_equal build_css_expected_string(package_name),
129
+ stylesheet_link_merged(:base)
130
+ end
131
+
132
+ def test_css_multiple_package_names
133
+ package_name1 = Synthesis::AssetPackage.find_by_target("stylesheets", "base").current_file
134
+ package_name2 = Synthesis::AssetPackage.find_by_target("stylesheets", "secondary").current_file
135
+ package_name3 = Synthesis::AssetPackage.find_by_target("stylesheets", "subdir/styles").current_file
136
+ assert_dom_equal build_css_expected_string(package_name1, package_name2, package_name3),
137
+ stylesheet_link_merged(:base, :secondary, "subdir/styles")
138
+ end
139
+
140
+ end
@@ -0,0 +1,92 @@
1
+ require File.dirname(__FILE__) + '/../../../../config/environment'
2
+ require 'test/unit'
3
+ require 'mocha'
4
+
5
+ $asset_packages_yml = YAML.load_file("#{RAILS_ROOT}/vendor/plugins/asset_packager/test/asset_packages.yml")
6
+ $asset_base_path = "#{RAILS_ROOT}/vendor/plugins/asset_packager/test/assets"
7
+
8
+ class AssetPackagerTest < Test::Unit::TestCase
9
+ include Synthesis
10
+
11
+ def setup
12
+ Synthesis::AssetPackage.any_instance.stubs(:log)
13
+ Synthesis::AssetPackage.build_all
14
+ end
15
+
16
+ def teardown
17
+ Synthesis::AssetPackage.delete_all
18
+ end
19
+
20
+ def test_find_by_type
21
+ js_asset_packages = Synthesis::AssetPackage.find_by_type("javascripts")
22
+ assert_equal 2, js_asset_packages.length
23
+ assert_equal "base", js_asset_packages[0].target
24
+ assert_equal ["prototype", "effects", "controls", "dragdrop"], js_asset_packages[0].sources
25
+ end
26
+
27
+ def test_find_by_target
28
+ package = Synthesis::AssetPackage.find_by_target("javascripts", "base")
29
+ assert_equal "base", package.target
30
+ assert_equal ["prototype", "effects", "controls", "dragdrop"], package.sources
31
+ end
32
+
33
+ def test_find_by_source
34
+ package = Synthesis::AssetPackage.find_by_source("javascripts", "controls")
35
+ assert_equal "base", package.target
36
+ assert_equal ["prototype", "effects", "controls", "dragdrop"], package.sources
37
+ end
38
+
39
+ def test_delete_and_build
40
+ Synthesis::AssetPackage.delete_all
41
+ js_package_names = Dir.new("#{$asset_base_path}/javascripts").entries.delete_if { |x| ! (x =~ /\A\w+_packaged.js/) }
42
+ css_package_names = Dir.new("#{$asset_base_path}/stylesheets").entries.delete_if { |x| ! (x =~ /\A\w+_packaged.css/) }
43
+ css_subdir_package_names = Dir.new("#{$asset_base_path}/stylesheets/subdir").entries.delete_if { |x| ! (x =~ /\A\w+_packaged.css/) }
44
+
45
+ assert_equal 0, js_package_names.length
46
+ assert_equal 0, css_package_names.length
47
+ assert_equal 0, css_subdir_package_names.length
48
+
49
+ Synthesis::AssetPackage.build_all
50
+ js_package_names = Dir.new("#{$asset_base_path}/javascripts").entries.delete_if { |x| ! (x =~ /\A\w+_packaged.js/) }.sort
51
+ css_package_names = Dir.new("#{$asset_base_path}/stylesheets").entries.delete_if { |x| ! (x =~ /\A\w+_packaged.css/) }.sort
52
+ css_subdir_package_names = Dir.new("#{$asset_base_path}/stylesheets/subdir").entries.delete_if { |x| ! (x =~ /\A\w+_packaged.css/) }.sort
53
+
54
+ assert_equal 2, js_package_names.length
55
+ assert_equal 2, css_package_names.length
56
+ assert_equal 1, css_subdir_package_names.length
57
+ assert js_package_names[0].match(/\Abase_packaged.js\z/)
58
+ assert js_package_names[1].match(/\Asecondary_packaged.js\z/)
59
+ assert css_package_names[0].match(/\Abase_packaged.css\z/)
60
+ assert css_package_names[1].match(/\Asecondary_packaged.css\z/)
61
+ assert css_subdir_package_names[0].match(/\Astyles_packaged.css\z/)
62
+ end
63
+
64
+ def test_js_names_from_sources
65
+ package_names = Synthesis::AssetPackage.targets_from_sources("javascripts", ["prototype", "effects", "noexist1", "controls", "foo", "noexist2"])
66
+ assert_equal 4, package_names.length
67
+ assert package_names[0].match(/\Abase_packaged\z/)
68
+ assert_equal package_names[1], "noexist1"
69
+ assert package_names[2].match(/\Asecondary_packaged\z/)
70
+ assert_equal package_names[3], "noexist2"
71
+ end
72
+
73
+ def test_css_names_from_sources
74
+ package_names = Synthesis::AssetPackage.targets_from_sources("stylesheets", ["header", "screen", "noexist1", "foo", "noexist2"])
75
+ assert_equal 4, package_names.length
76
+ assert package_names[0].match(/\Abase_packaged\z/)
77
+ assert_equal package_names[1], "noexist1"
78
+ assert package_names[2].match(/\Asecondary_packaged\z/)
79
+ assert_equal package_names[3], "noexist2"
80
+ end
81
+
82
+ def test_should_return_merge_environments_when_set
83
+ Synthesis::AssetPackage.merge_environments = ["staging", "production"]
84
+ assert_equal ["staging", "production"], Synthesis::AssetPackage.merge_environments
85
+ end
86
+
87
+ def test_should_only_return_production_merge_environment_when_not_set
88
+ assert_equal ["production"], Synthesis::AssetPackage.merge_environments
89
+ end
90
+
91
+
92
+ end
@@ -0,0 +1,20 @@
1
+ javascripts:
2
+ - base:
3
+ - prototype
4
+ - effects
5
+ - controls
6
+ - dragdrop
7
+ - secondary:
8
+ - foo
9
+ - bar
10
+ - application
11
+ stylesheets:
12
+ - base:
13
+ - screen
14
+ - header
15
+ - secondary:
16
+ - foo
17
+ - bar
18
+ - subdir/styles:
19
+ - foo
20
+ - bar
@@ -0,0 +1,2 @@
1
+ // Place your application-specific JavaScript functions and classes here
2
+ // This file is automatically included by javascript_include_tag :defaults
@@ -0,0 +1,4 @@
1
+ bar bar bar
2
+ bar bar bar
3
+ bar bar bar
4
+
@@ -0,0 +1,815 @@
1
+ // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2
+ // (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3
+ // (c) 2005 Jon Tirsen (http://www.tirsen.com)
4
+ // Contributors:
5
+ // Richard Livsey
6
+ // Rahul Bhargava
7
+ // Rob Wills
8
+ //
9
+ // See scriptaculous.js for full license.
10
+
11
+ // Autocompleter.Base handles all the autocompletion functionality
12
+ // that's independent of the data source for autocompletion. This
13
+ // includes drawing the autocompletion menu, observing keyboard
14
+ // and mouse events, and similar.
15
+ //
16
+ // Specific autocompleters need to provide, at the very least,
17
+ // a getUpdatedChoices function that will be invoked every time
18
+ // the text inside the monitored textbox changes. This method
19
+ // should get the text for which to provide autocompletion by
20
+ // invoking this.getToken(), NOT by directly accessing
21
+ // this.element.value. This is to allow incremental tokenized
22
+ // autocompletion. Specific auto-completion logic (AJAX, etc)
23
+ // belongs in getUpdatedChoices.
24
+ //
25
+ // Tokenized incremental autocompletion is enabled automatically
26
+ // when an autocompleter is instantiated with the 'tokens' option
27
+ // in the options parameter, e.g.:
28
+ // new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
29
+ // will incrementally autocomplete with a comma as the token.
30
+ // Additionally, ',' in the above example can be replaced with
31
+ // a token array, e.g. { tokens: [',', '\n'] } which
32
+ // enables autocompletion on multiple tokens. This is most
33
+ // useful when one of the tokens is \n (a newline), as it
34
+ // allows smart autocompletion after linebreaks.
35
+
36
+ var Autocompleter = {}
37
+ Autocompleter.Base = function() {};
38
+ Autocompleter.Base.prototype = {
39
+ baseInitialize: function(element, update, options) {
40
+ this.element = $(element);
41
+ this.update = $(update);
42
+ this.hasFocus = false;
43
+ this.changed = false;
44
+ this.active = false;
45
+ this.index = 0;
46
+ this.entryCount = 0;
47
+
48
+ if (this.setOptions)
49
+ this.setOptions(options);
50
+ else
51
+ this.options = options || {};
52
+
53
+ this.options.paramName = this.options.paramName || this.element.name;
54
+ this.options.tokens = this.options.tokens || [];
55
+ this.options.frequency = this.options.frequency || 0.4;
56
+ this.options.minChars = this.options.minChars || 1;
57
+ this.options.onShow = this.options.onShow ||
58
+ function(element, update){
59
+ if(!update.style.position || update.style.position=='absolute') {
60
+ update.style.position = 'absolute';
61
+ Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
62
+ }
63
+ Effect.Appear(update,{duration:0.15});
64
+ };
65
+ this.options.onHide = this.options.onHide ||
66
+ function(element, update){ new Effect.Fade(update,{duration:0.15}) };
67
+
68
+ if (typeof(this.options.tokens) == 'string')
69
+ this.options.tokens = new Array(this.options.tokens);
70
+
71
+ this.observer = null;
72
+
73
+ this.element.setAttribute('autocomplete','off');
74
+
75
+ Element.hide(this.update);
76
+
77
+ Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
78
+ Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
79
+ },
80
+
81
+ show: function() {
82
+ if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
83
+ if(!this.iefix &&
84
+ (navigator.appVersion.indexOf('MSIE')>0) &&
85
+ (navigator.userAgent.indexOf('Opera')<0) &&
86
+ (Element.getStyle(this.update, 'position')=='absolute')) {
87
+ new Insertion.After(this.update,
88
+ '<iframe id="' + this.update.id + '_iefix" '+
89
+ 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
90
+ 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
91
+ this.iefix = $(this.update.id+'_iefix');
92
+ }
93
+ if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
94
+ },
95
+
96
+ fixIEOverlapping: function() {
97
+ Position.clone(this.update, this.iefix);
98
+ this.iefix.style.zIndex = 1;
99
+ this.update.style.zIndex = 2;
100
+ Element.show(this.iefix);
101
+ },
102
+
103
+ hide: function() {
104
+ this.stopIndicator();
105
+ if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
106
+ if(this.iefix) Element.hide(this.iefix);
107
+ },
108
+
109
+ startIndicator: function() {
110
+ if(this.options.indicator) Element.show(this.options.indicator);
111
+ },
112
+
113
+ stopIndicator: function() {
114
+ if(this.options.indicator) Element.hide(this.options.indicator);
115
+ },
116
+
117
+ onKeyPress: function(event) {
118
+ if(this.active)
119
+ switch(event.keyCode) {
120
+ case Event.KEY_TAB:
121
+ case Event.KEY_RETURN:
122
+ this.selectEntry();
123
+ Event.stop(event);
124
+ case Event.KEY_ESC:
125
+ this.hide();
126
+ this.active = false;
127
+ Event.stop(event);
128
+ return;
129
+ case Event.KEY_LEFT:
130
+ case Event.KEY_RIGHT:
131
+ return;
132
+ case Event.KEY_UP:
133
+ this.markPrevious();
134
+ this.render();
135
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
136
+ return;
137
+ case Event.KEY_DOWN:
138
+ this.markNext();
139
+ this.render();
140
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
141
+ return;
142
+ }
143
+ else
144
+ if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
145
+ (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
146
+
147
+ this.changed = true;
148
+ this.hasFocus = true;
149
+
150
+ if(this.observer) clearTimeout(this.observer);
151
+ this.observer =
152
+ setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
153
+ },
154
+
155
+ activate: function() {
156
+ this.changed = false;
157
+ this.hasFocus = true;
158
+ this.getUpdatedChoices();
159
+ },
160
+
161
+ onHover: function(event) {
162
+ var element = Event.findElement(event, 'LI');
163
+ if(this.index != element.autocompleteIndex)
164
+ {
165
+ this.index = element.autocompleteIndex;
166
+ this.render();
167
+ }
168
+ Event.stop(event);
169
+ },
170
+
171
+ onClick: function(event) {
172
+ var element = Event.findElement(event, 'LI');
173
+ this.index = element.autocompleteIndex;
174
+ this.selectEntry();
175
+ this.hide();
176
+ },
177
+
178
+ onBlur: function(event) {
179
+ // needed to make click events working
180
+ setTimeout(this.hide.bind(this), 250);
181
+ this.hasFocus = false;
182
+ this.active = false;
183
+ },
184
+
185
+ render: function() {
186
+ if(this.entryCount > 0) {
187
+ for (var i = 0; i < this.entryCount; i++)
188
+ this.index==i ?
189
+ Element.addClassName(this.getEntry(i),"selected") :
190
+ Element.removeClassName(this.getEntry(i),"selected");
191
+
192
+ if(this.hasFocus) {
193
+ this.show();
194
+ this.active = true;
195
+ }
196
+ } else {
197
+ this.active = false;
198
+ this.hide();
199
+ }
200
+ },
201
+
202
+ markPrevious: function() {
203
+ if(this.index > 0) this.index--
204
+ else this.index = this.entryCount-1;
205
+ },
206
+
207
+ markNext: function() {
208
+ if(this.index < this.entryCount-1) this.index++
209
+ else this.index = 0;
210
+ },
211
+
212
+ getEntry: function(index) {
213
+ return this.update.firstChild.childNodes[index];
214
+ },
215
+
216
+ getCurrentEntry: function() {
217
+ return this.getEntry(this.index);
218
+ },
219
+
220
+ selectEntry: function() {
221
+ this.active = false;
222
+ this.updateElement(this.getCurrentEntry());
223
+ },
224
+
225
+ updateElement: function(selectedElement) {
226
+ if (this.options.updateElement) {
227
+ this.options.updateElement(selectedElement);
228
+ return;
229
+ }
230
+ var value = '';
231
+ if (this.options.select) {
232
+ var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
233
+ if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
234
+ } else
235
+ value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
236
+
237
+ var lastTokenPos = this.findLastToken();
238
+ if (lastTokenPos != -1) {
239
+ var newValue = this.element.value.substr(0, lastTokenPos + 1);
240
+ var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
241
+ if (whitespace)
242
+ newValue += whitespace[0];
243
+ this.element.value = newValue + value;
244
+ } else {
245
+ this.element.value = value;
246
+ }
247
+ this.element.focus();
248
+
249
+ if (this.options.afterUpdateElement)
250
+ this.options.afterUpdateElement(this.element, selectedElement);
251
+ },
252
+
253
+ updateChoices: function(choices) {
254
+ if(!this.changed && this.hasFocus) {
255
+ this.update.innerHTML = choices;
256
+ Element.cleanWhitespace(this.update);
257
+ Element.cleanWhitespace(this.update.firstChild);
258
+
259
+ if(this.update.firstChild && this.update.firstChild.childNodes) {
260
+ this.entryCount =
261
+ this.update.firstChild.childNodes.length;
262
+ for (var i = 0; i < this.entryCount; i++) {
263
+ var entry = this.getEntry(i);
264
+ entry.autocompleteIndex = i;
265
+ this.addObservers(entry);
266
+ }
267
+ } else {
268
+ this.entryCount = 0;
269
+ }
270
+
271
+ this.stopIndicator();
272
+
273
+ this.index = 0;
274
+ this.render();
275
+ }
276
+ },
277
+
278
+ addObservers: function(element) {
279
+ Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
280
+ Event.observe(element, "click", this.onClick.bindAsEventListener(this));
281
+ },
282
+
283
+ onObserverEvent: function() {
284
+ this.changed = false;
285
+ if(this.getToken().length>=this.options.minChars) {
286
+ this.startIndicator();
287
+ this.getUpdatedChoices();
288
+ } else {
289
+ this.active = false;
290
+ this.hide();
291
+ }
292
+ },
293
+
294
+ getToken: function() {
295
+ var tokenPos = this.findLastToken();
296
+ if (tokenPos != -1)
297
+ var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
298
+ else
299
+ var ret = this.element.value;
300
+
301
+ return /\n/.test(ret) ? '' : ret;
302
+ },
303
+
304
+ findLastToken: function() {
305
+ var lastTokenPos = -1;
306
+
307
+ for (var i=0; i<this.options.tokens.length; i++) {
308
+ var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
309
+ if (thisTokenPos > lastTokenPos)
310
+ lastTokenPos = thisTokenPos;
311
+ }
312
+ return lastTokenPos;
313
+ }
314
+ }
315
+
316
+ Ajax.Autocompleter = Class.create();
317
+ Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
318
+ initialize: function(element, update, url, options) {
319
+ this.baseInitialize(element, update, options);
320
+ this.options.asynchronous = true;
321
+ this.options.onComplete = this.onComplete.bind(this);
322
+ this.options.defaultParams = this.options.parameters || null;
323
+ this.url = url;
324
+ },
325
+
326
+ getUpdatedChoices: function() {
327
+ entry = encodeURIComponent(this.options.paramName) + '=' +
328
+ encodeURIComponent(this.getToken());
329
+
330
+ this.options.parameters = this.options.callback ?
331
+ this.options.callback(this.element, entry) : entry;
332
+
333
+ if(this.options.defaultParams)
334
+ this.options.parameters += '&' + this.options.defaultParams;
335
+
336
+ new Ajax.Request(this.url, this.options);
337
+ },
338
+
339
+ onComplete: function(request) {
340
+ this.updateChoices(request.responseText);
341
+ }
342
+
343
+ });
344
+
345
+ // The local array autocompleter. Used when you'd prefer to
346
+ // inject an array of autocompletion options into the page, rather
347
+ // than sending out Ajax queries, which can be quite slow sometimes.
348
+ //
349
+ // The constructor takes four parameters. The first two are, as usual,
350
+ // the id of the monitored textbox, and id of the autocompletion menu.
351
+ // The third is the array you want to autocomplete from, and the fourth
352
+ // is the options block.
353
+ //
354
+ // Extra local autocompletion options:
355
+ // - choices - How many autocompletion choices to offer
356
+ //
357
+ // - partialSearch - If false, the autocompleter will match entered
358
+ // text only at the beginning of strings in the
359
+ // autocomplete array. Defaults to true, which will
360
+ // match text at the beginning of any *word* in the
361
+ // strings in the autocomplete array. If you want to
362
+ // search anywhere in the string, additionally set
363
+ // the option fullSearch to true (default: off).
364
+ //
365
+ // - fullSsearch - Search anywhere in autocomplete array strings.
366
+ //
367
+ // - partialChars - How many characters to enter before triggering
368
+ // a partial match (unlike minChars, which defines
369
+ // how many characters are required to do any match
370
+ // at all). Defaults to 2.
371
+ //
372
+ // - ignoreCase - Whether to ignore case when autocompleting.
373
+ // Defaults to true.
374
+ //
375
+ // It's possible to pass in a custom function as the 'selector'
376
+ // option, if you prefer to write your own autocompletion logic.
377
+ // In that case, the other options above will not apply unless
378
+ // you support them.
379
+
380
+ Autocompleter.Local = Class.create();
381
+ Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
382
+ initialize: function(element, update, array, options) {
383
+ this.baseInitialize(element, update, options);
384
+ this.options.array = array;
385
+ },
386
+
387
+ getUpdatedChoices: function() {
388
+ this.updateChoices(this.options.selector(this));
389
+ },
390
+
391
+ setOptions: function(options) {
392
+ this.options = Object.extend({
393
+ choices: 10,
394
+ partialSearch: true,
395
+ partialChars: 2,
396
+ ignoreCase: true,
397
+ fullSearch: false,
398
+ selector: function(instance) {
399
+ var ret = []; // Beginning matches
400
+ var partial = []; // Inside matches
401
+ var entry = instance.getToken();
402
+ var count = 0;
403
+
404
+ for (var i = 0; i < instance.options.array.length &&
405
+ ret.length < instance.options.choices ; i++) {
406
+
407
+ var elem = instance.options.array[i];
408
+ var foundPos = instance.options.ignoreCase ?
409
+ elem.toLowerCase().indexOf(entry.toLowerCase()) :
410
+ elem.indexOf(entry);
411
+
412
+ while (foundPos != -1) {
413
+ if (foundPos == 0 && elem.length != entry.length) {
414
+ ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
415
+ elem.substr(entry.length) + "</li>");
416
+ break;
417
+ } else if (entry.length >= instance.options.partialChars &&
418
+ instance.options.partialSearch && foundPos != -1) {
419
+ if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
420
+ partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
421
+ elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
422
+ foundPos + entry.length) + "</li>");
423
+ break;
424
+ }
425
+ }
426
+
427
+ foundPos = instance.options.ignoreCase ?
428
+ elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
429
+ elem.indexOf(entry, foundPos + 1);
430
+
431
+ }
432
+ }
433
+ if (partial.length)
434
+ ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
435
+ return "<ul>" + ret.join('') + "</ul>";
436
+ }
437
+ }, options || {});
438
+ }
439
+ });
440
+
441
+ // AJAX in-place editor
442
+ //
443
+ // see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
444
+
445
+ // Use this if you notice weird scrolling problems on some browsers,
446
+ // the DOM might be a bit confused when this gets called so do this
447
+ // waits 1 ms (with setTimeout) until it does the activation
448
+ Field.scrollFreeActivate = function(field) {
449
+ setTimeout(function() {
450
+ Field.activate(field);
451
+ }, 1);
452
+ }
453
+
454
+ Ajax.InPlaceEditor = Class.create();
455
+ Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
456
+ Ajax.InPlaceEditor.prototype = {
457
+ initialize: function(element, url, options) {
458
+ this.url = url;
459
+ this.element = $(element);
460
+
461
+ this.options = Object.extend({
462
+ okButton: true,
463
+ okText: "ok",
464
+ cancelLink: true,
465
+ cancelText: "cancel",
466
+ savingText: "Saving...",
467
+ clickToEditText: "Click to edit",
468
+ okText: "ok",
469
+ rows: 1,
470
+ onComplete: function(transport, element) {
471
+ new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
472
+ },
473
+ onFailure: function(transport) {
474
+ alert("Error communicating with the server: " + transport.responseText.stripTags());
475
+ },
476
+ callback: function(form) {
477
+ return Form.serialize(form);
478
+ },
479
+ handleLineBreaks: true,
480
+ loadingText: 'Loading...',
481
+ savingClassName: 'inplaceeditor-saving',
482
+ loadingClassName: 'inplaceeditor-loading',
483
+ formClassName: 'inplaceeditor-form',
484
+ highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
485
+ highlightendcolor: "#FFFFFF",
486
+ externalControl: null,
487
+ submitOnBlur: false,
488
+ ajaxOptions: {},
489
+ evalScripts: false
490
+ }, options || {});
491
+
492
+ if(!this.options.formId && this.element.id) {
493
+ this.options.formId = this.element.id + "-inplaceeditor";
494
+ if ($(this.options.formId)) {
495
+ // there's already a form with that name, don't specify an id
496
+ this.options.formId = null;
497
+ }
498
+ }
499
+
500
+ if (this.options.externalControl) {
501
+ this.options.externalControl = $(this.options.externalControl);
502
+ }
503
+
504
+ this.originalBackground = Element.getStyle(this.element, 'background-color');
505
+ if (!this.originalBackground) {
506
+ this.originalBackground = "transparent";
507
+ }
508
+
509
+ this.element.title = this.options.clickToEditText;
510
+
511
+ this.onclickListener = this.enterEditMode.bindAsEventListener(this);
512
+ this.mouseoverListener = this.enterHover.bindAsEventListener(this);
513
+ this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
514
+ Event.observe(this.element, 'click', this.onclickListener);
515
+ Event.observe(this.element, 'mouseover', this.mouseoverListener);
516
+ Event.observe(this.element, 'mouseout', this.mouseoutListener);
517
+ if (this.options.externalControl) {
518
+ Event.observe(this.options.externalControl, 'click', this.onclickListener);
519
+ Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
520
+ Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
521
+ }
522
+ },
523
+ enterEditMode: function(evt) {
524
+ if (this.saving) return;
525
+ if (this.editing) return;
526
+ this.editing = true;
527
+ this.onEnterEditMode();
528
+ if (this.options.externalControl) {
529
+ Element.hide(this.options.externalControl);
530
+ }
531
+ Element.hide(this.element);
532
+ this.createForm();
533
+ this.element.parentNode.insertBefore(this.form, this.element);
534
+ Field.scrollFreeActivate(this.editField);
535
+ // stop the event to avoid a page refresh in Safari
536
+ if (evt) {
537
+ Event.stop(evt);
538
+ }
539
+ return false;
540
+ },
541
+ createForm: function() {
542
+ this.form = document.createElement("form");
543
+ this.form.id = this.options.formId;
544
+ Element.addClassName(this.form, this.options.formClassName)
545
+ this.form.onsubmit = this.onSubmit.bind(this);
546
+
547
+ this.createEditField();
548
+
549
+ if (this.options.textarea) {
550
+ var br = document.createElement("br");
551
+ this.form.appendChild(br);
552
+ }
553
+
554
+ if (this.options.okButton) {
555
+ okButton = document.createElement("input");
556
+ okButton.type = "submit";
557
+ okButton.value = this.options.okText;
558
+ okButton.className = 'editor_ok_button';
559
+ this.form.appendChild(okButton);
560
+ }
561
+
562
+ if (this.options.cancelLink) {
563
+ cancelLink = document.createElement("a");
564
+ cancelLink.href = "#";
565
+ cancelLink.appendChild(document.createTextNode(this.options.cancelText));
566
+ cancelLink.onclick = this.onclickCancel.bind(this);
567
+ cancelLink.className = 'editor_cancel';
568
+ this.form.appendChild(cancelLink);
569
+ }
570
+ },
571
+ hasHTMLLineBreaks: function(string) {
572
+ if (!this.options.handleLineBreaks) return false;
573
+ return string.match(/<br/i) || string.match(/<p>/i);
574
+ },
575
+ convertHTMLLineBreaks: function(string) {
576
+ return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
577
+ },
578
+ createEditField: function() {
579
+ var text;
580
+ if(this.options.loadTextURL) {
581
+ text = this.options.loadingText;
582
+ } else {
583
+ text = this.getText();
584
+ }
585
+
586
+ var obj = this;
587
+
588
+ if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
589
+ this.options.textarea = false;
590
+ var textField = document.createElement("input");
591
+ textField.obj = this;
592
+ textField.type = "text";
593
+ textField.name = "value";
594
+ textField.value = text;
595
+ textField.style.backgroundColor = this.options.highlightcolor;
596
+ textField.className = 'editor_field';
597
+ var size = this.options.size || this.options.cols || 0;
598
+ if (size != 0) textField.size = size;
599
+ if (this.options.submitOnBlur)
600
+ textField.onblur = this.onSubmit.bind(this);
601
+ this.editField = textField;
602
+ } else {
603
+ this.options.textarea = true;
604
+ var textArea = document.createElement("textarea");
605
+ textArea.obj = this;
606
+ textArea.name = "value";
607
+ textArea.value = this.convertHTMLLineBreaks(text);
608
+ textArea.rows = this.options.rows;
609
+ textArea.cols = this.options.cols || 40;
610
+ textArea.className = 'editor_field';
611
+ if (this.options.submitOnBlur)
612
+ textArea.onblur = this.onSubmit.bind(this);
613
+ this.editField = textArea;
614
+ }
615
+
616
+ if(this.options.loadTextURL) {
617
+ this.loadExternalText();
618
+ }
619
+ this.form.appendChild(this.editField);
620
+ },
621
+ getText: function() {
622
+ return this.element.innerHTML;
623
+ },
624
+ loadExternalText: function() {
625
+ Element.addClassName(this.form, this.options.loadingClassName);
626
+ this.editField.disabled = true;
627
+ new Ajax.Request(
628
+ this.options.loadTextURL,
629
+ Object.extend({
630
+ asynchronous: true,
631
+ onComplete: this.onLoadedExternalText.bind(this)
632
+ }, this.options.ajaxOptions)
633
+ );
634
+ },
635
+ onLoadedExternalText: function(transport) {
636
+ Element.removeClassName(this.form, this.options.loadingClassName);
637
+ this.editField.disabled = false;
638
+ this.editField.value = transport.responseText.stripTags();
639
+ },
640
+ onclickCancel: function() {
641
+ this.onComplete();
642
+ this.leaveEditMode();
643
+ return false;
644
+ },
645
+ onFailure: function(transport) {
646
+ this.options.onFailure(transport);
647
+ if (this.oldInnerHTML) {
648
+ this.element.innerHTML = this.oldInnerHTML;
649
+ this.oldInnerHTML = null;
650
+ }
651
+ return false;
652
+ },
653
+ onSubmit: function() {
654
+ // onLoading resets these so we need to save them away for the Ajax call
655
+ var form = this.form;
656
+ var value = this.editField.value;
657
+
658
+ // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
659
+ // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
660
+ // to be displayed indefinitely
661
+ this.onLoading();
662
+
663
+ if (this.options.evalScripts) {
664
+ new Ajax.Request(
665
+ this.url, Object.extend({
666
+ parameters: this.options.callback(form, value),
667
+ onComplete: this.onComplete.bind(this),
668
+ onFailure: this.onFailure.bind(this),
669
+ asynchronous:true,
670
+ evalScripts:true
671
+ }, this.options.ajaxOptions));
672
+ } else {
673
+ new Ajax.Updater(
674
+ { success: this.element,
675
+ // don't update on failure (this could be an option)
676
+ failure: null },
677
+ this.url, Object.extend({
678
+ parameters: this.options.callback(form, value),
679
+ onComplete: this.onComplete.bind(this),
680
+ onFailure: this.onFailure.bind(this)
681
+ }, this.options.ajaxOptions));
682
+ }
683
+ // stop the event to avoid a page refresh in Safari
684
+ if (arguments.length > 1) {
685
+ Event.stop(arguments[0]);
686
+ }
687
+ return false;
688
+ },
689
+ onLoading: function() {
690
+ this.saving = true;
691
+ this.removeForm();
692
+ this.leaveHover();
693
+ this.showSaving();
694
+ },
695
+ showSaving: function() {
696
+ this.oldInnerHTML = this.element.innerHTML;
697
+ this.element.innerHTML = this.options.savingText;
698
+ Element.addClassName(this.element, this.options.savingClassName);
699
+ this.element.style.backgroundColor = this.originalBackground;
700
+ Element.show(this.element);
701
+ },
702
+ removeForm: function() {
703
+ if(this.form) {
704
+ if (this.form.parentNode) Element.remove(this.form);
705
+ this.form = null;
706
+ }
707
+ },
708
+ enterHover: function() {
709
+ if (this.saving) return;
710
+ this.element.style.backgroundColor = this.options.highlightcolor;
711
+ if (this.effect) {
712
+ this.effect.cancel();
713
+ }
714
+ Element.addClassName(this.element, this.options.hoverClassName)
715
+ },
716
+ leaveHover: function() {
717
+ if (this.options.backgroundColor) {
718
+ this.element.style.backgroundColor = this.oldBackground;
719
+ }
720
+ Element.removeClassName(this.element, this.options.hoverClassName)
721
+ if (this.saving) return;
722
+ this.effect = new Effect.Highlight(this.element, {
723
+ startcolor: this.options.highlightcolor,
724
+ endcolor: this.options.highlightendcolor,
725
+ restorecolor: this.originalBackground
726
+ });
727
+ },
728
+ leaveEditMode: function() {
729
+ Element.removeClassName(this.element, this.options.savingClassName);
730
+ this.removeForm();
731
+ this.leaveHover();
732
+ this.element.style.backgroundColor = this.originalBackground;
733
+ Element.show(this.element);
734
+ if (this.options.externalControl) {
735
+ Element.show(this.options.externalControl);
736
+ }
737
+ this.editing = false;
738
+ this.saving = false;
739
+ this.oldInnerHTML = null;
740
+ this.onLeaveEditMode();
741
+ },
742
+ onComplete: function(transport) {
743
+ this.leaveEditMode();
744
+ this.options.onComplete.bind(this)(transport, this.element);
745
+ },
746
+ onEnterEditMode: function() {},
747
+ onLeaveEditMode: function() {},
748
+ dispose: function() {
749
+ if (this.oldInnerHTML) {
750
+ this.element.innerHTML = this.oldInnerHTML;
751
+ }
752
+ this.leaveEditMode();
753
+ Event.stopObserving(this.element, 'click', this.onclickListener);
754
+ Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
755
+ Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
756
+ if (this.options.externalControl) {
757
+ Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
758
+ Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
759
+ Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
760
+ }
761
+ }
762
+ };
763
+
764
+ Ajax.InPlaceCollectionEditor = Class.create();
765
+ Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
766
+ Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
767
+ createEditField: function() {
768
+ if (!this.cached_selectTag) {
769
+ var selectTag = document.createElement("select");
770
+ var collection = this.options.collection || [];
771
+ var optionTag;
772
+ collection.each(function(e,i) {
773
+ optionTag = document.createElement("option");
774
+ optionTag.value = (e instanceof Array) ? e[0] : e;
775
+ if(this.options.value==optionTag.value) optionTag.selected = true;
776
+ optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
777
+ selectTag.appendChild(optionTag);
778
+ }.bind(this));
779
+ this.cached_selectTag = selectTag;
780
+ }
781
+
782
+ this.editField = this.cached_selectTag;
783
+ if(this.options.loadTextURL) this.loadExternalText();
784
+ this.form.appendChild(this.editField);
785
+ this.options.callback = function(form, value) {
786
+ return "value=" + encodeURIComponent(value);
787
+ }
788
+ }
789
+ });
790
+
791
+ // Delayed observer, like Form.Element.Observer,
792
+ // but waits for delay after last key input
793
+ // Ideal for live-search fields
794
+
795
+ Form.Element.DelayedObserver = Class.create();
796
+ Form.Element.DelayedObserver.prototype = {
797
+ initialize: function(element, delay, callback) {
798
+ this.delay = delay || 0.5;
799
+ this.element = $(element);
800
+ this.callback = callback;
801
+ this.timer = null;
802
+ this.lastValue = $F(this.element);
803
+ Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
804
+ },
805
+ delayedListener: function(event) {
806
+ if(this.lastValue == $F(this.element)) return;
807
+ if(this.timer) clearTimeout(this.timer);
808
+ this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
809
+ this.lastValue = $F(this.element);
810
+ },
811
+ onTimerEvent: function() {
812
+ this.timer = null;
813
+ this.callback(this.element, $F(this.element));
814
+ }
815
+ };