parlement 0.14 → 0.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (205) hide show
  1. data/CHANGES +41 -1
  2. data/MEMORY +66 -5
  3. data/README +10 -5
  4. data/Rakefile +15 -23
  5. data/app/controllers/account_controller.rb +48 -43
  6. data/app/controllers/{application.rb → application_controller.rb} +15 -12
  7. data/app/controllers/elt_controller.rb +77 -32
  8. data/app/controllers/subscriber_controller.rb +11 -10
  9. data/app/helpers/application_helper.rb +14 -1
  10. data/app/helpers/elt_helper.rb +9 -7
  11. data/app/models/elt.rb +25 -24
  12. data/app/models/mail.rb +44 -47
  13. data/app/models/person_notify.rb +2 -2
  14. data/app/models/user.rb +128 -2
  15. data/app/models/user_notify.rb +15 -15
  16. data/app/views/account/_login.rhtml +39 -39
  17. data/app/views/account/_show.rhtml +22 -30
  18. data/app/views/account/signup.rhtml +2 -2
  19. data/app/views/elt/_choice.rhtml +6 -6
  20. data/app/views/elt/_elt.rhtml +27 -32
  21. data/app/views/elt/choices.rhtml +16 -18
  22. data/app/views/elt/list/_byDate.rhtml +14 -14
  23. data/app/views/elt/list/_byVote.rhtml +15 -15
  24. data/app/views/elt/list/_children.rhtml +48 -40
  25. data/app/views/elt/list/_subscribers.rhtml +1 -1
  26. data/app/views/elt/new.rhtml +22 -21
  27. data/app/views/elt/rss.rxml +4 -11
  28. data/app/views/elt/show.rhtml +65 -61
  29. data/app/views/elt/vote_rss.rxml +4 -11
  30. data/app/views/layouts/top.rhtml +39 -50
  31. data/app/views/person/_listElts.rhtml +1 -1
  32. data/app/views/person/show.rhtml +1 -1
  33. data/{vendor/plugins/login_engine/app → app}/views/user_notify/change_password.rhtml +0 -0
  34. data/{vendor/plugins/login_engine/app → app}/views/user_notify/delete.rhtml +0 -0
  35. data/{vendor/plugins/login_engine/app → app}/views/user_notify/forgot_password.rhtml +0 -0
  36. data/{vendor/plugins/login_engine/app → app}/views/user_notify/pending_delete.rhtml +0 -0
  37. data/{vendor/plugins/login_engine/app → app}/views/user_notify/signup.rhtml +0 -0
  38. data/config/boot.rb +97 -32
  39. data/config/environment.rb +37 -35
  40. data/config/environments/development.rb +2 -3
  41. data/config/environments/production.rb +3 -0
  42. data/config/initializers/string_ruby_1.8.rb +10 -0
  43. data/config/routes.rb +17 -22
  44. data/db/schema.rb +102 -74
  45. data/lib/tasks/rspec.rake +167 -0
  46. data/public/404.html +25 -7
  47. data/public/500.html +26 -7
  48. data/public/dispatch.cgi +0 -0
  49. data/public/dispatch.fcgi +0 -0
  50. data/public/dispatch.rb +0 -0
  51. data/public/images/live_tree_branch_collapsed_icon.gif +0 -0
  52. data/public/images/live_tree_branch_expanded_icon.gif +0 -0
  53. data/public/images/live_tree_leaf_icon.gif +0 -0
  54. data/public/javascripts/application.js +258 -0
  55. data/public/javascripts/controls.js +544 -414
  56. data/public/javascripts/dragdrop.js +229 -198
  57. data/public/javascripts/effects.js +499 -459
  58. data/public/javascripts/prototype.js +2926 -1121
  59. data/public/javascripts/shadedborder.js +68 -50
  60. data/public/stylesheets/default.css +34 -34
  61. data/public/stylesheets/live_tree.css +0 -0
  62. data/public/stylesheets/scaffold.css +6 -6
  63. data/script/about +0 -0
  64. data/script/autospec +6 -0
  65. data/script/benchmarker +0 -0
  66. data/script/breakpointer +0 -0
  67. data/script/console +0 -0
  68. data/script/dbconsole +3 -0
  69. data/script/destroy +0 -0
  70. data/script/generate +0 -0
  71. data/script/plugin +0 -0
  72. data/script/profiler +0 -0
  73. data/script/runner +0 -0
  74. data/script/server +0 -0
  75. data/script/spec +10 -0
  76. data/script/spec_server +9 -0
  77. data/test/unit/attachment_test.rb +4 -4
  78. data/test/unit/choice_test.rb +1 -1
  79. data/test/unit/elt_test.rb +9 -9
  80. data/test/unit/mail_notify_test.rb +2 -2
  81. data/test/unit/mail_test.rb +18 -11
  82. data/test/unit/person_notify_test.rb +1 -1
  83. data/test/unit/person_test.rb +1 -1
  84. data/test/unit/subscriber_test.rb +1 -1
  85. data/test/unit/user_test.rb +81 -0
  86. data/test/unit/visit_test.rb +6 -6
  87. data/vendor/plugins/activerecord_foreign_key_extensions/init.rb +2 -0
  88. data/vendor/plugins/activerecord_foreign_key_extensions/lib/active_record_extensions.rb +182 -0
  89. data/vendor/plugins/activerecord_text_id_extensions/init.rb +2 -0
  90. data/vendor/plugins/activerecord_text_id_extensions/lib/active_record_extensions.rb +24 -0
  91. data/vendor/plugins/acts_as_nested_set/README +15 -0
  92. data/vendor/plugins/acts_as_nested_set/init.rb +1 -0
  93. data/vendor/plugins/acts_as_nested_set/lib/active_record/acts/nested_set.rb +210 -0
  94. data/vendor/plugins/acts_as_nested_set/test/nested_set_test.rb +269 -0
  95. data/vendor/plugins/acts_as_tree/README +26 -0
  96. data/vendor/plugins/acts_as_tree/Rakefile +22 -0
  97. data/vendor/plugins/acts_as_tree/init.rb +1 -0
  98. data/vendor/plugins/acts_as_tree/lib/active_record/acts/tree.rb +96 -0
  99. data/vendor/plugins/{output_compression/CHANGELOG → acts_as_tree/test/abstract_unit.rb} +0 -0
  100. data/vendor/plugins/acts_as_tree/test/acts_as_tree_test.rb +219 -0
  101. data/vendor/plugins/acts_as_tree/test/database.yml +0 -0
  102. data/vendor/plugins/acts_as_tree/test/fixtures/mixin.rb +0 -0
  103. data/vendor/plugins/acts_as_tree/test/fixtures/mixins.yml +0 -0
  104. data/vendor/plugins/acts_as_tree/test/schema.rb +0 -0
  105. data/vendor/plugins/classic_pagination/CHANGELOG +152 -0
  106. data/vendor/plugins/classic_pagination/README +18 -0
  107. data/vendor/plugins/{output_compression/rakefile → classic_pagination/Rakefile} +22 -22
  108. data/vendor/plugins/classic_pagination/init.rb +33 -0
  109. data/vendor/plugins/classic_pagination/install.rb +1 -0
  110. data/vendor/plugins/classic_pagination/lib/pagination.rb +405 -0
  111. data/vendor/plugins/classic_pagination/lib/pagination_helper.rb +135 -0
  112. data/vendor/plugins/classic_pagination/test/fixtures/companies.yml +24 -0
  113. data/vendor/plugins/classic_pagination/test/fixtures/company.rb +9 -0
  114. data/vendor/plugins/classic_pagination/test/fixtures/developer.rb +7 -0
  115. data/vendor/plugins/classic_pagination/test/fixtures/developers.yml +21 -0
  116. data/vendor/plugins/classic_pagination/test/fixtures/developers_projects.yml +13 -0
  117. data/vendor/plugins/classic_pagination/test/fixtures/project.rb +3 -0
  118. data/vendor/plugins/classic_pagination/test/fixtures/projects.yml +7 -0
  119. data/vendor/plugins/classic_pagination/test/fixtures/replies.yml +13 -0
  120. data/vendor/plugins/classic_pagination/test/fixtures/reply.rb +5 -0
  121. data/vendor/plugins/classic_pagination/test/fixtures/schema.sql +42 -0
  122. data/vendor/plugins/classic_pagination/test/fixtures/topic.rb +3 -0
  123. data/vendor/plugins/classic_pagination/test/fixtures/topics.yml +22 -0
  124. data/vendor/plugins/classic_pagination/test/helper.rb +117 -0
  125. data/vendor/plugins/classic_pagination/test/pagination_helper_test.rb +38 -0
  126. data/vendor/plugins/classic_pagination/test/pagination_test.rb +177 -0
  127. data/vendor/plugins/file_column/lib/file_column.rb +1 -1
  128. data/vendor/plugins/file_column/test/file_column_test.rb +0 -0
  129. metadata +151 -197
  130. data/app/helpers/live_tree.rb +0 -238
  131. data/app/views/elt/_form.rhtml +0 -31
  132. data/app/views/elt/show_tree.rhtml +0 -8
  133. data/config/environments/user_environment.rb +0 -1
  134. data/db/ROOT/Titemagli.txt +0 -3
  135. data/db/ROOT/titemagli.txt +0 -9
  136. data/public/javascripts/behaviour.js +0 -254
  137. data/public/javascripts/ie7-load.htc +0 -1
  138. data/public/javascripts/ie7.js +0 -6
  139. data/public/javascripts/live_tree.js +0 -749
  140. data/public/javascripts/mybehaviour.js +0 -225
  141. data/public/javascripts/scriptaculous.js +0 -47
  142. data/public/javascripts/slider.js +0 -283
  143. data/public/stylesheets/blue.css +0 -471
  144. data/vendor/plugins/engines/CHANGELOG +0 -241
  145. data/vendor/plugins/engines/MIT-LICENSE +0 -21
  146. data/vendor/plugins/engines/README +0 -64
  147. data/vendor/plugins/engines/Rakefile +0 -32
  148. data/vendor/plugins/engines/UPGRADING +0 -93
  149. data/vendor/plugins/engines/about.yml +0 -7
  150. data/vendor/plugins/engines/generators/plugin_migration/USAGE +0 -45
  151. data/vendor/plugins/engines/generators/plugin_migration/plugin_migration_generator.rb +0 -79
  152. data/vendor/plugins/engines/generators/plugin_migration/templates/plugin_migration.erb +0 -13
  153. data/vendor/plugins/engines/init.rb +0 -40
  154. data/vendor/plugins/engines/install.rb +0 -32
  155. data/vendor/plugins/engines/lib/engines.rb +0 -323
  156. data/vendor/plugins/engines/lib/engines/deprecated_config_support.rb +0 -135
  157. data/vendor/plugins/engines/lib/engines/plugin.rb +0 -214
  158. data/vendor/plugins/engines/lib/engines/plugin_list.rb +0 -31
  159. data/vendor/plugins/engines/lib/engines/plugin_migrator.rb +0 -60
  160. data/vendor/plugins/engines/lib/engines/rails_extensions.rb +0 -6
  161. data/vendor/plugins/engines/lib/engines/rails_extensions/active_record.rb +0 -19
  162. data/vendor/plugins/engines/lib/engines/rails_extensions/dependencies.rb +0 -143
  163. data/vendor/plugins/engines/lib/engines/rails_extensions/migrations.rb +0 -155
  164. data/vendor/plugins/engines/lib/engines/rails_extensions/public_asset_helpers.rb +0 -116
  165. data/vendor/plugins/engines/lib/engines/rails_extensions/rails.rb +0 -20
  166. data/vendor/plugins/engines/lib/engines/rails_extensions/rails_initializer.rb +0 -86
  167. data/vendor/plugins/engines/lib/engines/rails_extensions/routing.rb +0 -77
  168. data/vendor/plugins/engines/lib/engines/rails_extensions/templates.rb +0 -140
  169. data/vendor/plugins/engines/lib/engines/testing.rb +0 -87
  170. data/vendor/plugins/engines/tasks/engines.rake +0 -149
  171. data/vendor/plugins/login_engine/CHANGELOG +0 -22
  172. data/vendor/plugins/login_engine/README +0 -344
  173. data/vendor/plugins/login_engine/app/controllers/user_controller.rb +0 -262
  174. data/vendor/plugins/login_engine/app/helpers/user_helper.rb +0 -88
  175. data/vendor/plugins/login_engine/app/models/user.rb +0 -7
  176. data/vendor/plugins/login_engine/app/models/user_notify.rb +0 -75
  177. data/vendor/plugins/login_engine/app/views/user/_edit.rhtml +0 -11
  178. data/vendor/plugins/login_engine/app/views/user/_password.rhtml +0 -9
  179. data/vendor/plugins/login_engine/app/views/user/change_password.rhtml +0 -17
  180. data/vendor/plugins/login_engine/app/views/user/edit.rhtml +0 -23
  181. data/vendor/plugins/login_engine/app/views/user/forgot_password.rhtml +0 -18
  182. data/vendor/plugins/login_engine/app/views/user/home.rhtml +0 -7
  183. data/vendor/plugins/login_engine/app/views/user/login.rhtml +0 -17
  184. data/vendor/plugins/login_engine/app/views/user/logout.rhtml +0 -8
  185. data/vendor/plugins/login_engine/app/views/user/signup.rhtml +0 -17
  186. data/vendor/plugins/login_engine/db/migrate/001_initial_schema.rb +0 -25
  187. data/vendor/plugins/login_engine/init_engine.rb +0 -11
  188. data/vendor/plugins/login_engine/install.rb +0 -4
  189. data/vendor/plugins/login_engine/lib/login_engine.rb +0 -62
  190. data/vendor/plugins/login_engine/lib/login_engine/authenticated_system.rb +0 -113
  191. data/vendor/plugins/login_engine/lib/login_engine/authenticated_user.rb +0 -155
  192. data/vendor/plugins/login_engine/public/stylesheets/login_engine.css +0 -81
  193. data/vendor/plugins/login_engine/test/fixtures/users.yml +0 -41
  194. data/vendor/plugins/login_engine/test/functional/user_controller_test.rb +0 -536
  195. data/vendor/plugins/login_engine/test/mocks/mail.rb +0 -14
  196. data/vendor/plugins/login_engine/test/mocks/time.rb +0 -19
  197. data/vendor/plugins/login_engine/test/test_helper.rb +0 -11
  198. data/vendor/plugins/login_engine/test/unit/user_test.rb +0 -114
  199. data/vendor/plugins/output_compression/MIT-LICENSE +0 -20
  200. data/vendor/plugins/output_compression/README +0 -4
  201. data/vendor/plugins/output_compression/init.rb +0 -1
  202. data/vendor/plugins/output_compression/lib/output_compression.rb +0 -84
  203. data/vendor/plugins/output_compression/test/output_test.rb +0 -11
  204. data/vendor/plugins/output_compression/test/test_controller.rb +0 -3
  205. data/vendor/plugins/output_compression/test/test_helper.rb +0 -14
