parlement 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. data/CHANGES +709 -0
  2. data/COPYING +223 -0
  3. data/README +20 -0
  4. data/Rakefile +136 -0
  5. data/app/controllers/account_controller.rb +181 -0
  6. data/app/controllers/application.rb +30 -0
  7. data/app/controllers/elt_controller.rb +83 -0
  8. data/app/helpers/account_helper.rb +2 -0
  9. data/app/helpers/application_helper.rb +4 -0
  10. data/app/helpers/elt_helper.rb +37 -0
  11. data/app/helpers/live_tree.rb +238 -0
  12. data/app/helpers/mailman.rb +96 -0
  13. data/app/models/attachment.rb +4 -0
  14. data/app/models/elt.rb +17 -0
  15. data/app/models/mail.rb +4 -0
  16. data/app/models/notifier.rb +13 -0
  17. data/app/models/person.rb +9 -0
  18. data/app/models/user.rb +7 -0
  19. data/app/models/user_notify.rb +75 -0
  20. data/app/views/account/_help.rhtml +23 -0
  21. data/app/views/account/_login.rhtml +57 -0
  22. data/app/views/account/_show.rhtml +31 -0
  23. data/app/views/account/logout.rhtml +10 -0
  24. data/app/views/account/signup.rhtml +17 -0
  25. data/app/views/account/welcome.rhtml +13 -0
  26. data/app/views/elt/_elt.rhtml +105 -0
  27. data/app/views/elt/_form.rhtml +31 -0
  28. data/app/views/elt/_list.rhtml +28 -0
  29. data/app/views/elt/new.rhtml +102 -0
  30. data/app/views/elt/rss.rxml +31 -0
  31. data/app/views/elt/show.rhtml +46 -0
  32. data/app/views/elt/show_tree.rhtml +8 -0
  33. data/app/views/layouts/scaffold.rhtml +13 -0
  34. data/app/views/layouts/top.rhtml +45 -0
  35. data/app/views/notifier/changeEmail.rhtml +10 -0
  36. data/config/boot.rb +17 -0
  37. data/config/database.yml +82 -0
  38. data/config/environment.rb +92 -0
  39. data/config/environments/development.rb +17 -0
  40. data/config/environments/production.rb +17 -0
  41. data/config/environments/test.rb +17 -0
  42. data/config/environments/user_environment.rb +1 -0
  43. data/config/routes.rb +28 -0
  44. data/db/ROOT/CV.txt +166 -0
  45. data/db/ROOT/IP.txt +3 -0
  46. data/db/ROOT/parleR.txt +3 -0
  47. data/db/ROOT/parlement/security.txt +34 -0
  48. data/db/ROOT/parlement/test.txt +4 -0
  49. data/db/ROOT/parlement.txt +51 -0
  50. data/db/ROOT/perso.txt +215 -0
  51. data/db/schema.sql +127 -0
  52. data/lib/data_import.rb +54 -0
  53. data/lib/file_column.rb +263 -0
  54. data/lib/file_column_helper.rb +45 -0
  55. data/lib/localization.rb +88 -0
  56. data/lib/localizer.rb +88 -0
  57. data/lib/login_system.rb +87 -0
  58. data/lib/rails_file_column.rb +19 -0
  59. data/lib/user_system.rb +101 -0
  60. data/public/404.html +8 -0
  61. data/public/500.html +8 -0
  62. data/public/dispatch.cgi +10 -0
  63. data/public/dispatch.fcgi +24 -0
  64. data/public/dispatch.rb +10 -0
  65. data/public/engine_files/README +5 -0
  66. data/public/engine_files/login_engine/stylesheets/login_engine.css +81 -0
  67. data/public/favicon.ico +0 -0
  68. data/public/favicon.png +0 -0
  69. data/public/images/live_tree_branch_collapsed_icon.gif +0 -0
  70. data/public/images/live_tree_branch_expanded_icon.gif +0 -0
  71. data/public/images/live_tree_leaf_icon.gif +0 -0
  72. data/public/images/live_tree_loading_spinner.gif +0 -0
  73. data/public/images/webfeed.gif +0 -0
  74. data/public/javascripts/controls.js +721 -0
  75. data/public/javascripts/dragdrop.js +519 -0
  76. data/public/javascripts/effects.js +992 -0
  77. data/public/javascripts/live_tree.js +749 -0
  78. data/public/javascripts/prototype.js +1726 -0
  79. data/public/javascripts/scriptaculous.js +47 -0
  80. data/public/javascripts/slider.js +258 -0
  81. data/public/oldREADME +190 -0
  82. data/public/oldindex.html +78 -0
  83. data/public/robots.txt +1 -0
  84. data/public/stylesheets/default.css +238 -0
  85. data/public/stylesheets/live_tree.css +62 -0
  86. data/public/stylesheets/scaffold.css +74 -0
  87. data/script/about +3 -0
  88. data/script/benchmarker +19 -0
  89. data/script/breakpointer +3 -0
  90. data/script/console +3 -0
  91. data/script/create_db +7 -0
  92. data/script/destroy +3 -0
  93. data/script/generate +3 -0
  94. data/script/performance/benchmarker +3 -0
  95. data/script/performance/profiler +3 -0
  96. data/script/plugin +3 -0
  97. data/script/process/reaper +3 -0
  98. data/script/process/spawner +3 -0
  99. data/script/process/spinner +3 -0
  100. data/script/profiler +34 -0
  101. data/script/runner +3 -0
  102. data/script/server +3 -0
  103. data/test/fixtures/attachments.yml +10 -0
  104. data/test/fixtures/elts.yml +15 -0
  105. data/test/fixtures/mails.yml +7 -0
  106. data/test/fixtures/people.yml +49 -0
  107. data/test/fixtures/users.yml +41 -0
  108. data/test/functional/account_controller_test.rb +239 -0
  109. data/test/functional/elt_controller_test.rb +18 -0
  110. data/test/mocks/test/time.rb +17 -0
  111. data/test/mocks/test/user_notify.rb +16 -0
  112. data/test/test_helper.rb +28 -0
  113. data/test/unit/attachment_test.rb +14 -0
  114. data/test/unit/elt_test.rb +14 -0
  115. data/test/unit/mail_test.rb +14 -0
  116. data/test/unit/notifier_test.rb +31 -0
  117. data/test/unit/person_test.rb +24 -0
  118. data/test/unit/user_test.rb +94 -0
  119. data/vendor/plugins/engines/CHANGELOG +7 -0
  120. data/vendor/plugins/engines/README +128 -0
  121. data/vendor/plugins/engines/init.rb +33 -0
  122. data/vendor/plugins/engines/lib/action_mailer_extensions.rb +160 -0
  123. data/vendor/plugins/engines/lib/action_view_extensions.rb +130 -0
  124. data/vendor/plugins/engines/lib/dependencies_extensions.rb +56 -0
  125. data/vendor/plugins/engines/lib/engines.rb +292 -0
  126. data/vendor/plugins/engines/lib/ruby_extensions.rb +127 -0
  127. data/vendor/plugins/engines/lib/testing_extensions.rb +33 -0
  128. data/vendor/plugins/engines/test/ruby_extensions_test.rb +94 -0
  129. data/vendor/plugins/login_engine/README +258 -0
  130. data/vendor/plugins/login_engine/app/controllers/user_controller.rb +248 -0
  131. data/vendor/plugins/login_engine/app/helpers/user_helper.rb +88 -0
  132. data/vendor/plugins/login_engine/app/models/user.rb +7 -0
  133. data/vendor/plugins/login_engine/app/models/user_notify.rb +75 -0
  134. data/vendor/plugins/login_engine/app/views/user/_edit.rhtml +11 -0
  135. data/vendor/plugins/login_engine/app/views/user/_password.rhtml +9 -0
  136. data/vendor/plugins/login_engine/app/views/user/change_password.rhtml +17 -0
  137. data/vendor/plugins/login_engine/app/views/user/edit.rhtml +23 -0
  138. data/vendor/plugins/login_engine/app/views/user/forgot_password.rhtml +18 -0
  139. data/vendor/plugins/login_engine/app/views/user/home.rhtml +7 -0
  140. data/vendor/plugins/login_engine/app/views/user/login.rhtml +17 -0
  141. data/vendor/plugins/login_engine/app/views/user/logout.rhtml +8 -0
  142. data/vendor/plugins/login_engine/app/views/user/signup.rhtml +17 -0
  143. data/vendor/plugins/login_engine/app/views/user_notify/change_password.rhtml +10 -0
  144. data/vendor/plugins/login_engine/app/views/user_notify/delete.rhtml +5 -0
  145. data/vendor/plugins/login_engine/app/views/user_notify/forgot_password.rhtml +11 -0
  146. data/vendor/plugins/login_engine/app/views/user_notify/pending_delete.rhtml +9 -0
  147. data/vendor/plugins/login_engine/app/views/user_notify/signup.rhtml +12 -0
  148. data/vendor/plugins/login_engine/db/schema.rb +25 -0
  149. data/vendor/plugins/login_engine/init_engine.rb +10 -0
  150. data/vendor/plugins/login_engine/lib/login_engine/authenticated_system.rb +107 -0
  151. data/vendor/plugins/login_engine/lib/login_engine/authenticated_user.rb +149 -0
  152. data/vendor/plugins/login_engine/lib/login_engine.rb +58 -0
  153. data/vendor/plugins/login_engine/public/stylesheets/login_engine.css +81 -0
  154. data/vendor/plugins/login_engine/tasks/tasks.rake +4 -0
  155. data/vendor/plugins/login_engine/test/fixtures/templates/users.yml +41 -0
  156. data/vendor/plugins/login_engine/test/fixtures/users.yml +41 -0
  157. data/vendor/plugins/login_engine/test/functional/user_controller_test.rb +533 -0
  158. data/vendor/plugins/login_engine/test/mocks/mail.rb +14 -0
  159. data/vendor/plugins/login_engine/test/mocks/time.rb +19 -0
  160. data/vendor/plugins/login_engine/test/test_helper.rb +15 -0
  161. data/vendor/plugins/login_engine/test/unit/user_test.rb +94 -0
  162. metadata +276 -0
