parlement 0.14 → 0.17

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 (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
+ });