@@ -1,8 +1,26 @@
1
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
2
- "http://www.w3.org/TR/html4/loose.dtd">
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
3
  <html>
4
- <body>
5
- <h1>File not found</h1>
6
- <p>Change this error message for pages not found in public/404.html</p>
7
- </body>
8
- </html>
4
+ <head>
5
+ <title>Error</title>
6
+ <link href="/stylesheets/default.css" media="screen" rel="stylesheet" type="text/css" />
7
+
8
+ <link rel="shortcut icon" href="/images/world.png" type="image/png"/>
9
+
10
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
11
+
12
+ <script src="/javascripts/shadedborder.js" type="text/javascript"></script>
13
+ <script type="text/javascript">
14
+ var border = RUZEE.ShadedBorder.create({ corner: 8, shadow: 16, border: 2 });
15
+ window.onload = function() { border.render('rounded') }
16
+ </script>
17
+ </head>
18
+
19
+ <body>
20
+ <div style="padding: 1em; background: white;" id="rounded">
21
+ <h1>File not found</h1>
22
+ <p>Sorry. The page you were looking for could not be found. Please retry again</p>
23
+ <p>Original site is there: <a href="http://leparlement.org">Parlement</a></p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -1,8 +1,27 @@
1
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
2
- "http://www.w3.org/TR/html4/loose.dtd">
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
3
  <html>
4
- <body>
5
- <h1>Application error (Apache)</h1>
6
- <p>Change this error message for exceptions thrown outside of an action (like in Dispatcher setups or broken Ruby code) in public/500.html</p>
7
- </body>
8
- </html>
4
+ <head>
5
+ <title>Error</title>
6
+ <link href="/stylesheets/default.css" media="screen" rel="stylesheet" type="text/css" />
7
+
8
+ <link rel="shortcut icon" href="/images/world.png" type="image/png"/>
9
+
10
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
11
+
12
+ <script src="/javascripts/shadedborder.js" type="text/javascript"></script>
13
+ <script type="text/javascript">
14
+ var border = RUZEE.ShadedBorder.create({ corner: 8, shadow: 16, border: 2 });
15
+ window.onload = function() { border.render('rounded') }
16
+ </script>
17
+ </head>
18
+
19
+ <body>
20
+ <div style="padding: 1em; background: white;" id="rounded">
21
+ <h1>Application error (Apache)</h1>
22
+ <p>Sorry</p>
23
+ <p>Original site is there: <a href="http://leparlement.org">Parlement</a></p>
24
+ </div>
25
+ </body>
26
+ </html>
27
+
File without changes
File without changes
File without changes
File without changes
@@ -1,2 +1,260 @@
1
1
  // Place your application-specific JavaScript functions and classes here
2
2
  // This file is automatically included by javascript_include_tag :defaults