@@ -0,0 +1,721 @@
1
+ // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2
+ // (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3
+ // (c) 2005 Jon Tirsen (http://www.tirsen.com)
4
+ // Contributors:
5
+ // Richard Livsey
6
+ // Rahul Bhargava
7
+ // Rob Wills
8
+ //
9
+ // See scriptaculous.js for full license.
10
+
11
+ // Autocompleter.Base handles all the autocompletion functionality
12
+ // that's independent of the data source for autocompletion. This
13
+ // includes drawing the autocompletion menu, observing keyboard
14
+ // and mouse events, and similar.
15
+ //
16
+ // Specific autocompleters need to provide, at the very least,
17
+ // a getUpdatedChoices function that will be invoked every time
18
+ // the text inside the monitored textbox changes. This method
19
+ // should get the text for which to provide autocompletion by
20
+ // invoking this.getToken(), NOT by directly accessing
21
+ // this.element.value. This is to allow incremental tokenized
22
+ // autocompletion. Specific auto-completion logic (AJAX, etc)
23
+ // belongs in getUpdatedChoices.
24
+ //
25
+ // Tokenized incremental autocompletion is enabled automatically
26
+ // when an autocompleter is instantiated with the 'tokens' option
27
+ // in the options parameter, e.g.:
28
+ // new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
29
+ // will incrementally autocomplete with a comma as the token.
30
+ // Additionally, ',' in the above example can be replaced with
31
+ // a token array, e.g. { tokens: [',', '\n'] } which
32
+ // enables autocompletion on multiple tokens. This is most
33
+ // useful when one of the tokens is \n (a newline), as it
34
+ // allows smart autocompletion after linebreaks.
35
+
36
+ var Autocompleter = {}
37
+ Autocompleter.Base = function() {};
38
+ Autocompleter.Base.prototype = {
39
+ baseInitialize: function(element, update, options) {
40
+ this.element = $(element);
41
+ this.update = $(update);
42
+ this.hasFocus = false;
43
+ this.changed = false;
44
+ this.active = false;
45
+ this.index = 0;
46
+ this.entryCount = 0;
47
+
48
+ if (this.setOptions)
49
+ this.setOptions(options);
50
+ else
51
+ this.options = options || {};
52
+
53
+ this.options.paramName = this.options.paramName || this.element.name;
54
+ this.options.tokens = this.options.tokens || [];
55
+ this.options.frequency = this.options.frequency || 0.4;
56
+ this.options.minChars = this.options.minChars || 1;
57
+ this.options.onShow = this.options.onShow ||
58
+ function(element, update){
59
+ if(!update.style.position || update.style.position=='absolute') {
60
+ update.style.position = 'absolute';
61
+ Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
62
+ }
63
+ Effect.Appear(update,{duration:0.15});
64
+ };
65
+ this.options.onHide = this.options.onHide ||
66
+ function(element, update){ new Effect.Fade(update,{duration:0.15}) };
67
+
68
+ if (typeof(this.options.tokens) == 'string')
69
+ this.options.tokens = new Array(this.options.tokens);
70
+
71
+ this.observer = null;
72
+
73
+ this.element.setAttribute('autocomplete','off');
74
+
75
+ Element.hide(this.update);
76
+
77
+ Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
78
+ Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
79
+ },
80
+
81
+ show: function() {
82
+ if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
83
+ if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && (Element.getStyle(this.update, 'position')=='absolute')) {
84
+ new Insertion.After(this.update,
85
+ '<iframe id="' + this.update.id + '_iefix" '+
86
+ 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
87
+ 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
88
+ this.iefix = $(this.update.id+'_iefix');
89
+ }
90
+ if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
91
+ },
92
+
93
+ fixIEOverlapping: function() {
94
+ Position.clone(this.update, this.iefix);
95
+ this.iefix.style.zIndex = 1;
96
+ this.update.style.zIndex = 2;
97
+ Element.show(this.iefix);
98
+ },
99
+
100
+ hide: function() {
101
+ this.stopIndicator();
102
+ if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
103
+ if(this.iefix) Element.hide(this.iefix);
104
+ },
105
+
106
+ startIndicator: function() {
107
+ if(this.options.indicator) Element.show(this.options.indicator);
108
+ },
109
+
110
+ stopIndicator: function() {
111
+ if(this.options.indicator) Element.hide(this.options.indicator);
112
+ },
113
+
114
+ onKeyPress: function(event) {
115
+ if(this.active)
116
+ switch(event.keyCode) {
117
+ case Event.KEY_TAB:
118
+ case Event.KEY_RETURN:
119
+ this.selectEntry();
120
+ Event.stop(event);
121
+ case Event.KEY_ESC:
122
+ this.hide();
123
+ this.active = false;
124
+ Event.stop(event);
125
+ return;
126
+ case Event.KEY_LEFT:
127
+ case Event.KEY_RIGHT:
128
+ return;
129
+ case Event.KEY_UP:
130
+ this.markPrevious();
131
+ this.render();
132
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
133
+ return;
134
+ case Event.KEY_DOWN:
135
+ this.markNext();
136
+ this.render();
137
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
138
+ return;
139
+ }
140
+ else
141
+ if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN)
142
+ return;
143
+
144
+ this.changed = true;
145
+ this.hasFocus = true;
146
+
147
+ if(this.observer) clearTimeout(this.observer);
148
+ this.observer =
149
+ setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
150
+ },
151
+
152
+ onHover: function(event) {
153
+ var element = Event.findElement(event, 'LI');
154
+ if(this.index != element.autocompleteIndex)
155
+ {
156
+ this.index = element.autocompleteIndex;
157
+ this.render();
158
+ }
159
+ Event.stop(event);
160
+ },
161
+
162
+ onClick: function(event) {
163
+ var element = Event.findElement(event, 'LI');
164
+ this.index = element.autocompleteIndex;
165
+ this.selectEntry();
166
+ this.hide();
167
+ },
168
+
169
+ onBlur: function(event) {
170
+ // needed to make click events working
171
+ setTimeout(this.hide.bind(this), 250);
172
+ this.hasFocus = false;
173
+ this.active = false;
174
+ },
175
+
176
+ render: function() {
177
+ if(this.entryCount > 0) {
178
+ for (var i = 0; i < this.entryCount; i++)
179
+ this.index==i ?
180
+ Element.addClassName(this.getEntry(i),"selected") :
181
+ Element.removeClassName(this.getEntry(i),"selected");
182
+
183
+ if(this.hasFocus) {
184
+ this.show();
185
+ this.active = true;
186
+ }
187
+ } else {
188
+ this.active = false;
189
+ this.hide();
190
+ }
191
+ },
192
+
193
+ markPrevious: function() {
194
+ if(this.index > 0) this.index--
195
+ else this.index = this.entryCount-1;
196
+ },
197
+
198
+ markNext: function() {
199
+ if(this.index < this.entryCount-1) this.index++
200
+ else this.index = 0;
201
+ },
202
+
203
+ getEntry: function(index) {
204
+ return this.update.firstChild.childNodes[index];
205
+ },
206
+
207
+ getCurrentEntry: function() {
208
+ return this.getEntry(this.index);
209
+ },
210
+
211
+ selectEntry: function() {
212
+ this.active = false;
213
+ this.updateElement(this.getCurrentEntry());
214
+ },
215
+
216
+ updateElement: function(selectedElement) {
217
+ if (this.options.updateElement) {
218
+ this.options.updateElement(selectedElement);
219
+ return;
220
+ }
221
+
222
+ var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
223
+ var lastTokenPos = this.findLastToken();
224
+ if (lastTokenPos != -1) {
225
+ var newValue = this.element.value.substr(0, lastTokenPos + 1);
226
+ var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
227
+ if (whitespace)
228
+ newValue += whitespace[0];
229
+ this.element.value = newValue + value;
230
+ } else {
231
+ this.element.value = value;
232
+ }
233
+ this.element.focus();
234
+
235
+ if (this.options.afterUpdateElement)
236
+ this.options.afterUpdateElement(this.element, selectedElement);
237
+ },
238
+
239
+ updateChoices: function(choices) {
240
+ if(!this.changed && this.hasFocus) {
241
+ this.update.innerHTML = choices;
242
+ Element.cleanWhitespace(this.update);
243
+ Element.cleanWhitespace(this.update.firstChild);
244
+
245
+ if(this.update.firstChild && this.update.firstChild.childNodes) {
246
+ this.entryCount =
247
+ this.update.firstChild.childNodes.length;
248
+ for (var i = 0; i < this.entryCount; i++) {
249
+ var entry = this.getEntry(i);
250
+ entry.autocompleteIndex = i;
251
+ this.addObservers(entry);
252
+ }
253
+ } else {
254
+ this.entryCount = 0;
255
+ }
256
+
257
+ this.stopIndicator();
258
+
259
+ this.index = 0;
260
+ this.render();
261
+ }
262
+ },
263
+
264
+ addObservers: function(element) {
265
+ Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
266
+ Event.observe(element, "click", this.onClick.bindAsEventListener(this));
267
+ },
268
+
269
+ onObserverEvent: function() {
270
+ this.changed = false;
271
+ if(this.getToken().length>=this.options.minChars) {
272
+ this.startIndicator();
273
+ this.getUpdatedChoices();
274
+ } else {
275
+ this.active = false;
276
+ this.hide();
277
+ }
278
+ },
279
+
280
+ getToken: function() {
281
+ var tokenPos = this.findLastToken();
282
+ if (tokenPos != -1)
283
+ var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
284
+ else
285
+ var ret = this.element.value;
286
+
287
+ return /\n/.test(ret) ? '' : ret;
288
+ },
289
+
290
+ findLastToken: function() {
291
+ var lastTokenPos = -1;
292
+
293
+ for (var i=0; i<this.options.tokens.length; i++) {
294
+ var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
295
+ if (thisTokenPos > lastTokenPos)
296
+ lastTokenPos = thisTokenPos;
297
+ }
298
+ return lastTokenPos;
299
+ }
300
+ }
301
+
302
+ Ajax.Autocompleter = Class.create();
303
+ Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
304
+ initialize: function(element, update, url, options) {
305
+ this.baseInitialize(element, update, options);
306
+ this.options.asynchronous = true;
307
+ this.options.onComplete = this.onComplete.bind(this);
308
+ this.options.defaultParams = this.options.parameters || null;
309
+ this.url = url;
310
+ },
311
+
312
+ getUpdatedChoices: function() {
313
+ entry = encodeURIComponent(this.options.paramName) + '=' +
314
+ encodeURIComponent(this.getToken());
315
+
316
+ this.options.parameters = this.options.callback ?
317
+ this.options.callback(this.element, entry) : entry;
318
+
319
+ if(this.options.defaultParams)
320
+ this.options.parameters += '&' + this.options.defaultParams;
321
+
322
+ new Ajax.Request(this.url, this.options);
323
+ },
324
+
325
+ onComplete: function(request) {
326
+ this.updateChoices(request.responseText);
327
+ }
328
+
329
+ });
330
+
331
+ // The local array autocompleter. Used when you'd prefer to
332
+ // inject an array of autocompletion options into the page, rather
333
+ // than sending out Ajax queries, which can be quite slow sometimes.
334
+ //
335
+ // The constructor takes four parameters. The first two are, as usual,
336
+ // the id of the monitored textbox, and id of the autocompletion menu.
337
+ // The third is the array you want to autocomplete from, and the fourth
338
+ // is the options block.
339
+ //
340
+ // Extra local autocompletion options:
341
+ // - choices - How many autocompletion choices to offer
342
+ //
343
+ // - partialSearch - If false, the autocompleter will match entered
344
+ // text only at the beginning of strings in the
345
+ // autocomplete array. Defaults to true, which will
346
+ // match text at the beginning of any *word* in the
347
+ // strings in the autocomplete array. If you want to
348
+ // search anywhere in the string, additionally set
349
+ // the option fullSearch to true (default: off).
350
+ //
351
+ // - fullSsearch - Search anywhere in autocomplete array strings.
352
+ //
353
+ // - partialChars - How many characters to enter before triggering
354
+ // a partial match (unlike minChars, which defines
355
+ // how many characters are required to do any match
356
+ // at all). Defaults to 2.
357
+ //
358
+ // - ignoreCase - Whether to ignore case when autocompleting.
359
+ // Defaults to true.
360
+ //
361
+ // It's possible to pass in a custom function as the 'selector'
362
+ // option, if you prefer to write your own autocompletion logic.
363
+ // In that case, the other options above will not apply unless
364
+ // you support them.
365
+
366
+ Autocompleter.Local = Class.create();
367
+ Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
368
+ initialize: function(element, update, array, options) {
369
+ this.baseInitialize(element, update, options);
370
+ this.options.array = array;
371
+ },
372
+
373
+ getUpdatedChoices: function() {
374
+ this.updateChoices(this.options.selector(this));
375
+ },
376
+
377
+ setOptions: function(options) {
378
+ this.options = Object.extend({
379
+ choices: 10,
380
+ partialSearch: true,
381
+ partialChars: 2,
382
+ ignoreCase: true,
383
+ fullSearch: false,
384
+ selector: function(instance) {
385
+ var ret = []; // Beginning matches
386
+ var partial = []; // Inside matches
387
+ var entry = instance.getToken();
388
+ var count = 0;
389
+
390
+ for (var i = 0; i < instance.options.array.length &&
391
+ ret.length < instance.options.choices ; i++) {
392
+
393
+ var elem = instance.options.array[i];
394
+ var foundPos = instance.options.ignoreCase ?
395
+ elem.toLowerCase().indexOf(entry.toLowerCase()) :
396
+ elem.indexOf(entry);
397
+
398
+ while (foundPos != -1) {
399
+ if (foundPos == 0 && elem.length != entry.length) {
400
+ ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
401
+ elem.substr(entry.length) + "</li>");
402
+ break;
403
+ } else if (entry.length >= instance.options.partialChars &&
404
+ instance.options.partialSearch && foundPos != -1) {
405
+ if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
406
+ partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
407
+ elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
408
+ foundPos + entry.length) + "</li>");
409
+ break;
410
+ }
411
+ }
412
+
413
+ foundPos = instance.options.ignoreCase ?
414
+ elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
415
+ elem.indexOf(entry, foundPos + 1);
416
+
417
+ }
418
+ }
419
+ if (partial.length)
420
+ ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
421
+ return "<ul>" + ret.join('') + "</ul>";
422
+ }
423
+ }, options || {});
424
+ }
425
+ });
426
+
427
+ // AJAX in-place editor
428
+ //
429
+ // see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
430
+
431
+ // Use this if you notice weird scrolling problems on some browsers,
432
+ // the DOM might be a bit confused when this gets called so do this
433
+ // waits 1 ms (with setTimeout) until it does the activation
434
+ Field.scrollFreeActivate = function(field) {
435
+ setTimeout(function() {
436
+ Field.activate(field);
437
+ }, 1);
438
+ }
439
+
440
+ Ajax.InPlaceEditor = Class.create();
441
+ Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
442
+ Ajax.InPlaceEditor.prototype = {
443
+ initialize: function(element, url, options) {
444
+ this.url = url;
445
+ this.element = $(element);
446
+
447
+ this.options = Object.extend({
448
+ okText: "ok",
449
+ cancelText: "cancel",
450
+ savingText: "Saving...",
451
+ clickToEditText: "Click to edit",
452
+ okText: "ok",
453
+ rows: 1,
454
+ onComplete: function(transport, element) {
455
+ new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
456
+ },
457
+ onFailure: function(transport) {
458
+ alert("Error communicating with the server: " + transport.responseText.stripTags());
459
+ },
460
+ callback: function(form) {
461
+ return Form.serialize(form);
462
+ },
463
+ handleLineBreaks: true,
464
+ loadingText: 'Loading...',
465
+ savingClassName: 'inplaceeditor-saving',
466
+ loadingClassName: 'inplaceeditor-loading',
467
+ formClassName: 'inplaceeditor-form',
468
+ highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
469
+ highlightendcolor: "#FFFFFF",
470
+ externalControl: null,
471
+ ajaxOptions: {}
472
+ }, options || {});
473
+
474
+ if(!this.options.formId && this.element.id) {
475
+ this.options.formId = this.element.id + "-inplaceeditor";
476
+ if ($(this.options.formId)) {
477
+ // there's already a form with that name, don't specify an id
478
+ this.options.formId = null;
479
+ }
480
+ }
481
+
482
+ if (this.options.externalControl) {
483
+ this.options.externalControl = $(this.options.externalControl);
484
+ }
485
+
486
+ this.originalBackground = Element.getStyle(this.element, 'background-color');
487
+ if (!this.originalBackground) {
488
+ this.originalBackground = "transparent";
489
+ }
490
+
491
+ this.element.title = this.options.clickToEditText;
492
+
493
+ this.onclickListener = this.enterEditMode.bindAsEventListener(this);
494
+ this.mouseoverListener = this.enterHover.bindAsEventListener(this);
495
+ this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
496
+ Event.observe(this.element, 'click', this.onclickListener);
497
+ Event.observe(this.element, 'mouseover', this.mouseoverListener);
498
+ Event.observe(this.element, 'mouseout', this.mouseoutListener);
499
+ if (this.options.externalControl) {
500
+ Event.observe(this.options.externalControl, 'click', this.onclickListener);
501
+ Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
502
+ Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
503
+ }
504
+ },
505
+ enterEditMode: function(evt) {
506
+ if (this.saving) return;
507
+ if (this.editing) return;
508
+ this.editing = true;
509
+ this.onEnterEditMode();
510
+ if (this.options.externalControl) {
511
+ Element.hide(this.options.externalControl);
512
+ }
513
+ Element.hide(this.element);
514
+ this.createForm();
515
+ this.element.parentNode.insertBefore(this.form, this.element);
516
+ Field.scrollFreeActivate(this.editField);
517
+ // stop the event to avoid a page refresh in Safari
518
+ if (evt) {
519
+ Event.stop(evt);
520
+ }
521
+ return false;
522
+ },
523
+ createForm: function() {
524
+ this.form = document.createElement("form");
525
+ this.form.id = this.options.formId;
526
+ Element.addClassName(this.form, this.options.formClassName)
527
+ this.form.onsubmit = this.onSubmit.bind(this);
528
+
529
+ this.createEditField();
530
+
531
+ if (this.options.textarea) {
532
+ var br = document.createElement("br");
533
+ this.form.appendChild(br);
534
+ }
535
+
536
+ okButton = document.createElement("input");
537
+ okButton.type = "submit";
538
+ okButton.value = this.options.okText;
539
+ this.form.appendChild(okButton);
540
+
541
+ cancelLink = document.createElement("a");
542
+ cancelLink.href = "#";
543
+ cancelLink.appendChild(document.createTextNode(this.options.cancelText));
544
+ cancelLink.onclick = this.onclickCancel.bind(this);
545
+ this.form.appendChild(cancelLink);
546
+ },
547
+ hasHTMLLineBreaks: function(string) {
548
+ if (!this.options.handleLineBreaks) return false;
549
+ return string.match(/<br/i) || string.match(/<p>/i);
550
+ },
551
+ convertHTMLLineBreaks: function(string) {
552
+ return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
553
+ },
554
+ createEditField: function() {
555
+ var text;
556
+ if(this.options.loadTextURL) {
557
+ text = this.options.loadingText;
558
+ } else {
559
+ text = this.getText();
560
+ }
561
+
562
+ if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
563
+ this.options.textarea = false;
564
+ var textField = document.createElement("input");
565
+ textField.type = "text";
566
+ textField.name = "value";
567
+ textField.value = text;
568
+ textField.style.backgroundColor = this.options.highlightcolor;
569
+ var size = this.options.size || this.options.cols || 0;
570
+ if (size != 0) textField.size = size;
571
+ this.editField = textField;
572
+ } else {
573
+ this.options.textarea = true;
574
+ var textArea = document.createElement("textarea");
575
+ textArea.name = "value";
576
+ textArea.value = this.convertHTMLLineBreaks(text);
577
+ textArea.rows = this.options.rows;
578
+ textArea.cols = this.options.cols || 40;
579
+ this.editField = textArea;
580
+ }
581
+
582
+ if(this.options.loadTextURL) {
583
+ this.loadExternalText();
584
+ }
585
+ this.form.appendChild(this.editField);
586
+ },
587
+ getText: function() {
588
+ return this.element.innerHTML;
589
+ },
590
+ loadExternalText: function() {
591
+ Element.addClassName(this.form, this.options.loadingClassName);
592
+ this.editField.disabled = true;
593
+ new Ajax.Request(
594
+ this.options.loadTextURL,
595
+ Object.extend({
596
+ asynchronous: true,
597
+ onComplete: this.onLoadedExternalText.bind(this)
598
+ }, this.options.ajaxOptions)
599
+ );
600
+ },
601
+ onLoadedExternalText: function(transport) {
602
+ Element.removeClassName(this.form, this.options.loadingClassName);
603
+ this.editField.disabled = false;
604
+ this.editField.value = transport.responseText.stripTags();
605
+ },
606
+ onclickCancel: function() {
607
+ this.onComplete();
608
+ this.leaveEditMode();
609
+ return false;
610
+ },
611
+ onFailure: function(transport) {
612
+ this.options.onFailure(transport);
613
+ if (this.oldInnerHTML) {
614
+ this.element.innerHTML = this.oldInnerHTML;
615
+ this.oldInnerHTML = null;
616
+ }
617
+ return false;
618
+ },
619
+ onSubmit: function() {
620
+ // onLoading resets these so we need to save them away for the Ajax call
621
+ var form = this.form;
622
+ var value = this.editField.value;
623
+
624
+ // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
625
+ // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
626
+ // to be displayed indefinitely
627
+ this.onLoading();
628
+
629
+ new Ajax.Updater(
630
+ {
631
+ success: this.element,
632
+ // don't update on failure (this could be an option)
633
+ failure: null
634
+ },
635
+ this.url,
636
+ Object.extend({
637
+ parameters: this.options.callback(form, value),
638
+ onComplete: this.onComplete.bind(this),
639
+ onFailure: this.onFailure.bind(this)
640
+ }, this.options.ajaxOptions)
641
+ );
642
+ // stop the event to avoid a page refresh in Safari
643
+ if (arguments.length > 1) {
644
+ Event.stop(arguments[0]);
645
+ }
646
+ return false;
647
+ },
648
+ onLoading: function() {
649
+ this.saving = true;
650
+ this.removeForm();
651
+ this.leaveHover();
652
+ this.showSaving();
653
+ },
654
+ showSaving: function() {
655
+ this.oldInnerHTML = this.element.innerHTML;
656
+ this.element.innerHTML = this.options.savingText;
657
+ Element.addClassName(this.element, this.options.savingClassName);
658
+ this.element.style.backgroundColor = this.originalBackground;
659
+ Element.show(this.element);
660
+ },
661
+ removeForm: function() {
662
+ if(this.form) {
663
+ if (this.form.parentNode) Element.remove(this.form);
664
+ this.form = null;
665
+ }
666
+ },
667
+ enterHover: function() {
668
+ if (this.saving) return;
669
+ this.element.style.backgroundColor = this.options.highlightcolor;
670
+ if (this.effect) {
671
+ this.effect.cancel();
672
+ }
673
+ Element.addClassName(this.element, this.options.hoverClassName)
674
+ },
675
+ leaveHover: function() {
676
+ if (this.options.backgroundColor) {
677
+ this.element.style.backgroundColor = this.oldBackground;
678
+ }
679
+ Element.removeClassName(this.element, this.options.hoverClassName)
680
+ if (this.saving) return;
681
+ this.effect = new Effect.Highlight(this.element, {
682
+ startcolor: this.options.highlightcolor,
683
+ endcolor: this.options.highlightendcolor,
684
+ restorecolor: this.originalBackground
685
+ });
686
+ },
687
+ leaveEditMode: function() {
688
+ Element.removeClassName(this.element, this.options.savingClassName);
689
+ this.removeForm();
690
+ this.leaveHover();
691
+ this.element.style.backgroundColor = this.originalBackground;
692
+ Element.show(this.element);
693
+ if (this.options.externalControl) {
694
+ Element.show(this.options.externalControl);
695
+ }
696
+ this.editing = false;
697
+ this.saving = false;
698
+ this.oldInnerHTML = null;
699
+ this.onLeaveEditMode();
700
+ },
701
+ onComplete: function(transport) {
702
+ this.leaveEditMode();
703
+ this.options.onComplete.bind(this)(transport, this.element);
704
+ },
705
+ onEnterEditMode: function() {},
706
+ onLeaveEditMode: function() {},
707
+ dispose: function() {
708
+ if (this.oldInnerHTML) {
709
+ this.element.innerHTML = this.oldInnerHTML;
710
+ }
711
+ this.leaveEditMode();
712
+ Event.stopObserving(this.element, 'click', this.onclickListener);
713
+ Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
714
+ Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
715
+ if (this.options.externalControl) {
716
+ Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
717
+ Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
718
+ Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
719
+ }
720
+ }
721
+ };