3
+
4
+ var myrules = {
5
+ '#sidebar' : function(elt) {
6
+ border.render(elt);
7
+ }
8
+ };
9
+
10
+ /* Called for each element to set its knobs */
11
+ function setKnobs(elt) {
12
+ // To not filter out the top element
13
+ if (Element.hasClassName(elt.parentNode, 'top')) return;
14
+
15
+ var knobOpened = document.createElement("a");
16
+ Element.addClassName(knobOpened, "knobOpened");
17
+ knobOpened.href = "#";
18
+ knobOpened.appendChild(document.createTextNode("V"));
19
+
20
+ var knobClosed = document.createElement("a");
21
+ Element.addClassName(knobClosed, "knobClosed");
22
+ knobClosed.href = "#";
23
+ knobClosed.appendChild(document.createTextNode(">"));
24
+
25
+ elt.insertBefore(knobOpened, elt.firstChild);
26
+ elt.insertBefore(knobClosed, elt.firstChild);
27
+
28
+ knobOpened.onclick = function() { return closeElt(elt); }
29
+ knobClosed.onclick = function() { return openElt(elt); }
30
+ }
31
+
32
+ /*
33
+ * Called for each element as it is displayed, to set its status according to
34
+ * the filter
35
+ */
36
+ function setKnob(elt, result) {
37
+ // To not filter out the top element
38
+ if (Element.hasClassName(elt.parentNode, 'top')) return;
39
+
40
+ var f = $("filterForm").filter;
41
+ var filter = parseInt(f.options[f.selectedIndex].value);
42
+
43
+ if (filter == 'null' || result >= filter) {
44
+ Element.removeClassName(elt, 'closed');
45
+ } else if (result < filter) {
46
+ Element.addClassName(elt, 'closed');
47
+ }
48
+ }
49
+
50
+ function closeElt(elt) {
51
+ Element.addClassName(elt, "closed");
52
+ Element.removeClassName(elt, "opened");
53
+
54
+ elt.select("opened").each( function(e) {
55
+ Element.removeClassName(e, "opened");
56
+ });
57
+ elt.select("closed").each( function(e) {
58
+ Element.removeClassName(e, "closed");
59
+ });
60
+
61
+ var openedParent = false;
62
+ var current = elt;
63
+ while (!openedParent && document != (current = current.parentNode)) {
64
+ openedParent = Element.hasClassName(current, 'opened');
65
+ }
66
+ if (openedParent) {
67
+ Element.removeClassName(current, "opened");
68
+ Element.removeClassName(elt, "closed");
69
+ }
70
+ return false;
71
+ }
72
+
73
+ function openElt(elt) {
74
+ var closedParent = false;
75
+ var current = elt;
76
+ while (!closedParent && document != (current = current.parentNode)) {
77
+ closedParent = Element.hasClassName(current, 'closed');
78
+ }
79
+ if (closedParent) {
80
+ Element.addClassName(elt, "opened");
81
+ }
82
+
83
+ Element.removeClassName(elt, "closed");
84
+ elt.select("opened").each( function(e) {
85
+ Element.removeClassName(e, "opened");
86
+ });
87
+ elt.select("closed").each( function(e) {
88
+ Element.removeClassName(e, "closed");
89
+ });
90
+ return false;
91
+ }
92
+
93
+
94
+ function getCookie(name) {
95
+ var start = document.cookie.indexOf(name+"=");
96
+ var len = start+name.length+1;
97
+ if ((!start) && (name != document.cookie.substring(0, name.length))) return null;
98
+ if (start == -1) return null;
99
+ var end = document.cookie.indexOf(";",len);
100
+ if (end == -1) end = document.cookie.length;
101
+ return unescape(document.cookie.substring(len, end));
102
+ }
103
+
104
+ function setCookie(name, value) {
105
+ var expires_date = new Date( new Date().getTime() + (3000 * 1000 * 60 * 60 * 24) );
106
+ document.cookie = name + "=" +escape( value ) +
107
+ (";expires=" + expires_date.toGMTString());
108
+ /*+
109
+ ( ( path ) ? ";path=" + path : "" ) +
110
+ ( ( domain ) ? ";domain=" + domain : "" ) +
111
+ ( ( secure ) ? ";secure" : "" );*/
112
+ }
113
+
114
+
115
+ /*
116
+ * The parameter 'filter' is not the value itself, but an index!
117
+ */
118
+ function setFilter(filter, elt) {
119
+ setCookie('filter', $("filterForm").filter.selectedIndex);
120
+
121
+ new Ajax.Updater('listByVote', '/elt/listByVote/'+elt, { asynchronous:true, evalScripts:true });
122
+ new Ajax.Updater('listByDate', '/elt/listByDate/'+elt, { asynchronous:true, evalScripts:true });
123
+
124
+ $$('.result').each( function(result) {
125
+ //alert(parseInt(result.innerHTML)+', '+filter);
126
+ Element.removeClassName(result.parentNode.parentNode, 'opened');
127
+ if (filter == 'null' || parseInt(result.innerHTML) >= parseInt(filter)) {
128
+ Element.removeClassName(result.parentNode.parentNode, 'closed');
129
+ } else if (parseInt(result.innerHTML) < parseInt(filter)) {
130
+ Element.addClassName(result.parentNode.parentNode, 'closed');
131
+ }
132
+ });
133
+ }
134
+
135
+ function setFilterFromCookie() {
136
+ if (getCookie('filter') != null) {
137
+ $("filterForm").filter.selectedIndex = getCookie('filter');
138
+ }
139
+ }
140
+
141
+ function decrementFilter() {
142
+ $("filterForm").filter.selectedIndex--;
143
+ $("filterForm").filter.onchange();
144
+ return false;
145
+ }
146
+
147
+ function incrementFilter() {
148
+ $("filterForm").filter.selectedIndex++;
149
+ $("filterForm").filter.onchange();
150
+ return false;
151
+ }
152
+
153
+ /*
154
+ * Set all choices to nothing.
155
+ * Mostly used for login/logout
156
+ */
157
+ function resetChoices() {
158
+ document.body.select('selected').each( function(choice) {
159
+ Element.removeClassName(choice, 'selected');
160
+ });
161
+ }
162
+
163
+ /*
164
+ * Set all choices to user's values
165
+ * Mostly used for login/logout
166
+ */
167
+ function updateChoices(choices) {
168
+ $$('.con').each( function(choice) {
169
+ if (choices[choice.parentNode.parentNode.parentNode.id] == "-1") {
170
+ Element.addClassName(choice, 'selected');
171
+ }
172
+ });
173
+ $$('.result').each( function(choice) {
174
+ if (choices[choice.parentNode.parentNode.id] == "-1"
175
+ || choices[choice.parentNode.parentNode.id] == "1") {
176
+ Element.addClassName(choice, 'selected');
177
+ }
178
+ });
179
+ $$('.pro').each( function(choice) {
180
+ if (choices[choice.parentNode.parentNode.parentNode.id] == "1") {
181
+ Element.addClassName(choice, 'selected');
182
+ }
183
+ });
184
+ }
185
+
186
+ function setSize(img) {
187
+ var divHeight = findPosY(img.parentNode.getElementsByClassName('choice')[0]) - findPosY(img);
188
+
189
+ if (false && img.offsetWidth > 1) {
190
+ var debug = document.createElement("span");
191
+ debug.appendChild(document.createTextNode(
192
+ "divHeight: "+divHeight
193
+ +"; img.offsetHeight:"+img.offsetHeight
194
+ +"; img.offsetParent.offsetWidth: "+img.offsetParent.offsetWidth
195
+ +"; img.offsetWidth:"+img.offsetWidth
196
+ +"; findPosY(img):"+findPosY(img)
197
+ +"; findPosY(document.getElementsByClassName('choice', img.parentNode)[0]):"
198
+ +findPosY(img.parentNode.getElementsByClassName('choice')[0])));
199
+ if (img.parentNode.parentNode) img.parentNode.parentNode.insertBefore(debug, img.parentNode);
200
+ }
201
+
202
+ if (divHeight > 0 && img.offsetHeight > divHeight) img.style.height = divHeight+"px";
203
+
204
+ // For IE
205
+ // Only touch the width, in order to keep the width/height ratio
206
+ while (img.offsetHeight > 0 && img.offsetWidth > img.offsetParent.offsetWidth / 3) {
207
+ img.style.height = (img.offsetHeight - 30) + "px";
208
+ }
209
+ }
210
+
211
+ function findPosY(obj) {
212
+ if (!obj) return 0;
213
+ var curtop = 0;
214
+ if (obj.offsetParent)
215
+ while (1) {
216
+ curtop += obj.offsetTop;
217
+ if (!obj.offsetParent)
218
+ break;
219
+ obj = obj.offsetParent;
220
+ }
221
+ else if (obj.y)
222
+ curtop += obj.y;
223
+ return curtop;
224
+ }
225
+
226
+ // Widen the elt until it is moved below or it is wider than its parent elt
227
+ function widen(elt) {
228
+ if (elt.widened) return;
229
+ elt.widened = true;
230
+
231
+ var initialPosition = findPosY(elt);
232
+ var initialParentWidth = elt.parentNode.offsetWidth;
233
+ while (initialPosition == findPosY(elt) && elt.offsetWidth < initialParentWidth) {
234
+ elt.cols = elt.cols + 1;
235
+ }
236
+ elt.cols = elt.cols - 1;
237
+ //alert(elt.cols);
238
+ }
239
+
240
+ // Resize text area accordingly to its content
241
+ function resize(elt) {
242
+ //alert(elt.parentNode.offsetWidth);
243
+ //elt.style.width = elt.parentNode.offsetWidth+"px";
244
+ widen(elt);
245
+ var lines = elt.value.split('\n');
246
+ var newRows = lines.length + 1;
247
+ var oldRows = elt.rows;
248
+
249
+ for (var i = 0; i < lines.length; i++) {
250
+ var line = lines[i];
251
+ if (line.length >= elt.cols) newRows += Math.floor(line.length / elt.cols);
252
+ }
253
+ //alert(newRows);
254
+
255
+ if (newRows > elt.rows) elt.rows = newRows;
256
+ if (newRows < elt.rows) elt.rows = Math.max(5, newRows);
257
+ }
258
+
259
+ var border = RUZEE.ShadedBorder.create({ corner: 8, shadow: 16, border: 2 });
260
+
@@ -1,22 +1,22 @@
1
- // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2
- // (c) 2005, 2006 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3
- // (c) 2005, 2006 Jon Tirsen (http://www.tirsen.com)
1
+ // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2
+ // (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3
+ // (c) 2005-2008 Jon Tirsen (http://www.tirsen.com)
4
4
  // Contributors:
5
5
  // Richard Livsey
6
6
  // Rahul Bhargava
7
7
  // Rob Wills
8
- //
8
+ //
9
9
  // script.aculo.us is freely distributable under the terms of an MIT-style license.
10
10
  // For details, see the script.aculo.us web site: http://script.aculo.us/
11
11
 
12
- // Autocompleter.Base handles all the autocompletion functionality
12
+ // Autocompleter.Base handles all the autocompletion functionality
13
13
  // that's independent of the data source for autocompletion. This
14
14
  // includes drawing the autocompletion menu, observing keyboard
15
15
  // and mouse events, and similar.
16
16
  //
17
- // Specific autocompleters need to provide, at the very least,
17
+ // Specific autocompleters need to provide, at the very least,
18
18
  // a getUpdatedChoices function that will be invoked every time
19
- // the text inside the monitored textbox changes. This method
19
+ // the text inside the monitored textbox changes. This method
20
20
  // should get the text for which to provide autocompletion by
21
21
  // invoking this.getToken(), NOT by directly accessing
22
22
  // this.element.value. This is to allow incremental tokenized
@@ -30,68 +30,71 @@
30
30
  // will incrementally autocomplete with a comma as the token.
31
31
  // Additionally, ',' in the above example can be replaced with
32
32
  // a token array, e.g. { tokens: [',', '\n'] } which
33
- // enables autocompletion on multiple tokens. This is most
34
- // useful when one of the tokens is \n (a newline), as it
33
+ // enables autocompletion on multiple tokens. This is most
34
+ // useful when one of the tokens is \n (a newline), as it
35
35
  // allows smart autocompletion after linebreaks.
36
36
 
37
37
  if(typeof Effect == 'undefined')
38
38
  throw("controls.js requires including script.aculo.us' effects.js library");
39
39
 
40
- var Autocompleter = {}
41
- Autocompleter.Base = function() {};
42
- Autocompleter.Base.prototype = {
40
+ var Autocompleter = { };
41
+ Autocompleter.Base = Class.create({
43
42
  baseInitialize: function(element, update, options) {
44
- this.element = $(element);
45
- this.update = $(update);
46
- this.hasFocus = false;
47
- this.changed = false;
48
- this.active = false;
49
- this.index = 0;
43
+ element = $(element);
44
+ this.element = element;
45
+ this.update = $(update);
46
+ this.hasFocus = false;
47
+ this.changed = false;
48
+ this.active = false;
49
+ this.index = 0;
50
50
  this.entryCount = 0;
51
+ this.oldElementValue = this.element.value;
51
52
 
52
53
  if(this.setOptions)
53
54
  this.setOptions(options);
54
55
  else
55
- this.options = options || {};
56
+ this.options = options || { };
56
57
 
57
58
  this.options.paramName = this.options.paramName || this.element.name;
58
59
  this.options.tokens = this.options.tokens || [];
59
60
  this.options.frequency = this.options.frequency || 0.4;
60
61
  this.options.minChars = this.options.minChars || 1;
61
- this.options.onShow = this.options.onShow ||
62
- function(element, update){
62
+ this.options.onShow = this.options.onShow ||
63
+ function(element, update){
63
64
  if(!update.style.position || update.style.position=='absolute') {
64
65
  update.style.position = 'absolute';
65
66
  Position.clone(element, update, {
66
- setHeight: false,
67
+ setHeight: false,
67
68
  offsetTop: element.offsetHeight
68
69
  });
69
70
  }
70
71
  Effect.Appear(update,{duration:0.15});
71
72
  };
72
- this.options.onHide = this.options.onHide ||
73
+ this.options.onHide = this.options.onHide ||
73
74
  function(element, update){ new Effect.Fade(update,{duration:0.15}) };
74
75
 
75
- if(typeof(this.options.tokens) == 'string')
76
+ if(typeof(this.options.tokens) == 'string')
76
77
  this.options.tokens = new Array(this.options.tokens);
78
+ // Force carriage returns as token delimiters anyway
79
+ if (!this.options.tokens.include('\n'))
80
+ this.options.tokens.push('\n');
77
81
 
78
82
  this.observer = null;
79
-
83
+
80
84
  this.element.setAttribute('autocomplete','off');
81
85
 
82
86
  Element.hide(this.update);
83
87
 
84
- Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
85
- Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
88
+ Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
89
+ Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
86
90
  },
87
91
 
88
92
  show: function() {
89
93
  if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
90
- if(!this.iefix &&
91
- (navigator.appVersion.indexOf('MSIE')>0) &&
92
- (navigator.userAgent.indexOf('Opera')<0) &&
94
+ if(!this.iefix &&
95
+ (Prototype.Browser.IE) &&
93
96
  (Element.getStyle(this.update, 'position')=='absolute')) {
94
- new Insertion.After(this.update,
97
+ new Insertion.After(this.update,
95
98
  '<iframe id="' + this.update.id + '_iefix" '+
96
99
  'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
97
100
  'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
@@ -99,7 +102,7 @@ Autocompleter.Base.prototype = {
99
102
  }
100
103
  if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
101
104
  },
102
-
105
+
103
106
  fixIEOverlapping: function() {
104
107
  Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
105
108
  this.iefix.style.zIndex = 1;
@@ -139,23 +142,23 @@ Autocompleter.Base.prototype = {
139
142
  case Event.KEY_UP:
140
143
  this.markPrevious();
141
144
  this.render();
142
- if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
145
+ Event.stop(event);
143
146
  return;
144
147
  case Event.KEY_DOWN:
145
148
  this.markNext();
146
149
  this.render();
147
- if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
150
+ Event.stop(event);
148
151
  return;
149
152
  }
150
- else
151
- if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
152
- (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
153
+ else
154
+ if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
155
+ (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
153
156
 
154
157
  this.changed = true;
155
158
  this.hasFocus = true;
156
159
 
157
160
  if(this.observer) clearTimeout(this.observer);
158
- this.observer =
161
+ this.observer =
159
162
  setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
160
163
  },
161
164
 
@@ -167,36 +170,35 @@ Autocompleter.Base.prototype = {
167
170
 
168
171
  onHover: function(event) {
169
172
  var element = Event.findElement(event, 'LI');
170
- if(this.index != element.autocompleteIndex)
173
+ if(this.index != element.autocompleteIndex)
171
174
  {
172
175
  this.index = element.autocompleteIndex;
173
176
  this.render();
174
177
  }
175
178
  Event.stop(event);
176
179
  },
177
-
180
+
178
181
  onClick: function(event) {
179
182
  var element = Event.findElement(event, 'LI');
180
183
  this.index = element.autocompleteIndex;
181
184
  this.selectEntry();
182
185
  this.hide();
183
186
  },
184
-
187
+
185
188
  onBlur: function(event) {
186
189
  // needed to make click events working
187
190
  setTimeout(this.hide.bind(this), 250);
188
191
  this.hasFocus = false;
189
- this.active = false;
190
- },
191
-
192
+ this.active = false;
193
+ },
194
+
192
195
  render: function() {
193
196
  if(this.entryCount > 0) {
194
197
  for (var i = 0; i < this.entryCount; i++)
195
- this.index==i ?
196
- Element.addClassName(this.getEntry(i),"selected") :
198
+ this.index==i ?
199
+ Element.addClassName(this.getEntry(i),"selected") :
197
200
  Element.removeClassName(this.getEntry(i),"selected");
198
-
199
- if(this.hasFocus) {
201
+ if(this.hasFocus) {
200
202
  this.show();
201
203
  this.active = true;
202
204
  }
@@ -205,27 +207,27 @@ Autocompleter.Base.prototype = {
205
207
  this.hide();
206
208
  }
207
209
  },
208
-
210
+
209
211
  markPrevious: function() {
210
- if(this.index > 0) this.index--
212
+ if(this.index > 0) this.index--;
211
213
  else this.index = this.entryCount-1;
212
214
  this.getEntry(this.index).scrollIntoView(true);
213
215
  },
214
-
216
+
215
217
  markNext: function() {
216
- if(this.index < this.entryCount-1) this.index++
218
+ if(this.index < this.entryCount-1) this.index++;
217
219
  else this.index = 0;
218
220
  this.getEntry(this.index).scrollIntoView(false);
219
221
  },
220
-
222
+
221
223
  getEntry: function(index) {
222
224
  return this.update.firstChild.childNodes[index];
223
225
  },
224
-
226
+
225
227
  getCurrentEntry: function() {
226
228
  return this.getEntry(this.index);
227
229
  },
228
-
230
+
229
231
  selectEntry: function() {
230
232
  this.active = false;
231
233
  this.updateElement(this.getCurrentEntry());
@@ -238,23 +240,24 @@ Autocompleter.Base.prototype = {
238
240
  }
239
241
  var value = '';
240
242
  if (this.options.select) {
241
- var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
243
+ var nodes = $(selectedElement).select('.' + this.options.select) || [];
242
244
  if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
243
245
  } else
244
246
  value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
245
-
246
- var lastTokenPos = this.findLastToken();
247
- if (lastTokenPos != -1) {
248
- var newValue = this.element.value.substr(0, lastTokenPos + 1);
249
- var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
247
+
248
+ var bounds = this.getTokenBounds();
249
+ if (bounds[0] != -1) {
250
+ var newValue = this.element.value.substr(0, bounds[0]);
251
+ var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
250
252
  if (whitespace)
251
253
  newValue += whitespace[0];
252
- this.element.value = newValue + value;
254
+ this.element.value = newValue + value + this.element.value.substr(bounds[1]);
253
255
  } else {
254
256
  this.element.value = value;
255
257
  }
258
+ this.oldElementValue = this.element.value;
256
259
  this.element.focus();
257
-
260
+
258
261
  if (this.options.afterUpdateElement)
259
262
  this.options.afterUpdateElement(this.element, selectedElement);
260
263
  },
@@ -266,20 +269,20 @@ Autocompleter.Base.prototype = {
266
269
  Element.cleanWhitespace(this.update.down());
267
270
 
268
271
  if(this.update.firstChild && this.update.down().childNodes) {
269
- this.entryCount =
272
+ this.entryCount =
270
273
  this.update.down().childNodes.length;
271
274
  for (var i = 0; i < this.entryCount; i++) {
272
275
  var entry = this.getEntry(i);
273
276
  entry.autocompleteIndex = i;
274
277
  this.addObservers(entry);
275
278
  }
276
- } else {
279
+ } else {
277
280
  this.entryCount = 0;
278
281
  }
279
282
 
280
283
  this.stopIndicator();
281
284
  this.index = 0;
282
-
285
+
283
286
  if(this.entryCount==1 && this.options.autoSelect) {
284
287
  this.selectEntry();
285
288
  this.hide();
@@ -295,40 +298,49 @@ Autocompleter.Base.prototype = {
295
298
  },
296
299
 
297
300
  onObserverEvent: function() {
298
- this.changed = false;
301
+ this.changed = false;
302
+ this.tokenBounds = null;
299
303
  if(this.getToken().length>=this.options.minChars) {
300
- this.startIndicator();
301
304
  this.getUpdatedChoices();
302
305
  } else {
303
306
  this.active = false;
304
307
  this.hide();
305
308
  }
309
+ this.oldElementValue = this.element.value;
306
310
  },
307
311
 
308
312
  getToken: function() {
309
- var tokenPos = this.findLastToken();
310
- if (tokenPos != -1)
311
- var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
312
- else
313
- var ret = this.element.value;
314
-
315
- return /\n/.test(ret) ? '' : ret;
316
- },
317
-
318
- findLastToken: function() {
319
- var lastTokenPos = -1;
320
-
321
- for (var i=0; i<this.options.tokens.length; i++) {
322
- var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
323
- if (thisTokenPos > lastTokenPos)
324
- lastTokenPos = thisTokenPos;
313
+ var bounds = this.getTokenBounds();
314
+ return this.element.value.substring(bounds[0], bounds[1]).strip();
315
+ },
316
+
317
+ getTokenBounds: function() {
318
+ if (null != this.tokenBounds) return this.tokenBounds;
319
+ var value = this.element.value;
320
+ if (value.strip().empty()) return [-1, 0];
321
+ var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
322
+ var offset = (diff == this.oldElementValue.length ? 1 : 0);
323
+ var prevTokenPos = -1, nextTokenPos = value.length;
324
+ var tp;
325
+ for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
326
+ tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
327
+ if (tp > prevTokenPos) prevTokenPos = tp;
328
+ tp = value.indexOf(this.options.tokens[index], diff + offset);
329
+ if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
325
330
  }
326
- return lastTokenPos;
331
+ return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
327
332
  }
328
- }
333
+ });
329
334
 
330
- Ajax.Autocompleter = Class.create();
331
- Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
335
+ Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
336
+ var boundary = Math.min(newS.length, oldS.length);
337
+ for (var index = 0; index < boundary; ++index)
338
+ if (newS[index] != oldS[index])
339
+ return index;
340
+ return boundary;
341
+ };
342
+
343
+ Ajax.Autocompleter = Class.create(Autocompleter.Base, {
332
344
  initialize: function(element, update, url, options) {
333
345
  this.baseInitialize(element, update, options);
334
346
  this.options.asynchronous = true;
@@ -338,13 +350,15 @@ Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.pro
338
350
  },
339
351
 
340
352
  getUpdatedChoices: function() {
341
- entry = encodeURIComponent(this.options.paramName) + '=' +
353
+ this.startIndicator();
354
+
355
+ var entry = encodeURIComponent(this.options.paramName) + '=' +
342
356
  encodeURIComponent(this.getToken());
343
357
 
344
358
  this.options.parameters = this.options.callback ?
345
359
  this.options.callback(this.element, entry) : entry;
346
360
 
347
- if(this.options.defaultParams)
361
+ if(this.options.defaultParams)
348
362
  this.options.parameters += '&' + this.options.defaultParams;
349
363
 
350
364
  new Ajax.Request(this.url, this.options);
@@ -353,7 +367,6 @@ Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.pro
353
367
  onComplete: function(request) {
354
368
  this.updateChoices(request.responseText);
355
369
  }
356
-
357
370
  });
358
371
 
359
372
  // The local array autocompleter. Used when you'd prefer to
@@ -369,7 +382,7 @@ Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.pro
369
382
  // - choices - How many autocompletion choices to offer
370
383
  //
371
384
  // - partialSearch - If false, the autocompleter will match entered
372
- // text only at the beginning of strings in the
385
+ // text only at the beginning of strings in the
373
386
  // autocomplete array. Defaults to true, which will
374
387
  // match text at the beginning of any *word* in the
375
388
  // strings in the autocomplete array. If you want to
@@ -386,13 +399,12 @@ Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.pro
386
399
  // - ignoreCase - Whether to ignore case when autocompleting.
387
400
  // Defaults to true.
388
401
  //
389
- // It's possible to pass in a custom function as the 'selector'
402
+ // It's possible to pass in a custom function as the 'selector'
390
403
  // option, if you prefer to write your own autocompletion logic.
391
404
  // In that case, the other options above will not apply unless
392
405
  // you support them.
393
406
 
394
- Autocompleter.Local = Class.create();
395
- Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
407
+ Autocompleter.Local = Class.create(Autocompleter.Base, {
396
408
  initialize: function(element, update, array, options) {
397
409
  this.baseInitialize(element, update, options);
398
410
  this.options.array = array;
@@ -415,20 +427,20 @@ Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
415
427
  var entry = instance.getToken();
416
428
  var count = 0;
417
429
 
418
- for (var i = 0; i < instance.options.array.length &&
419
- ret.length < instance.options.choices ; i++) {
430
+ for (var i = 0; i < instance.options.array.length &&
431
+ ret.length < instance.options.choices ; i++) {
420
432
 
421
433
  var elem = instance.options.array[i];
422
- var foundPos = instance.options.ignoreCase ?
423
- elem.toLowerCase().indexOf(entry.toLowerCase()) :
434
+ var foundPos = instance.options.ignoreCase ?
435
+ elem.toLowerCase().indexOf(entry.toLowerCase()) :
424
436
  elem.indexOf(entry);
425
437
 
426
438
  while (foundPos != -1) {
427
- if (foundPos == 0 && elem.length != entry.length) {
428
- ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
439
+ if (foundPos == 0 && elem.length != entry.length) {
440
+ ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
429
441
  elem.substr(entry.length) + "</li>");
430
442
  break;
431
- } else if (entry.length >= instance.options.partialChars &&
443
+ } else if (entry.length >= instance.options.partialChars &&
432
444
  instance.options.partialSearch && foundPos != -1) {
433
445
  if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
434
446
  partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
@@ -438,23 +450,22 @@ Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
438
450
  }
439
451
  }
440
452
 
441
- foundPos = instance.options.ignoreCase ?
442
- elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
453
+ foundPos = instance.options.ignoreCase ?
454
+ elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
443
455
  elem.indexOf(entry, foundPos + 1);
444
456
 
445
457
  }
446
458
  }
447
459
  if (partial.length)
448
- ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
460
+ ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
449
461
  return "<ul>" + ret.join('') + "</ul>";
450
462
  }
451
- }, options || {});
463
+ }, options || { });
452
464
  }
453
465
  });
454
466
 
455
- // AJAX in-place editor
456
- //
457
- // see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
467
+ // AJAX in-place editor and collection editor
468
+ // Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
458
469
 
459
470
  // Use this if you notice weird scrolling problems on some browsers,
460
471
  // the DOM might be a bit confused when this gets called so do this
@@ -463,361 +474,480 @@ Field.scrollFreeActivate = function(field) {
463
474
  setTimeout(function() {
464
475
  Field.activate(field);
465
476
  }, 1);
466
- }
477
+ };
467
478
 
468
- Ajax.InPlaceEditor = Class.create();
469
- Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
470
- Ajax.InPlaceEditor.prototype = {
479
+ Ajax.InPlaceEditor = Class.create({
471
480
  initialize: function(element, url, options) {
472
481
  this.url = url;
473
- this.element = $(element);
474
-
475
- this.options = Object.extend({
476
- paramName: "value",
477
- okButton: true,
478
- okText: "ok",
479
- cancelLink: true,
480
- cancelText: "cancel",
481
- savingText: "Saving...",
482
- clickToEditText: "Click to edit",
483
- okText: "ok",
484
- rows: 1,
485
- onComplete: function(transport, element) {
486
- new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
487
- },
488
- onFailure: function(transport) {
489
- alert("Error communicating with the server: " + transport.responseText.stripTags());
490
- },
491
- callback: function(form) {
492
- return Form.serialize(form);
493
- },
494
- handleLineBreaks: true,
495
- loadingText: 'Loading...',
496
- savingClassName: 'inplaceeditor-saving',
497
- loadingClassName: 'inplaceeditor-loading',
498
- formClassName: 'inplaceeditor-form',
499
- highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
500
- highlightendcolor: "#FFFFFF",
501
- externalControl: null,
502
- submitOnBlur: false,
503
- ajaxOptions: {},
504
- evalScripts: false
505
- }, options || {});
506
-
507
- if(!this.options.formId && this.element.id) {
508
- this.options.formId = this.element.id + "-inplaceeditor";
509
- if ($(this.options.formId)) {
510
- // there's already a form with that name, don't specify an id
511
- this.options.formId = null;
512
- }
482
+ this.element = element = $(element);
483
+ this.prepareOptions();
484
+ this._controls = { };
485
+ arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
486
+ Object.extend(this.options, options || { });
487
+ if (!this.options.formId && this.element.id) {
488
+ this.options.formId = this.element.id + '-inplaceeditor';
489
+ if ($(this.options.formId))
490
+ this.options.formId = '';
513
491
  }
514
-
515
- if (this.options.externalControl) {
492
+ if (this.options.externalControl)
516
493
  this.options.externalControl = $(this.options.externalControl);
517
- }
518
-
519
- this.originalBackground = Element.getStyle(this.element, 'background-color');
520
- if (!this.originalBackground) {
521
- this.originalBackground = "transparent";
522
- }
523
-
494
+ if (!this.options.externalControl)
495
+ this.options.externalControlOnly = false;
496
+ this._originalBackground = this.element.getStyle('background-color') || 'transparent';
524
497
  this.element.title = this.options.clickToEditText;
525
-
526
- this.onclickListener = this.enterEditMode.bindAsEventListener(this);
527
- this.mouseoverListener = this.enterHover.bindAsEventListener(this);
528
- this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
529
- Event.observe(this.element, 'click', this.onclickListener);
530
- Event.observe(this.element, 'mouseover', this.mouseoverListener);
531
- Event.observe(this.element, 'mouseout', this.mouseoutListener);
532
- if (this.options.externalControl) {
533
- Event.observe(this.options.externalControl, 'click', this.onclickListener);
534
- Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
535
- Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
498
+ this._boundCancelHandler = this.handleFormCancellation.bind(this);
499
+ this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
500
+ this._boundFailureHandler = this.handleAJAXFailure.bind(this);
501
+ this._boundSubmitHandler = this.handleFormSubmission.bind(this);
502
+ this._boundWrapperHandler = this.wrapUp.bind(this);
503
+ this.registerListeners();
504
+ },
505
+ checkForEscapeOrReturn: function(e) {
506
+ if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
507
+ if (Event.KEY_ESC == e.keyCode)
508
+ this.handleFormCancellation(e);
509
+ else if (Event.KEY_RETURN == e.keyCode)
510
+ this.handleFormSubmission(e);
511
+ },
512
+ createControl: function(mode, handler, extraClasses) {
513
+ var control = this.options[mode + 'Control'];
514
+ var text = this.options[mode + 'Text'];
515
+ if ('button' == control) {
516
+ var btn = document.createElement('input');
517
+ btn.type = 'submit';
518
+ btn.value = text;
519
+ btn.className = 'editor_' + mode + '_button';
520
+ if ('cancel' == mode)
521
+ btn.onclick = this._boundCancelHandler;
522
+ this._form.appendChild(btn);
523
+ this._controls[mode] = btn;
524
+ } else if ('link' == control) {
525
+ var link = document.createElement('a');
526
+ link.href = '#';
527
+ link.appendChild(document.createTextNode(text));
528
+ link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
529
+ link.className = 'editor_' + mode + '_link';
530
+ if (extraClasses)
531
+ link.className += ' ' + extraClasses;
532
+ this._form.appendChild(link);
533
+ this._controls[mode] = link;
536
534
  }
537
535
  },
538
- enterEditMode: function(evt) {
539
- if (this.saving) return;
540
- if (this.editing) return;
541
- this.editing = true;
542
- this.onEnterEditMode();
543
- if (this.options.externalControl) {
544
- Element.hide(this.options.externalControl);
545
- }
546
- Element.hide(this.element);
547
- this.createForm();
548
- this.element.parentNode.insertBefore(this.form, this.element);
549
- if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
550
- // stop the event to avoid a page refresh in Safari
551
- if (evt) {
552
- Event.stop(evt);
536
+ createEditField: function() {
537
+ var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
538
+ var fld;
539
+ if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
540
+ fld = document.createElement('input');
541
+ fld.type = 'text';
542
+ var size = this.options.size || this.options.cols || 0;
543
+ if (0 < size) fld.size = size;
544
+ } else {
545
+ fld = document.createElement('textarea');
546
+ fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
547
+ fld.cols = this.options.cols || 40;
553
548
  }
554
- return false;
549
+ fld.name = this.options.paramName;
550
+ fld.value = text; // No HTML breaks conversion anymore
551
+ fld.className = 'editor_field';
552
+ if (this.options.submitOnBlur)
553
+ fld.onblur = this._boundSubmitHandler;
554
+ this._controls.editor = fld;
555
+ if (this.options.loadTextURL)
556
+ this.loadExternalText();
557
+ this._form.appendChild(this._controls.editor);
555
558
  },
556
559
  createForm: function() {
557
- this.form = document.createElement("form");
558
- this.form.id = this.options.formId;
559
- Element.addClassName(this.form, this.options.formClassName)
560
- this.form.onsubmit = this.onSubmit.bind(this);
561
-
560
+ var ipe = this;
561
+ function addText(mode, condition) {
562
+ var text = ipe.options['text' + mode + 'Controls'];
563
+ if (!text || condition === false) return;
564
+ ipe._form.appendChild(document.createTextNode(text));
565
+ };
566
+ this._form = $(document.createElement('form'));
567
+ this._form.id = this.options.formId;
568
+ this._form.addClassName(this.options.formClassName);
569
+ this._form.onsubmit = this._boundSubmitHandler;
562
570
  this.createEditField();
563
-
564
- if (this.options.textarea) {
565
- var br = document.createElement("br");
566
- this.form.appendChild(br);
567
- }
568
-
569
- if (this.options.okButton) {
570
- okButton = document.createElement("input");
571
- okButton.type = "submit";
572
- okButton.value = this.options.okText;
573
- okButton.className = 'editor_ok_button';
574
- this.form.appendChild(okButton);
575
- }
576
-
577
- if (this.options.cancelLink) {
578
- cancelLink = document.createElement("a");
579
- cancelLink.href = "#";
580
- cancelLink.appendChild(document.createTextNode(this.options.cancelText));
581
- cancelLink.onclick = this.onclickCancel.bind(this);
582
- cancelLink.className = 'editor_cancel';
583
- this.form.appendChild(cancelLink);
584
- }
571
+ if ('textarea' == this._controls.editor.tagName.toLowerCase())
572
+ this._form.appendChild(document.createElement('br'));
573
+ if (this.options.onFormCustomization)
574
+ this.options.onFormCustomization(this, this._form);
575
+ addText('Before', this.options.okControl || this.options.cancelControl);
576
+ this.createControl('ok', this._boundSubmitHandler);
577
+ addText('Between', this.options.okControl && this.options.cancelControl);
578
+ this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
579
+ addText('After', this.options.okControl || this.options.cancelControl);
580
+ },
581
+ destroy: function() {
582
+ if (this._oldInnerHTML)
583
+ this.element.innerHTML = this._oldInnerHTML;
584
+ this.leaveEditMode();
585
+ this.unregisterListeners();
586
+ },
587
+ enterEditMode: function(e) {
588
+ if (this._saving || this._editing) return;
589
+ this._editing = true;
590
+ this.triggerCallback('onEnterEditMode');
591
+ if (this.options.externalControl)
592
+ this.options.externalControl.hide();
593
+ this.element.hide();
594
+ this.createForm();
595
+ this.element.parentNode.insertBefore(this._form, this.element);
596
+ if (!this.options.loadTextURL)
597
+ this.postProcessEditField();
598
+ if (e) Event.stop(e);
585
599
  },
586
- hasHTMLLineBreaks: function(string) {
587
- if (!this.options.handleLineBreaks) return false;
588
- return string.match(/<br/i) || string.match(/<p>/i);
600
+ enterHover: function(e) {
601
+ if (this.options.hoverClassName)
602
+ this.element.addClassName(this.options.hoverClassName);
603
+ if (this._saving) return;
604
+ this.triggerCallback('onEnterHover');
589
605
  },
590
- convertHTMLLineBreaks: function(string) {
591
- return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
606
+ getText: function() {
607
+ return this.element.innerHTML.unescapeHTML();
592
608
  },
593
- createEditField: function() {
594
- var text;
595
- if(this.options.loadTextURL) {
596
- text = this.options.loadingText;
597
- } else {
598
- text = this.getText();
609
+ handleAJAXFailure: function(transport) {
610
+ this.triggerCallback('onFailure', transport);
611
+ if (this._oldInnerHTML) {
612
+ this.element.innerHTML = this._oldInnerHTML;
613
+ this._oldInnerHTML = null;
599
614
  }
600
-
601
- var obj = this;
602
-
603
- if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
604
- this.options.textarea = false;
605
- var textField = document.createElement("input");
606
- textField.obj = this;
607
- textField.type = "text";
608
- textField.name = this.options.paramName;
609
- textField.value = text;
610
- textField.style.backgroundColor = this.options.highlightcolor;
611
- textField.className = 'editor_field';
612
- var size = this.options.size || this.options.cols || 0;
613
- if (size != 0) textField.size = size;
614
- if (this.options.submitOnBlur)
615
- textField.onblur = this.onSubmit.bind(this);
616
- this.editField = textField;
615
+ },
616
+ handleFormCancellation: function(e) {
617
+ this.wrapUp();
618
+ if (e) Event.stop(e);
619
+ },
620
+ handleFormSubmission: function(e) {
621
+ var form = this._form;
622
+ var value = $F(this._controls.editor);
623
+ this.prepareSubmission();
624
+ var params = this.options.callback(form, value) || '';
625
+ if (Object.isString(params))
626
+ params = params.toQueryParams();
627
+ params.editorId = this.element.id;
628
+ if (this.options.htmlResponse) {
629
+ var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
630
+ Object.extend(options, {
631
+ parameters: params,
632
+ onComplete: this._boundWrapperHandler,
633
+ onFailure: this._boundFailureHandler
634
+ });
635
+ new Ajax.Updater({ success: this.element }, this.url, options);
617
636
  } else {
618
- this.options.textarea = true;
619
- var textArea = document.createElement("textarea");
620
- textArea.obj = this;
621
- textArea.name = this.options.paramName;
622
- textArea.value = this.convertHTMLLineBreaks(text);
623
- textArea.rows = this.options.rows;
624
- textArea.cols = this.options.cols || 40;
625
- textArea.className = 'editor_field';
626
- if (this.options.submitOnBlur)
627
- textArea.onblur = this.onSubmit.bind(this);
628
- this.editField = textArea;
629
- }
630
-
631
- if(this.options.loadTextURL) {
632
- this.loadExternalText();
637
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
638
+ Object.extend(options, {
639
+ parameters: params,
640
+ onComplete: this._boundWrapperHandler,
641
+ onFailure: this._boundFailureHandler
642
+ });
643
+ new Ajax.Request(this.url, options);
633
644
  }
634
- this.form.appendChild(this.editField);
645
+ if (e) Event.stop(e);
635
646
  },
636
- getText: function() {
637
- return this.element.innerHTML;
647
+ leaveEditMode: function() {
648
+ this.element.removeClassName(this.options.savingClassName);
649
+ this.removeForm();
650
+ this.leaveHover();
651
+ this.element.style.backgroundColor = this._originalBackground;
652
+ this.element.show();
653
+ if (this.options.externalControl)
654
+ this.options.externalControl.show();
655
+ this._saving = false;
656
+ this._editing = false;
657
+ this._oldInnerHTML = null;
658
+ this.triggerCallback('onLeaveEditMode');
659
+ },
660
+ leaveHover: function(e) {
661
+ if (this.options.hoverClassName)
662
+ this.element.removeClassName(this.options.hoverClassName);
663
+ if (this._saving) return;
664
+ this.triggerCallback('onLeaveHover');
638
665
  },
639
666
  loadExternalText: function() {
640
- Element.addClassName(this.form, this.options.loadingClassName);
641
- this.editField.disabled = true;
642
- new Ajax.Request(
643
- this.options.loadTextURL,
644
- Object.extend({
645
- asynchronous: true,
646
- onComplete: this.onLoadedExternalText.bind(this)
647
- }, this.options.ajaxOptions)
648
- );
649
- },
650
- onLoadedExternalText: function(transport) {
651
- Element.removeClassName(this.form, this.options.loadingClassName);
652
- this.editField.disabled = false;
653
- this.editField.value = transport.responseText.stripTags();
654
- Field.scrollFreeActivate(this.editField);
655
- },
656
- onclickCancel: function() {
657
- this.onComplete();
658
- this.leaveEditMode();
659
- return false;
660
- },
661
- onFailure: function(transport) {
662
- this.options.onFailure(transport);
663
- if (this.oldInnerHTML) {
664
- this.element.innerHTML = this.oldInnerHTML;
665
- this.oldInnerHTML = null;
666
- }
667
- return false;
668
- },
669
- onSubmit: function() {
670
- // onLoading resets these so we need to save them away for the Ajax call
671
- var form = this.form;
672
- var value = this.editField.value;
673
-
674
- // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
675
- // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
676
- // to be displayed indefinitely
677
- this.onLoading();
678
-
679
- if (this.options.evalScripts) {
680
- new Ajax.Request(
681
- this.url, Object.extend({
682
- parameters: this.options.callback(form, value),
683
- onComplete: this.onComplete.bind(this),
684
- onFailure: this.onFailure.bind(this),
685
- asynchronous:true,
686
- evalScripts:true
687
- }, this.options.ajaxOptions));
688
- } else {
689
- new Ajax.Updater(
690
- { success: this.element,
691
- // don't update on failure (this could be an option)
692
- failure: null },
693
- this.url, Object.extend({
694
- parameters: this.options.callback(form, value),
695
- onComplete: this.onComplete.bind(this),
696
- onFailure: this.onFailure.bind(this)
697
- }, this.options.ajaxOptions));
698
- }
699
- // stop the event to avoid a page refresh in Safari
700
- if (arguments.length > 1) {
701
- Event.stop(arguments[0]);
702
- }
703
- return false;
704
- },
705
- onLoading: function() {
706
- this.saving = true;
667
+ this._form.addClassName(this.options.loadingClassName);
668
+ this._controls.editor.disabled = true;
669
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
670
+ Object.extend(options, {
671
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
672
+ onComplete: Prototype.emptyFunction,
673
+ onSuccess: function(transport) {
674
+ this._form.removeClassName(this.options.loadingClassName);
675
+ var text = transport.responseText;
676
+ if (this.options.stripLoadedTextTags)
677
+ text = text.stripTags();
678
+ this._controls.editor.value = text;
679
+ this._controls.editor.disabled = false;
680
+ this.postProcessEditField();
681
+ }.bind(this),
682
+ onFailure: this._boundFailureHandler
683
+ });
684
+ new Ajax.Request(this.options.loadTextURL, options);
685
+ },
686
+ postProcessEditField: function() {
687
+ var fpc = this.options.fieldPostCreation;
688
+ if (fpc)
689
+ $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
690
+ },
691
+ prepareOptions: function() {
692
+ this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
693
+ Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
694
+ [this._extraDefaultOptions].flatten().compact().each(function(defs) {
695
+ Object.extend(this.options, defs);
696
+ }.bind(this));
697
+ },
698
+ prepareSubmission: function() {
699
+ this._saving = true;
707
700
  this.removeForm();
708
701
  this.leaveHover();
709
702
  this.showSaving();
710
703
  },
704
+ registerListeners: function() {
705
+ this._listeners = { };
706
+ var listener;
707
+ $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
708
+ listener = this[pair.value].bind(this);
709
+ this._listeners[pair.key] = listener;
710
+ if (!this.options.externalControlOnly)
711
+ this.element.observe(pair.key, listener);
712
+ if (this.options.externalControl)
713
+ this.options.externalControl.observe(pair.key, listener);
714
+ }.bind(this));
715
+ },
716
+ removeForm: function() {
717
+ if (!this._form) return;
718
+ this._form.remove();
719
+ this._form = null;
720
+ this._controls = { };
721
+ },
711
722
  showSaving: function() {
712
- this.oldInnerHTML = this.element.innerHTML;
723
+ this._oldInnerHTML = this.element.innerHTML;
713
724
  this.element.innerHTML = this.options.savingText;
714
- Element.addClassName(this.element, this.options.savingClassName);
715
- this.element.style.backgroundColor = this.originalBackground;
716
- Element.show(this.element);
725
+ this.element.addClassName(this.options.savingClassName);
726
+ this.element.style.backgroundColor = this._originalBackground;
727
+ this.element.show();
717
728
  },
718
- removeForm: function() {
719
- if(this.form) {
720
- if (this.form.parentNode) Element.remove(this.form);
721
- this.form = null;
729
+ triggerCallback: function(cbName, arg) {
730
+ if ('function' == typeof this.options[cbName]) {
731
+ this.options[cbName](this, arg);
722
732
  }
723
733
  },
724
- enterHover: function() {
725
- if (this.saving) return;
726
- this.element.style.backgroundColor = this.options.highlightcolor;
727
- if (this.effect) {
728
- this.effect.cancel();
729
- }
730
- Element.addClassName(this.element, this.options.hoverClassName)
734
+ unregisterListeners: function() {
735
+ $H(this._listeners).each(function(pair) {
736
+ if (!this.options.externalControlOnly)
737
+ this.element.stopObserving(pair.key, pair.value);
738
+ if (this.options.externalControl)
739
+ this.options.externalControl.stopObserving(pair.key, pair.value);
740
+ }.bind(this));
731
741
  },
732
- leaveHover: function() {
733
- if (this.options.backgroundColor) {
734
- this.element.style.backgroundColor = this.oldBackground;
735
- }
736
- Element.removeClassName(this.element, this.options.hoverClassName)
737
- if (this.saving) return;
738
- this.effect = new Effect.Highlight(this.element, {
739
- startcolor: this.options.highlightcolor,
740
- endcolor: this.options.highlightendcolor,
741
- restorecolor: this.originalBackground
742
+ wrapUp: function(transport) {
743
+ this.leaveEditMode();
744
+ // Can't use triggerCallback due to backward compatibility: requires
745
+ // binding + direct element
746
+ this._boundComplete(transport, this.element);
747
+ }
748
+ });
749
+
750
+ Object.extend(Ajax.InPlaceEditor.prototype, {
751
+ dispose: Ajax.InPlaceEditor.prototype.destroy
752
+ });
753
+
754
+ Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
755
+ initialize: function($super, element, url, options) {
756
+ this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
757
+ $super(element, url, options);
758
+ },
759
+
760
+ createEditField: function() {
761
+ var list = document.createElement('select');
762
+ list.name = this.options.paramName;
763
+ list.size = 1;
764
+ this._controls.editor = list;
765
+ this._collection = this.options.collection || [];
766
+ if (this.options.loadCollectionURL)
767
+ this.loadCollection();
768
+ else
769
+ this.checkForExternalText();
770
+ this._form.appendChild(this._controls.editor);
771
+ },
772
+
773
+ loadCollection: function() {
774
+ this._form.addClassName(this.options.loadingClassName);
775
+ this.showLoadingText(this.options.loadingCollectionText);
776
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
777
+ Object.extend(options, {
778
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
779
+ onComplete: Prototype.emptyFunction,
780
+ onSuccess: function(transport) {
781
+ var js = transport.responseText.strip();
782
+ if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
783
+ throw('Server returned an invalid collection representation.');
784
+ this._collection = eval(js);
785
+ this.checkForExternalText();
786
+ }.bind(this),
787
+ onFailure: this.onFailure
742
788
  });
789
+ new Ajax.Request(this.options.loadCollectionURL, options);
743
790
  },
744
- leaveEditMode: function() {
745
- Element.removeClassName(this.element, this.options.savingClassName);
746
- this.removeForm();
747
- this.leaveHover();
748
- this.element.style.backgroundColor = this.originalBackground;
749
- Element.show(this.element);
750
- if (this.options.externalControl) {
751
- Element.show(this.options.externalControl);
791
+
792
+ showLoadingText: function(text) {
793
+ this._controls.editor.disabled = true;
794
+ var tempOption = this._controls.editor.firstChild;
795
+ if (!tempOption) {
796
+ tempOption = document.createElement('option');
797
+ tempOption.value = '';
798
+ this._controls.editor.appendChild(tempOption);
799
+ tempOption.selected = true;
752
800
  }
753
- this.editing = false;
754
- this.saving = false;
755
- this.oldInnerHTML = null;
756
- this.onLeaveEditMode();
801
+ tempOption.update((text || '').stripScripts().stripTags());
757
802
  },
758
- onComplete: function(transport) {
759
- this.leaveEditMode();
760
- this.options.onComplete.bind(this)(transport, this.element);
803
+
804
+ checkForExternalText: function() {
805
+ this._text = this.getText();
806
+ if (this.options.loadTextURL)
807
+ this.loadExternalText();
808
+ else
809
+ this.buildOptionList();
761
810
  },
762
- onEnterEditMode: function() {},
763
- onLeaveEditMode: function() {},
764
- dispose: function() {
765
- if (this.oldInnerHTML) {
766
- this.element.innerHTML = this.oldInnerHTML;
767
- }
768
- this.leaveEditMode();
769
- Event.stopObserving(this.element, 'click', this.onclickListener);
770
- Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
771
- Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
772
- if (this.options.externalControl) {
773
- Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
774
- Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
775
- Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
776
- }
811
+
812
+ loadExternalText: function() {
813
+ this.showLoadingText(this.options.loadingText);
814
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
815
+ Object.extend(options, {
816
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
817
+ onComplete: Prototype.emptyFunction,
818
+ onSuccess: function(transport) {
819
+ this._text = transport.responseText.strip();
820
+ this.buildOptionList();
821
+ }.bind(this),
822
+ onFailure: this.onFailure
823
+ });
824
+ new Ajax.Request(this.options.loadTextURL, options);
825
+ },
826
+
827
+ buildOptionList: function() {
828
+ this._form.removeClassName(this.options.loadingClassName);
829
+ this._collection = this._collection.map(function(entry) {
830
+ return 2 === entry.length ? entry : [entry, entry].flatten();
831
+ });
832
+ var marker = ('value' in this.options) ? this.options.value : this._text;
833
+ var textFound = this._collection.any(function(entry) {
834
+ return entry[0] == marker;
835
+ }.bind(this));
836
+ this._controls.editor.update('');
837
+ var option;
838
+ this._collection.each(function(entry, index) {
839
+ option = document.createElement('option');
840
+ option.value = entry[0];
841
+ option.selected = textFound ? entry[0] == marker : 0 == index;
842
+ option.appendChild(document.createTextNode(entry[1]));
843
+ this._controls.editor.appendChild(option);
844
+ }.bind(this));
845
+ this._controls.editor.disabled = false;
846
+ Field.scrollFreeActivate(this._controls.editor);
777
847
  }
778
- };
848
+ });
779
849
 
780
- Ajax.InPlaceCollectionEditor = Class.create();
781
- Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
782
- Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
783
- createEditField: function() {
784
- if (!this.cached_selectTag) {
785
- var selectTag = document.createElement("select");
786
- var collection = this.options.collection || [];
787
- var optionTag;
788
- collection.each(function(e,i) {
789
- optionTag = document.createElement("option");
790
- optionTag.value = (e instanceof Array) ? e[0] : e;
791
- if((typeof this.options.value == 'undefined') &&
792
- ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
793
- if(this.options.value==optionTag.value) optionTag.selected = true;
794
- optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
795
- selectTag.appendChild(optionTag);
796
- }.bind(this));
797
- this.cached_selectTag = selectTag;
798
- }
850
+ //**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
851
+ //**** This only exists for a while, in order to let ****
852
+ //**** users adapt to the new API. Read up on the new ****
853
+ //**** API and convert your code to it ASAP! ****
854
+
855
+ Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
856
+ if (!options) return;
857
+ function fallback(name, expr) {
858
+ if (name in options || expr === undefined) return;
859
+ options[name] = expr;
860
+ };
861
+ fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
862
+ options.cancelLink == options.cancelButton == false ? false : undefined)));
863
+ fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
864
+ options.okLink == options.okButton == false ? false : undefined)));
865
+ fallback('highlightColor', options.highlightcolor);
866
+ fallback('highlightEndColor', options.highlightendcolor);
867
+ };
799
868
 
800
- this.editField = this.cached_selectTag;
801
- if(this.options.loadTextURL) this.loadExternalText();
802
- this.form.appendChild(this.editField);
803
- this.options.callback = function(form, value) {
804
- return "value=" + encodeURIComponent(value);
869
+ Object.extend(Ajax.InPlaceEditor, {
870
+ DefaultOptions: {
871
+ ajaxOptions: { },
872
+ autoRows: 3, // Use when multi-line w/ rows == 1
873
+ cancelControl: 'link', // 'link'|'button'|false
874
+ cancelText: 'cancel',
875
+ clickToEditText: 'Click to edit',
876
+ externalControl: null, // id|elt
877
+ externalControlOnly: false,
878
+ fieldPostCreation: 'activate', // 'activate'|'focus'|false
879
+ formClassName: 'inplaceeditor-form',
880
+ formId: null, // id|elt
881
+ highlightColor: '#ffff99',
882
+ highlightEndColor: '#ffffff',
883
+ hoverClassName: '',
884
+ htmlResponse: true,
885
+ loadingClassName: 'inplaceeditor-loading',
886
+ loadingText: 'Loading...',
887
+ okControl: 'button', // 'link'|'button'|false
888
+ okText: 'ok',
889
+ paramName: 'value',
890
+ rows: 1, // If 1 and multi-line, uses autoRows
891
+ savingClassName: 'inplaceeditor-saving',
892
+ savingText: 'Saving...',
893
+ size: 0,
894
+ stripLoadedTextTags: false,
895
+ submitOnBlur: false,
896
+ textAfterControls: '',
897
+ textBeforeControls: '',
898
+ textBetweenControls: ''
899
+ },
900
+ DefaultCallbacks: {
901
+ callback: function(form) {
902
+ return Form.serialize(form);
903
+ },
904
+ onComplete: function(transport, element) {
905
+ // For backward compatibility, this one is bound to the IPE, and passes
906
+ // the element directly. It was too often customized, so we don't break it.
907
+ new Effect.Highlight(element, {
908
+ startcolor: this.options.highlightColor, keepBackgroundImage: true });
909
+ },
910
+ onEnterEditMode: null,
911
+ onEnterHover: function(ipe) {
912
+ ipe.element.style.backgroundColor = ipe.options.highlightColor;
913
+ if (ipe._effect)
914
+ ipe._effect.cancel();
915
+ },
916
+ onFailure: function(transport, ipe) {
917
+ alert('Error communication with the server: ' + transport.responseText.stripTags());
918
+ },
919
+ onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
920
+ onLeaveEditMode: null,
921
+ onLeaveHover: function(ipe) {
922
+ ipe._effect = new Effect.Highlight(ipe.element, {
923
+ startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
924
+ restorecolor: ipe._originalBackground, keepBackgroundImage: true
925
+ });
805
926
  }
927
+ },
928
+ Listeners: {
929
+ click: 'enterEditMode',
930
+ keydown: 'checkForEscapeOrReturn',
931
+ mouseover: 'enterHover',
932
+ mouseout: 'leaveHover'
806
933
  }
807
934
  });
808
935
 
809
- // Delayed observer, like Form.Element.Observer,
936
+ Ajax.InPlaceCollectionEditor.DefaultOptions = {
937
+ loadingCollectionText: 'Loading options...'
938
+ };
939
+
940
+ // Delayed observer, like Form.Element.Observer,
810
941
  // but waits for delay after last key input
811
942
  // Ideal for live-search fields
812
943
 
813
- Form.Element.DelayedObserver = Class.create();
814
- Form.Element.DelayedObserver.prototype = {
944
+ Form.Element.DelayedObserver = Class.create({
815
945
  initialize: function(element, delay, callback) {
816
946
  this.delay = delay || 0.5;
817
947
  this.element = $(element);
818
948
  this.callback = callback;
819
949
  this.timer = null;
820
- this.lastValue = $F(this.element);
950
+ this.lastValue = $F(this.element);
821
951
  Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
822
952
  },
823
953
  delayedListener: function(event) {
@@ -830,4 +960,4 @@ Form.Element.DelayedObserver.prototype = {
830
960
  this.timer = null;
831
961
  this.callback(this.element, $F(this.element));
832
962
  }
833
- };
963
+ });