author | Giulio Cesare Solaroli <giulio.cesare@solaroli.it> | 2011-10-03 16:04:12 (UTC) |
---|---|---|
committer | Giulio Cesare Solaroli <giulio.cesare@solaroli.it> | 2011-10-03 16:04:12 (UTC) |
commit | 541bb378ddece2eab135a8066a16994e94436dea (patch) (unidiff) | |
tree | ff160ea3e26f7fe07fcfd401387c5a0232ca715e /frontend/beta/js/MochiKit/Controls.js | |
parent | 1bf431fd3d45cbdf4afa3e12afefe5d24f4d3bc7 (diff) | |
parent | ecad5e895831337216544e81f1a467e0c68c4a6a (diff) | |
download | clipperz-541bb378ddece2eab135a8066a16994e94436dea.zip clipperz-541bb378ddece2eab135a8066a16994e94436dea.tar.gz clipperz-541bb378ddece2eab135a8066a16994e94436dea.tar.bz2 |
Merge pull request #1 from gcsolaroli/master
First version of the restructured repository
Diffstat (limited to 'frontend/beta/js/MochiKit/Controls.js') (more/less context) (ignore whitespace changes)
-rw-r--r-- | frontend/beta/js/MochiKit/Controls.js | 1388 |
1 files changed, 1388 insertions, 0 deletions
diff --git a/frontend/beta/js/MochiKit/Controls.js b/frontend/beta/js/MochiKit/Controls.js new file mode 100644 index 0000000..b0bc5bf --- a/dev/null +++ b/frontend/beta/js/MochiKit/Controls.js | |||
@@ -0,0 +1,1388 @@ | |||
1 | /*** | ||
2 | Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) | ||
3 | (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan) | ||
4 | (c) 2005 Jon Tirsen (http://www.tirsen.com) | ||
5 | Contributors: | ||
6 | Richard Livsey | ||
7 | Rahul Bhargava | ||
8 | Rob Wills | ||
9 | Mochi-ized By Thomas Herve (_firstname_@nimail.org) | ||
10 | |||
11 | See scriptaculous.js for full license. | ||
12 | |||
13 | Autocompleter.Base handles all the autocompletion functionality | ||
14 | that's independent of the data source for autocompletion. This | ||
15 | includes drawing the autocompletion menu, observing keyboard | ||
16 | and mouse events, and similar. | ||
17 | |||
18 | Specific autocompleters need to provide, at the very least, | ||
19 | a getUpdatedChoices function that will be invoked every time | ||
20 | the text inside the monitored textbox changes. This method | ||
21 | should get the text for which to provide autocompletion by | ||
22 | invoking this.getToken(), NOT by directly accessing | ||
23 | this.element.value. This is to allow incremental tokenized | ||
24 | autocompletion. Specific auto-completion logic (AJAX, etc) | ||
25 | belongs in getUpdatedChoices. | ||
26 | |||
27 | Tokenized incremental autocompletion is enabled automatically | ||
28 | when an autocompleter is instantiated with the 'tokens' option | ||
29 | in the options parameter, e.g.: | ||
30 | new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); | ||
31 | will incrementally autocomplete with a comma as the token. | ||
32 | Additionally, ',' in the above example can be replaced with | ||
33 | a token array, e.g. { tokens: [',', '\n'] } which | ||
34 | enables autocompletion on multiple tokens. This is most | ||
35 | useful when one of the tokens is \n (a newline), as it | ||
36 | allows smart autocompletion after linebreaks. | ||
37 | |||
38 | ***/ | ||
39 | |||
40 | MochiKit.Base.update(MochiKit.Base, { | ||
41 | ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)', | ||
42 | |||
43 | /** @id MochiKit.Base.stripScripts */ | ||
44 | stripScripts: function (str) { | ||
45 | return str.replace(new RegExp(MochiKit.Base.ScriptFragment, 'img'), ''); | ||
46 | }, | ||
47 | |||
48 | /** @id MochiKit.Base.stripTags */ | ||
49 | stripTags: function(str) { | ||
50 | return str.replace(/<\/?[^>]+>/gi, ''); | ||
51 | }, | ||
52 | |||
53 | /** @id MochiKit.Base.extractScripts */ | ||
54 | extractScripts: function (str) { | ||
55 | var matchAll = new RegExp(MochiKit.Base.ScriptFragment, 'img'); | ||
56 | var matchOne = new RegExp(MochiKit.Base.ScriptFragment, 'im'); | ||
57 | return MochiKit.Base.map(function (scriptTag) { | ||
58 | return (scriptTag.match(matchOne) || ['', ''])[1]; | ||
59 | }, str.match(matchAll) || []); | ||
60 | }, | ||
61 | |||
62 | /** @id MochiKit.Base.evalScripts */ | ||
63 | evalScripts: function (str) { | ||
64 | return MochiKit.Base.map(function (scr) { | ||
65 | eval(scr); | ||
66 | }, MochiKit.Base.extractScripts(str)); | ||
67 | } | ||
68 | }); | ||
69 | |||
70 | MochiKit.Form = { | ||
71 | |||
72 | /** @id MochiKit.Form.serialize */ | ||
73 | serialize: function (form) { | ||
74 | var elements = MochiKit.Form.getElements(form); | ||
75 | var queryComponents = []; | ||
76 | |||
77 | for (var i = 0; i < elements.length; i++) { | ||
78 | var queryComponent = MochiKit.Form.serializeElement(elements[i]); | ||
79 | if (queryComponent) { | ||
80 | queryComponents.push(queryComponent); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | return queryComponents.join('&'); | ||
85 | }, | ||
86 | |||
87 | /** @id MochiKit.Form.getElements */ | ||
88 | getElements: function (form) { | ||
89 | form = MochiKit.DOM.getElement(form); | ||
90 | var elements = []; | ||
91 | |||
92 | for (tagName in MochiKit.Form.Serializers) { | ||
93 | var tagElements = form.getElementsByTagName(tagName); | ||
94 | for (var j = 0; j < tagElements.length; j++) { | ||
95 | elements.push(tagElements[j]); | ||
96 | } | ||
97 | } | ||
98 | return elements; | ||
99 | }, | ||
100 | |||
101 | /** @id MochiKit.Form.serializeElement */ | ||
102 | serializeElement: function (element) { | ||
103 | element = MochiKit.DOM.getElement(element); | ||
104 | var method = element.tagName.toLowerCase(); | ||
105 | var parameter = MochiKit.Form.Serializers[method](element); | ||
106 | |||
107 | if (parameter) { | ||
108 | var key = encodeURIComponent(parameter[0]); | ||
109 | if (key.length === 0) { | ||
110 | return; | ||
111 | } | ||
112 | |||
113 | if (!(parameter[1] instanceof Array)) { | ||
114 | parameter[1] = [parameter[1]]; | ||
115 | } | ||
116 | |||
117 | return parameter[1].map(function (value) { | ||
118 | return key + '=' + encodeURIComponent(value); | ||
119 | }).join('&'); | ||
120 | } | ||
121 | } | ||
122 | }; | ||
123 | |||
124 | MochiKit.Form.Serializers = { | ||
125 | |||
126 | /** @id MochiKit.Form.Serializers.input */ | ||
127 | input: function (element) { | ||
128 | switch (element.type.toLowerCase()) { | ||
129 | case 'submit': | ||
130 | case 'hidden': | ||
131 | case 'password': | ||
132 | case 'text': | ||
133 | return MochiKit.Form.Serializers.textarea(element); | ||
134 | case 'checkbox': | ||
135 | case 'radio': | ||
136 | return MochiKit.Form.Serializers.inputSelector(element); | ||
137 | } | ||
138 | return false; | ||
139 | }, | ||
140 | |||
141 | /** @id MochiKit.Form.Serializers.inputSelector */ | ||
142 | inputSelector: function (element) { | ||
143 | if (element.checked) { | ||
144 | return [element.name, element.value]; | ||
145 | } | ||
146 | }, | ||
147 | |||
148 | /** @id MochiKit.Form.Serializers.textarea */ | ||
149 | textarea: function (element) { | ||
150 | return [element.name, element.value]; | ||
151 | }, | ||
152 | |||
153 | /** @id MochiKit.Form.Serializers.select */ | ||
154 | select: function (element) { | ||
155 | return MochiKit.Form.Serializers[element.type == 'select-one' ? | ||
156 | 'selectOne' : 'selectMany'](element); | ||
157 | }, | ||
158 | |||
159 | /** @id MochiKit.Form.Serializers.selectOne */ | ||
160 | selectOne: function (element) { | ||
161 | var value = '', opt, index = element.selectedIndex; | ||
162 | if (index >= 0) { | ||
163 | opt = element.options[index]; | ||
164 | value = opt.value; | ||
165 | if (!value && !('value' in opt)) { | ||
166 | value = opt.text; | ||
167 | } | ||
168 | } | ||
169 | return [element.name, value]; | ||
170 | }, | ||
171 | |||
172 | /** @id MochiKit.Form.Serializers.selectMany */ | ||
173 | selectMany: function (element) { | ||
174 | var value = []; | ||
175 | for (var i = 0; i < element.length; i++) { | ||
176 | var opt = element.options[i]; | ||
177 | if (opt.selected) { | ||
178 | var optValue = opt.value; | ||
179 | if (!optValue && !('value' in opt)) { | ||
180 | optValue = opt.text; | ||
181 | } | ||
182 | value.push(optValue); | ||
183 | } | ||
184 | } | ||
185 | return [element.name, value]; | ||
186 | } | ||
187 | }; | ||
188 | |||
189 | /** @id Ajax */ | ||
190 | var Ajax = { | ||
191 | activeRequestCount: 0 | ||
192 | }; | ||
193 | |||
194 | Ajax.Responders = { | ||
195 | responders: [], | ||
196 | |||
197 | /** @id Ajax.Responders.register */ | ||
198 | register: function (responderToAdd) { | ||
199 | if (MochiKit.Base.find(this.responders, responderToAdd) == -1) { | ||
200 | this.responders.push(responderToAdd); | ||
201 | } | ||
202 | }, | ||
203 | |||
204 | /** @id Ajax.Responders.unregister */ | ||
205 | unregister: function (responderToRemove) { | ||
206 | this.responders = this.responders.without(responderToRemove); | ||
207 | }, | ||
208 | |||
209 | /** @id Ajax.Responders.dispatch */ | ||
210 | dispatch: function (callback, request, transport, json) { | ||
211 | MochiKit.Iter.forEach(this.responders, function (responder) { | ||
212 | if (responder[callback] && | ||
213 | typeof(responder[callback]) == 'function') { | ||
214 | try { | ||
215 | responder[callback].apply(responder, [request, transport, json]); | ||
216 | } catch (e) {} | ||
217 | } | ||
218 | }); | ||
219 | } | ||
220 | }; | ||
221 | |||
222 | Ajax.Responders.register({ | ||
223 | |||
224 | /** @id Ajax.Responders.onCreate */ | ||
225 | onCreate: function () { | ||
226 | Ajax.activeRequestCount++; | ||
227 | }, | ||
228 | |||
229 | /** @id Ajax.Responders.onComplete */ | ||
230 | onComplete: function () { | ||
231 | Ajax.activeRequestCount--; | ||
232 | } | ||
233 | }); | ||
234 | |||
235 | /** @id Ajax.Base */ | ||
236 | Ajax.Base = function () {}; | ||
237 | |||
238 | Ajax.Base.prototype = { | ||
239 | |||
240 | /** @id Ajax.Base.prototype.setOptions */ | ||
241 | setOptions: function (options) { | ||
242 | this.options = { | ||
243 | method: 'post', | ||
244 | asynchronous: true, | ||
245 | parameters: '' | ||
246 | } | ||
247 | MochiKit.Base.update(this.options, options || {}); | ||
248 | }, | ||
249 | |||
250 | /** @id Ajax.Base.prototype.responseIsSuccess */ | ||
251 | responseIsSuccess: function () { | ||
252 | return this.transport.status == undefined | ||
253 | || this.transport.status === 0 | ||
254 | || (this.transport.status >= 200 && this.transport.status < 300); | ||
255 | }, | ||
256 | |||
257 | /** @id Ajax.Base.prototype.responseIsFailure */ | ||
258 | responseIsFailure: function () { | ||
259 | return !this.responseIsSuccess(); | ||
260 | } | ||
261 | }; | ||
262 | |||
263 | /** @id Ajax.Request */ | ||
264 | Ajax.Request = function (url, options) { | ||
265 | this.__init__(url, options); | ||
266 | }; | ||
267 | |||
268 | /** @id Ajax.Events */ | ||
269 | Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', | ||
270 | 'Interactive', 'Complete']; | ||
271 | |||
272 | MochiKit.Base.update(Ajax.Request.prototype, Ajax.Base.prototype); | ||
273 | |||
274 | MochiKit.Base.update(Ajax.Request.prototype, { | ||
275 | __init__: function (url, options) { | ||
276 | this.transport = MochiKit.Async.getXMLHttpRequest(); | ||
277 | this.setOptions(options); | ||
278 | this.request(url); | ||
279 | }, | ||
280 | |||
281 | /** @id Ajax.Request.prototype.request */ | ||
282 | request: function (url) { | ||
283 | var parameters = this.options.parameters || ''; | ||
284 | if (parameters.length > 0){ | ||
285 | parameters += '&_='; | ||
286 | } | ||
287 | |||
288 | try { | ||
289 | this.url = url; | ||
290 | if (this.options.method == 'get' && parameters.length > 0) { | ||
291 | this.url += (this.url.match(/\?/) ? '&' : '?') + parameters; | ||
292 | } | ||
293 | Ajax.Responders.dispatch('onCreate', this, this.transport); | ||
294 | |||
295 | this.transport.open(this.options.method, this.url, | ||
296 | this.options.asynchronous); | ||
297 | |||
298 | if (this.options.asynchronous) { | ||
299 | this.transport.onreadystatechange = MochiKit.Base.bind(this.onStateChange, this); | ||
300 | setTimeout(MochiKit.Base.bind(function () { | ||
301 | this.respondToReadyState(1); | ||
302 | }, this), 10); | ||
303 | } | ||
304 | |||
305 | this.setRequestHeaders(); | ||
306 | |||
307 | var body = this.options.postBody ? this.options.postBody : parameters; | ||
308 | this.transport.send(this.options.method == 'post' ? body : null); | ||
309 | |||
310 | } catch (e) { | ||
311 | this.dispatchException(e); | ||
312 | } | ||
313 | }, | ||
314 | |||
315 | /** @id Ajax.Request.prototype.setRequestHeaders */ | ||
316 | setRequestHeaders: function () { | ||
317 | var requestHeaders = ['X-Requested-With', 'XMLHttpRequest']; | ||
318 | |||
319 | if (this.options.method == 'post') { | ||
320 | requestHeaders.push('Content-type', | ||
321 | 'application/x-www-form-urlencoded'); | ||
322 | |||
323 | /* Force 'Connection: close' for Mozilla browsers to work around | ||
324 | * a bug where XMLHttpRequest sends an incorrect Content-length | ||
325 | * header. See Mozilla Bugzilla #246651. | ||
326 | */ | ||
327 | if (this.transport.overrideMimeType) { | ||
328 | requestHeaders.push('Connection', 'close'); | ||
329 | } | ||
330 | } | ||
331 | |||
332 | if (this.options.requestHeaders) { | ||
333 | requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); | ||
334 | } | ||
335 | |||
336 | for (var i = 0; i < requestHeaders.length; i += 2) { | ||
337 | this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); | ||
338 | } | ||
339 | }, | ||
340 | |||
341 | /** @id Ajax.Request.prototype.onStateChange */ | ||
342 | onStateChange: function () { | ||
343 | var readyState = this.transport.readyState; | ||
344 | if (readyState != 1) { | ||
345 | this.respondToReadyState(this.transport.readyState); | ||
346 | } | ||
347 | }, | ||
348 | |||
349 | /** @id Ajax.Request.prototype.header */ | ||
350 | header: function (name) { | ||
351 | try { | ||
352 | return this.transport.getResponseHeader(name); | ||
353 | } catch (e) {} | ||
354 | }, | ||
355 | |||
356 | /** @id Ajax.Request.prototype.evalJSON */ | ||
357 | evalJSON: function () { | ||
358 | try { | ||
359 | return eval(this.header('X-JSON')); | ||
360 | } catch (e) {} | ||
361 | }, | ||
362 | |||
363 | /** @id Ajax.Request.prototype.evalResponse */ | ||
364 | evalResponse: function () { | ||
365 | try { | ||
366 | return eval(this.transport.responseText); | ||
367 | } catch (e) { | ||
368 | this.dispatchException(e); | ||
369 | } | ||
370 | }, | ||
371 | |||
372 | /** @id Ajax.Request.prototype.respondToReadyState */ | ||
373 | respondToReadyState: function (readyState) { | ||
374 | var event = Ajax.Request.Events[readyState]; | ||
375 | var transport = this.transport, json = this.evalJSON(); | ||
376 | |||
377 | if (event == 'Complete') { | ||
378 | try { | ||
379 | (this.options['on' + this.transport.status] | ||
380 | || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] | ||
381 | || MochiKit.Base.noop)(transport, json); | ||
382 | } catch (e) { | ||
383 | this.dispatchException(e); | ||
384 | } | ||
385 | |||
386 | if ((this.header('Content-type') || '').match(/^text\/javascript/i)) { | ||
387 | this.evalResponse(); | ||
388 | } | ||
389 | } | ||
390 | |||
391 | try { | ||
392 | (this.options['on' + event] || MochiKit.Base.noop)(transport, json); | ||
393 | Ajax.Responders.dispatch('on' + event, this, transport, json); | ||
394 | } catch (e) { | ||
395 | this.dispatchException(e); | ||
396 | } | ||
397 | |||
398 | /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ | ||
399 | if (event == 'Complete') { | ||
400 | this.transport.onreadystatechange = MochiKit.Base.noop; | ||
401 | } | ||
402 | }, | ||
403 | |||
404 | /** @id Ajax.Request.prototype.dispatchException */ | ||
405 | dispatchException: function (exception) { | ||
406 | (this.options.onException || MochiKit.Base.noop)(this, exception); | ||
407 | Ajax.Responders.dispatch('onException', this, exception); | ||
408 | } | ||
409 | }); | ||
410 | |||
411 | /** @id Ajax.Updater */ | ||
412 | Ajax.Updater = function (container, url, options) { | ||
413 | this.__init__(container, url, options); | ||
414 | }; | ||
415 | |||
416 | MochiKit.Base.update(Ajax.Updater.prototype, Ajax.Request.prototype); | ||
417 | |||
418 | MochiKit.Base.update(Ajax.Updater.prototype, { | ||
419 | __init__: function (container, url, options) { | ||
420 | this.containers = { | ||
421 | success: container.success ? MochiKit.DOM.getElement(container.success) : MochiKit.DOM.getElement(container), | ||
422 | failure: container.failure ? MochiKit.DOM.getElement(container.failure) : | ||
423 | (container.success ? null : MochiKit.DOM.getElement(container)) | ||
424 | } | ||
425 | this.transport = MochiKit.Async.getXMLHttpRequest(); | ||
426 | this.setOptions(options); | ||
427 | |||
428 | var onComplete = this.options.onComplete || MochiKit.Base.noop; | ||
429 | this.options.onComplete = MochiKit.Base.bind(function (transport, object) { | ||
430 | this.updateContent(); | ||
431 | onComplete(transport, object); | ||
432 | }, this); | ||
433 | |||
434 | this.request(url); | ||
435 | }, | ||
436 | |||
437 | /** @id Ajax.Updater.prototype.updateContent */ | ||
438 | updateContent: function () { | ||
439 | var receiver = this.responseIsSuccess() ? | ||
440 | this.containers.success : this.containers.failure; | ||
441 | var response = this.transport.responseText; | ||
442 | |||
443 | if (!this.options.evalScripts) { | ||
444 | response = MochiKit.Base.stripScripts(response); | ||
445 | } | ||
446 | |||
447 | if (receiver) { | ||
448 | if (this.options.insertion) { | ||
449 | new this.options.insertion(receiver, response); | ||
450 | } else { | ||
451 | MochiKit.DOM.getElement(receiver).innerHTML = | ||
452 | MochiKit.Base.stripScripts(response); | ||
453 | setTimeout(function () { | ||
454 | MochiKit.Base.evalScripts(response); | ||
455 | }, 10); | ||
456 | } | ||
457 | } | ||
458 | |||
459 | if (this.responseIsSuccess()) { | ||
460 | if (this.onComplete) { | ||
461 | setTimeout(MochiKit.Base.bind(this.onComplete, this), 10); | ||
462 | } | ||
463 | } | ||
464 | } | ||
465 | }); | ||
466 | |||
467 | /** @id Field */ | ||
468 | var Field = { | ||
469 | |||
470 | /** @id clear */ | ||
471 | clear: function () { | ||
472 | for (var i = 0; i < arguments.length; i++) { | ||
473 | MochiKit.DOM.getElement(arguments[i]).value = ''; | ||
474 | } | ||
475 | }, | ||
476 | |||
477 | /** @id focus */ | ||
478 | focus: function (element) { | ||
479 | MochiKit.DOM.getElement(element).focus(); | ||
480 | }, | ||
481 | |||
482 | /** @id present */ | ||
483 | present: function () { | ||
484 | for (var i = 0; i < arguments.length; i++) { | ||
485 | if (MochiKit.DOM.getElement(arguments[i]).value == '') { | ||
486 | return false; | ||
487 | } | ||
488 | } | ||
489 | return true; | ||
490 | }, | ||
491 | |||
492 | /** @id select */ | ||
493 | select: function (element) { | ||
494 | MochiKit.DOM.getElement(element).select(); | ||
495 | }, | ||
496 | |||
497 | /** @id activate */ | ||
498 | activate: function (element) { | ||
499 | element = MochiKit.DOM.getElement(element); | ||
500 | element.focus(); | ||
501 | if (element.select) { | ||
502 | element.select(); | ||
503 | } | ||
504 | }, | ||
505 | |||
506 | /** @id scrollFreeActivate */ | ||
507 | scrollFreeActivate: function (field) { | ||
508 | setTimeout(function () { | ||
509 | Field.activate(field); | ||
510 | }, 1); | ||
511 | } | ||
512 | }; | ||
513 | |||
514 | |||
515 | /** @id Autocompleter */ | ||
516 | var Autocompleter = {}; | ||
517 | |||
518 | /** @id Autocompleter.Base */ | ||
519 | Autocompleter.Base = function () {}; | ||
520 | |||
521 | Autocompleter.Base.prototype = { | ||
522 | |||
523 | /** @id Autocompleter.Base.prototype.baseInitialize */ | ||
524 | baseInitialize: function (element, update, options) { | ||
525 | this.element = MochiKit.DOM.getElement(element); | ||
526 | this.update = MochiKit.DOM.getElement(update); | ||
527 | this.hasFocus = false; | ||
528 | this.changed = false; | ||
529 | this.active = false; | ||
530 | this.index = 0; | ||
531 | this.entryCount = 0; | ||
532 | |||
533 | if (this.setOptions) { | ||
534 | this.setOptions(options); | ||
535 | } | ||
536 | else { | ||
537 | this.options = options || {}; | ||
538 | } | ||
539 | |||
540 | this.options.paramName = this.options.paramName || this.element.name; | ||
541 | this.options.tokens = this.options.tokens || []; | ||
542 | this.options.frequency = this.options.frequency || 0.4; | ||
543 | this.options.minChars = this.options.minChars || 1; | ||
544 | this.options.onShow = this.options.onShow || function (element, update) { | ||
545 | if (!update.style.position || update.style.position == 'absolute') { | ||
546 | update.style.position = 'absolute'; | ||
547 | MochiKit.Position.clone(element, update, { | ||
548 | setHeight: false, | ||
549 | offsetTop: element.offsetHeight | ||
550 | }); | ||
551 | } | ||
552 | MochiKit.Visual.appear(update, {duration:0.15}); | ||
553 | }; | ||
554 | this.options.onHide = this.options.onHide || function (element, update) { | ||
555 | MochiKit.Visual.fade(update, {duration: 0.15}); | ||
556 | }; | ||
557 | |||
558 | if (typeof(this.options.tokens) == 'string') { | ||
559 | this.options.tokens = new Array(this.options.tokens); | ||
560 | } | ||
561 | |||
562 | this.observer = null; | ||
563 | |||
564 | this.element.setAttribute('autocomplete', 'off'); | ||
565 | |||
566 | MochiKit.Style.hideElement(this.update); | ||
567 | |||
568 | MochiKit.Signal.connect(this.element, 'onblur', this, this.onBlur); | ||
569 | MochiKit.Signal.connect(this.element, 'onkeypress', this, this.onKeyPress, this); | ||
570 | }, | ||
571 | |||
572 | /** @id Autocompleter.Base.prototype.show */ | ||
573 | show: function () { | ||
574 | if (MochiKit.Style.getStyle(this.update, 'display') == 'none') { | ||
575 | this.options.onShow(this.element, this.update); | ||
576 | } | ||
577 | if (!this.iefix && /MSIE/.test(navigator.userAgent && | ||
578 | (MochiKit.Style.getStyle(this.update, 'position') == 'absolute'))) { | ||
579 | new Insertion.After(this.update, | ||
580 | '<iframe id="' + this.update.id + '_iefix" '+ | ||
581 | 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' + | ||
582 | 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>'); | ||
583 | this.iefix = MochiKit.DOM.getElement(this.update.id + '_iefix'); | ||
584 | } | ||
585 | if (this.iefix) { | ||
586 | setTimeout(MochiKit.Base.bind(this.fixIEOverlapping, this), 50); | ||
587 | } | ||
588 | }, | ||
589 | |||
590 | /** @id Autocompleter.Base.prototype.fixIEOverlapping */ | ||
591 | fixIEOverlapping: function () { | ||
592 | MochiKit.Position.clone(this.update, this.iefix); | ||
593 | this.iefix.style.zIndex = 1; | ||
594 | this.update.style.zIndex = 2; | ||
595 | MochiKit.Style.showElement(this.iefix); | ||
596 | }, | ||
597 | |||
598 | /** @id Autocompleter.Base.prototype.hide */ | ||
599 | hide: function () { | ||
600 | this.stopIndicator(); | ||
601 | if (MochiKit.Style.getStyle(this.update, 'display') != 'none') { | ||
602 | this.options.onHide(this.element, this.update); | ||
603 | } | ||
604 | if (this.iefix) { | ||
605 | MochiKit.Style.hideElement(this.iefix); | ||
606 | } | ||
607 | }, | ||
608 | |||
609 | /** @id Autocompleter.Base.prototype.startIndicator */ | ||
610 | startIndicator: function () { | ||
611 | if (this.options.indicator) { | ||
612 | MochiKit.Style.showElement(this.options.indicator); | ||
613 | } | ||
614 | }, | ||
615 | |||
616 | /** @id Autocompleter.Base.prototype.stopIndicator */ | ||
617 | stopIndicator: function () { | ||
618 | if (this.options.indicator) { | ||
619 | MochiKit.Style.hideElement(this.options.indicator); | ||
620 | } | ||
621 | }, | ||
622 | |||
623 | /** @id Autocompleter.Base.prototype.onKeyPress */ | ||
624 | onKeyPress: function (event) { | ||
625 | if (this.active) { | ||
626 | if (event.key().string == "KEY_TAB" || event.key().string == "KEY_RETURN") { | ||
627 | this.selectEntry(); | ||
628 | MochiKit.Event.stop(event); | ||
629 | } else if (event.key().string == "KEY_ESCAPE") { | ||
630 | this.hide(); | ||
631 | this.active = false; | ||
632 | MochiKit.Event.stop(event); | ||
633 | return; | ||
634 | } else if (event.key().string == "KEY_LEFT" || event.key().string == "KEY_RIGHT") { | ||
635 | return; | ||
636 | } else if (event.key().string == "KEY_UP") { | ||
637 | this.markPrevious(); | ||
638 | this.render(); | ||
639 | if (/AppleWebKit'/.test(navigator.appVersion)) { | ||
640 | event.stop(); | ||
641 | } | ||
642 | return; | ||
643 | } else if (event.key().string == "KEY_DOWN") { | ||
644 | this.markNext(); | ||
645 | this.render(); | ||
646 | if (/AppleWebKit'/.test(navigator.appVersion)) { | ||
647 | event.stop(); | ||
648 | } | ||
649 | return; | ||
650 | } | ||
651 | } else { | ||
652 | if (event.key().string == "KEY_TAB" || event.key().string == "KEY_RETURN") { | ||
653 | return; | ||
654 | } | ||
655 | } | ||
656 | |||
657 | this.changed = true; | ||
658 | this.hasFocus = true; | ||
659 | |||
660 | if (this.observer) { | ||
661 | clearTimeout(this.observer); | ||
662 | } | ||
663 | this.observer = setTimeout(MochiKit.Base.bind(this.onObserverEvent, this), | ||
664 | this.options.frequency*1000); | ||
665 | }, | ||
666 | |||
667 | /** @id Autocompleter.Base.prototype.findElement */ | ||
668 | findElement: function (event, tagName) { | ||
669 | var element = event.target; | ||
670 | while (element.parentNode && (!element.tagName || | ||
671 | (element.tagName.toUpperCase() != tagName.toUpperCase()))) { | ||
672 | element = element.parentNode; | ||
673 | } | ||
674 | return element; | ||
675 | }, | ||
676 | |||
677 | /** @id Autocompleter.Base.prototype.hover */ | ||
678 | onHover: function (event) { | ||
679 | var element = this.findElement(event, 'LI'); | ||
680 | if (this.index != element.autocompleteIndex) { | ||
681 | this.index = element.autocompleteIndex; | ||
682 | this.render(); | ||
683 | } | ||
684 | event.stop(); | ||
685 | }, | ||
686 | |||
687 | /** @id Autocompleter.Base.prototype.onClick */ | ||
688 | onClick: function (event) { | ||
689 | var element = this.findElement(event, 'LI'); | ||
690 | this.index = element.autocompleteIndex; | ||
691 | this.selectEntry(); | ||
692 | this.hide(); | ||
693 | }, | ||
694 | |||
695 | /** @id Autocompleter.Base.prototype.onBlur */ | ||
696 | onBlur: function (event) { | ||
697 | // needed to make click events working | ||
698 | setTimeout(MochiKit.Base.bind(this.hide, this), 250); | ||
699 | this.hasFocus = false; | ||
700 | this.active = false; | ||
701 | }, | ||
702 | |||
703 | /** @id Autocompleter.Base.prototype.render */ | ||
704 | render: function () { | ||
705 | if (this.entryCount > 0) { | ||
706 | for (var i = 0; i < this.entryCount; i++) { | ||
707 | this.index == i ? | ||
708 | MochiKit.DOM.addElementClass(this.getEntry(i), 'selected') : | ||
709 | MochiKit.DOM.removeElementClass(this.getEntry(i), 'selected'); | ||
710 | } | ||
711 | if (this.hasFocus) { | ||
712 | this.show(); | ||
713 | this.active = true; | ||
714 | } | ||
715 | } else { | ||
716 | this.active = false; | ||
717 | this.hide(); | ||
718 | } | ||
719 | }, | ||
720 | |||
721 | /** @id Autocompleter.Base.prototype.markPrevious */ | ||
722 | markPrevious: function () { | ||
723 | if (this.index > 0) { | ||
724 | this.index-- | ||
725 | } else { | ||
726 | this.index = this.entryCount-1; | ||
727 | } | ||
728 | }, | ||
729 | |||
730 | /** @id Autocompleter.Base.prototype.markNext */ | ||
731 | markNext: function () { | ||
732 | if (this.index < this.entryCount-1) { | ||
733 | this.index++ | ||
734 | } else { | ||
735 | this.index = 0; | ||
736 | } | ||
737 | }, | ||
738 | |||
739 | /** @id Autocompleter.Base.prototype.getEntry */ | ||
740 | getEntry: function (index) { | ||
741 | return this.update.firstChild.childNodes[index]; | ||
742 | }, | ||
743 | |||
744 | /** @id Autocompleter.Base.prototype.getCurrentEntry */ | ||
745 | getCurrentEntry: function () { | ||
746 | return this.getEntry(this.index); | ||
747 | }, | ||
748 | |||
749 | /** @id Autocompleter.Base.prototype.selectEntry */ | ||
750 | selectEntry: function () { | ||
751 | this.active = false; | ||
752 | this.updateElement(this.getCurrentEntry()); | ||
753 | }, | ||
754 | |||
755 | /** @id Autocompleter.Base.prototype.collectTextNodesIgnoreClass */ | ||
756 | collectTextNodesIgnoreClass: function (element, className) { | ||
757 | return MochiKit.Base.flattenArray(MochiKit.Base.map(function (node) { | ||
758 | if (node.nodeType == 3) { | ||
759 | return node.nodeValue; | ||
760 | } else if (node.hasChildNodes() && !MochiKit.DOM.hasElementClass(node, className)) { | ||
761 | return this.collectTextNodesIgnoreClass(node, className); | ||
762 | } | ||
763 | return ''; | ||
764 | }, MochiKit.DOM.getElement(element).childNodes)).join(''); | ||
765 | }, | ||
766 | |||
767 | /** @id Autocompleter.Base.prototype.updateElement */ | ||
768 | updateElement: function (selectedElement) { | ||
769 | if (this.options.updateElement) { | ||
770 | this.options.updateElement(selectedElement); | ||
771 | return; | ||
772 | } | ||
773 | var value = ''; | ||
774 | if (this.options.select) { | ||
775 | var nodes = document.getElementsByClassName(this.options.select, selectedElement) || []; | ||
776 | if (nodes.length > 0) { | ||
777 | value = MochiKit.DOM.scrapeText(nodes[0]); | ||
778 | } | ||
779 | } else { | ||
780 | value = this.collectTextNodesIgnoreClass(selectedElement, 'informal'); | ||
781 | } | ||
782 | var lastTokenPos = this.findLastToken(); | ||
783 | if (lastTokenPos != -1) { | ||
784 | var newValue = this.element.value.substr(0, lastTokenPos + 1); | ||
785 | var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/); | ||
786 | if (whitespace) { | ||
787 | newValue += whitespace[0]; | ||
788 | } | ||
789 | this.element.value = newValue + value; | ||
790 | } else { | ||
791 | this.element.value = value; | ||
792 | } | ||
793 | this.element.focus(); | ||
794 | |||
795 | if (this.options.afterUpdateElement) { | ||
796 | this.options.afterUpdateElement(this.element, selectedElement); | ||
797 | } | ||
798 | }, | ||
799 | |||
800 | /** @id Autocompleter.Base.prototype.updateChoices */ | ||
801 | updateChoices: function (choices) { | ||
802 | if (!this.changed && this.hasFocus) { | ||
803 | this.update.innerHTML = choices; | ||
804 | var d = MochiKit.DOM; | ||
805 | d.removeEmptyTextNodes(this.update); | ||
806 | d.removeEmptyTextNodes(this.update.firstChild); | ||
807 | |||
808 | if (this.update.firstChild && this.update.firstChild.childNodes) { | ||
809 | this.entryCount = this.update.firstChild.childNodes.length; | ||
810 | for (var i = 0; i < this.entryCount; i++) { | ||
811 | var entry = this.getEntry(i); | ||
812 | entry.autocompleteIndex = i; | ||
813 | this.addObservers(entry); | ||
814 | } | ||
815 | } else { | ||
816 | this.entryCount = 0; | ||
817 | } | ||
818 | |||
819 | this.stopIndicator(); | ||
820 | |||
821 | this.index = 0; | ||
822 | this.render(); | ||
823 | } | ||
824 | }, | ||
825 | |||
826 | /** @id Autocompleter.Base.prototype.addObservers */ | ||
827 | addObservers: function (element) { | ||
828 | MochiKit.Signal.connect(element, 'onmouseover', this, this.onHover); | ||
829 | MochiKit.Signal.connect(element, 'onclick', this, this.onClick); | ||
830 | }, | ||
831 | |||
832 | /** @id Autocompleter.Base.prototype.onObserverEvent */ | ||
833 | onObserverEvent: function () { | ||
834 | this.changed = false; | ||
835 | if (this.getToken().length >= this.options.minChars) { | ||
836 | this.startIndicator(); | ||
837 | this.getUpdatedChoices(); | ||
838 | } else { | ||
839 | this.active = false; | ||
840 | this.hide(); | ||
841 | } | ||
842 | }, | ||
843 | |||
844 | /** @id Autocompleter.Base.prototype.getToken */ | ||
845 | getToken: function () { | ||
846 | var tokenPos = this.findLastToken(); | ||
847 | if (tokenPos != -1) { | ||
848 | var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,''); | ||
849 | } else { | ||
850 | var ret = this.element.value; | ||
851 | } | ||
852 | return /\n/.test(ret) ? '' : ret; | ||
853 | }, | ||
854 | |||
855 | /** @id Autocompleter.Base.prototype.findLastToken */ | ||
856 | findLastToken: function () { | ||
857 | var lastTokenPos = -1; | ||
858 | |||
859 | for (var i = 0; i < this.options.tokens.length; i++) { | ||
860 | var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]); | ||
861 | if (thisTokenPos > lastTokenPos) { | ||
862 | lastTokenPos = thisTokenPos; | ||
863 | } | ||
864 | } | ||
865 | return lastTokenPos; | ||
866 | } | ||
867 | } | ||
868 | |||
869 | /** @id Ajax.Autocompleter */ | ||
870 | Ajax.Autocompleter = function (element, update, url, options) { | ||
871 | this.__init__(element, update, url, options); | ||
872 | }; | ||
873 | |||
874 | MochiKit.Base.update(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype); | ||
875 | |||
876 | MochiKit.Base.update(Ajax.Autocompleter.prototype, { | ||
877 | __init__: function (element, update, url, options) { | ||
878 | this.baseInitialize(element, update, options); | ||
879 | this.options.asynchronous = true; | ||
880 | this.options.onComplete = MochiKit.Base.bind(this.onComplete, this); | ||
881 | this.options.defaultParams = this.options.parameters || null; | ||
882 | this.url = url; | ||
883 | }, | ||
884 | |||
885 | /** @id Ajax.Autocompleter.prototype.getUpdatedChoices */ | ||
886 | getUpdatedChoices: function () { | ||
887 | var entry = encodeURIComponent(this.options.paramName) + '=' + | ||
888 | encodeURIComponent(this.getToken()); | ||
889 | |||
890 | this.options.parameters = this.options.callback ? | ||
891 | this.options.callback(this.element, entry) : entry; | ||
892 | |||
893 | if (this.options.defaultParams) { | ||
894 | this.options.parameters += '&' + this.options.defaultParams; | ||
895 | } | ||
896 | new Ajax.Request(this.url, this.options); | ||
897 | }, | ||
898 | |||
899 | /** @id Ajax.Autocompleter.prototype.onComplete */ | ||
900 | onComplete: function (request) { | ||
901 | this.updateChoices(request.responseText); | ||
902 | } | ||
903 | }); | ||
904 | |||
905 | /*** | ||
906 | |||
907 | The local array autocompleter. Used when you'd prefer to | ||
908 | inject an array of autocompletion options into the page, rather | ||
909 | than sending out Ajax queries, which can be quite slow sometimes. | ||
910 | |||
911 | The constructor takes four parameters. The first two are, as usual, | ||
912 | the id of the monitored textbox, and id of the autocompletion menu. | ||
913 | The third is the array you want to autocomplete from, and the fourth | ||
914 | is the options block. | ||
915 | |||
916 | Extra local autocompletion options: | ||
917 | - choices - How many autocompletion choices to offer | ||
918 | |||
919 | - partialSearch - If false, the autocompleter will match entered | ||
920 | text only at the beginning of strings in the | ||
921 | autocomplete array. Defaults to true, which will | ||
922 | match text at the beginning of any *word* in the | ||
923 | strings in the autocomplete array. If you want to | ||
924 | search anywhere in the string, additionally set | ||
925 | the option fullSearch to true (default: off). | ||
926 | |||
927 | - fullSsearch - Search anywhere in autocomplete array strings. | ||
928 | |||
929 | - partialChars - How many characters to enter before triggering | ||
930 | a partial match (unlike minChars, which defines | ||
931 | how many characters are required to do any match | ||
932 | at all). Defaults to 2. | ||
933 | |||
934 | - ignoreCase - Whether to ignore case when autocompleting. | ||
935 | Defaults to true. | ||
936 | |||
937 | It's possible to pass in a custom function as the 'selector' | ||
938 | option, if you prefer to write your own autocompletion logic. | ||
939 | In that case, the other options above will not apply unless | ||
940 | you support them. | ||
941 | |||
942 | ***/ | ||
943 | |||
944 | /** @id Autocompleter.Local */ | ||
945 | Autocompleter.Local = function (element, update, array, options) { | ||
946 | this.__init__(element, update, array, options); | ||
947 | }; | ||
948 | |||
949 | MochiKit.Base.update(Autocompleter.Local.prototype, Autocompleter.Base.prototype); | ||
950 | |||
951 | MochiKit.Base.update(Autocompleter.Local.prototype, { | ||
952 | __init__: function (element, update, array, options) { | ||
953 | this.baseInitialize(element, update, options); | ||
954 | this.options.array = array; | ||
955 | }, | ||
956 | |||
957 | /** @id Autocompleter.Local.prototype.getUpdatedChoices */ | ||
958 | getUpdatedChoices: function () { | ||
959 | this.updateChoices(this.options.selector(this)); | ||
960 | }, | ||
961 | |||
962 | /** @id Autocompleter.Local.prototype.setOptions */ | ||
963 | setOptions: function (options) { | ||
964 | this.options = MochiKit.Base.update({ | ||
965 | choices: 10, | ||
966 | partialSearch: true, | ||
967 | partialChars: 2, | ||
968 | ignoreCase: true, | ||
969 | fullSearch: false, | ||
970 | selector: function (instance) { | ||
971 | var ret = []; // Beginning matches | ||
972 | var partial = []; // Inside matches | ||
973 | var entry = instance.getToken(); | ||
974 | var count = 0; | ||
975 | |||
976 | for (var i = 0; i < instance.options.array.length && | ||
977 | ret.length < instance.options.choices ; i++) { | ||
978 | |||
979 | var elem = instance.options.array[i]; | ||
980 | var foundPos = instance.options.ignoreCase ? | ||
981 | elem.toLowerCase().indexOf(entry.toLowerCase()) : | ||
982 | elem.indexOf(entry); | ||
983 | |||
984 | while (foundPos != -1) { | ||
985 | if (foundPos === 0 && elem.length != entry.length) { | ||
986 | ret.push('<li><strong>' + elem.substr(0, entry.length) + '</strong>' + | ||
987 | elem.substr(entry.length) + '</li>'); | ||
988 | break; | ||
989 | } else if (entry.length >= instance.options.partialChars && | ||
990 | instance.options.partialSearch && foundPos != -1) { | ||
991 | if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos - 1, 1))) { | ||
992 | partial.push('<li>' + elem.substr(0, foundPos) + '<strong>' + | ||
993 | elem.substr(foundPos, entry.length) + '</strong>' + elem.substr( | ||
994 | foundPos + entry.length) + '</li>'); | ||
995 | break; | ||
996 | } | ||
997 | } | ||
998 | |||
999 | foundPos = instance.options.ignoreCase ? | ||
1000 | elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : | ||
1001 | elem.indexOf(entry, foundPos + 1); | ||
1002 | |||
1003 | } | ||
1004 | } | ||
1005 | if (partial.length) { | ||
1006 | ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)) | ||
1007 | } | ||
1008 | return '<ul>' + ret.join('') + '</ul>'; | ||
1009 | } | ||
1010 | }, options || {}); | ||
1011 | } | ||
1012 | }); | ||
1013 | |||
1014 | /*** | ||
1015 | |||
1016 | AJAX in-place editor | ||
1017 | |||
1018 | see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor | ||
1019 | |||
1020 | Use this if you notice weird scrolling problems on some browsers, | ||
1021 | the DOM might be a bit confused when this gets called so do this | ||
1022 | waits 1 ms (with setTimeout) until it does the activation | ||
1023 | |||
1024 | ***/ | ||
1025 | |||
1026 | /** @id Ajax.InPlaceEditor */ | ||
1027 | Ajax.InPlaceEditor = function (element, url, options) { | ||
1028 | this.__init__(element, url, options); | ||
1029 | }; | ||
1030 | |||
1031 | /** @id Ajax.InPlaceEditor.defaultHighlightColor */ | ||
1032 | Ajax.InPlaceEditor.defaultHighlightColor = '#FFFF99'; | ||
1033 | |||
1034 | Ajax.InPlaceEditor.prototype = { | ||
1035 | __init__: function (element, url, options) { | ||
1036 | this.url = url; | ||
1037 | this.element = MochiKit.DOM.getElement(element); | ||
1038 | |||
1039 | this.options = MochiKit.Base.update({ | ||
1040 | okButton: true, | ||
1041 | okText: 'ok', | ||
1042 | cancelLink: true, | ||
1043 | cancelText: 'cancel', | ||
1044 | savingText: 'Saving...', | ||
1045 | clickToEditText: 'Click to edit', | ||
1046 | okText: 'ok', | ||
1047 | rows: 1, | ||
1048 | onComplete: function (transport, element) { | ||
1049 | new MochiKit.Visual.Highlight(element, {startcolor: this.options.highlightcolor}); | ||
1050 | }, | ||
1051 | onFailure: function (transport) { | ||
1052 | alert('Error communicating with the server: ' + MochiKit.Base.stripTags(transport.responseText)); | ||
1053 | }, | ||
1054 | callback: function (form) { | ||
1055 | return MochiKit.DOM.formContents(form); | ||
1056 | }, | ||
1057 | handleLineBreaks: true, | ||
1058 | loadingText: 'Loading...', | ||
1059 | savingClassName: 'inplaceeditor-saving', | ||
1060 | loadingClassName: 'inplaceeditor-loading', | ||
1061 | formClassName: 'inplaceeditor-form', | ||
1062 | highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor, | ||
1063 | highlightendcolor: '#FFFFFF', | ||
1064 | externalControl: null, | ||
1065 | submitOnBlur: false, | ||
1066 | ajaxOptions: {} | ||
1067 | }, options || {}); | ||
1068 | |||
1069 | if (!this.options.formId && this.element.id) { | ||
1070 | this.options.formId = this.element.id + '-inplaceeditor'; | ||
1071 | if (MochiKit.DOM.getElement(this.options.formId)) { | ||
1072 | // there's already a form with that name, don't specify an id | ||
1073 | this.options.formId = null; | ||
1074 | } | ||
1075 | } | ||
1076 | |||
1077 | if (this.options.externalControl) { | ||
1078 | this.options.externalControl = MochiKit.DOM.getElement(this.options.externalControl); | ||
1079 | } | ||
1080 | |||
1081 | this.originalBackground = MochiKit.Style.getStyle(this.element, 'background-color'); | ||
1082 | if (!this.originalBackground) { | ||
1083 | this.originalBackground = 'transparent'; | ||
1084 | } | ||
1085 | |||
1086 | this.element.title = this.options.clickToEditText; | ||
1087 | |||
1088 | this.onclickListener = MochiKit.Signal.connect(this.element, 'onclick', this, this.enterEditMode); | ||
1089 | this.mouseoverListener = MochiKit.Signal.connect(this.element, 'onmouseover', this, this.enterHover); | ||
1090 | this.mouseoutListener = MochiKit.Signal.connect(this.element, 'onmouseout', this, this.leaveHover); | ||
1091 | if (this.options.externalControl) { | ||
1092 | this.onclickListenerExternal = MochiKit.Signal.connect(this.options.externalControl, | ||
1093 | 'onclick', this, this.enterEditMode); | ||
1094 | this.mouseoverListenerExternal = MochiKit.Signal.connect(this.options.externalControl, | ||
1095 | 'onmouseover', this, this.enterHover); | ||
1096 | this.mouseoutListenerExternal = MochiKit.Signal.connect(this.options.externalControl, | ||
1097 | 'onmouseout', this, this.leaveHover); | ||
1098 | } | ||
1099 | }, | ||
1100 | |||
1101 | /** @id Ajax.InPlaceEditor.prototype.enterEditMode */ | ||
1102 | enterEditMode: function (evt) { | ||
1103 | if (this.saving) { | ||
1104 | return; | ||
1105 | } | ||
1106 | if (this.editing) { | ||
1107 | return; | ||
1108 | } | ||
1109 | this.editing = true; | ||
1110 | this.onEnterEditMode(); | ||
1111 | if (this.options.externalControl) { | ||
1112 | MochiKit.Style.hideElement(this.options.externalControl); | ||
1113 | } | ||
1114 | MochiKit.Style.hideElement(this.element); | ||
1115 | this.createForm(); | ||
1116 | this.element.parentNode.insertBefore(this.form, this.element); | ||
1117 | Field.scrollFreeActivate(this.editField); | ||
1118 | // stop the event to avoid a page refresh in Safari | ||
1119 | if (evt) { | ||
1120 | evt.stop(); | ||
1121 | } | ||
1122 | return false; | ||
1123 | }, | ||
1124 | |||
1125 | /** @id Ajax.InPlaceEditor.prototype.createForm */ | ||
1126 | createForm: function () { | ||
1127 | this.form = document.createElement('form'); | ||
1128 | this.form.id = this.options.formId; | ||
1129 | MochiKit.DOM.addElementClass(this.form, this.options.formClassName) | ||
1130 | this.form.onsubmit = MochiKit.Base.bind(this.onSubmit, this); | ||
1131 | |||
1132 | this.createEditField(); | ||
1133 | |||
1134 | if (this.options.textarea) { | ||
1135 | var br = document.createElement('br'); | ||
1136 | this.form.appendChild(br); | ||
1137 | } | ||
1138 | |||
1139 | if (this.options.okButton) { | ||
1140 | okButton = document.createElement('input'); | ||
1141 | okButton.type = 'submit'; | ||
1142 | okButton.value = this.options.okText; | ||
1143 | this.form.appendChild(okButton); | ||
1144 | } | ||
1145 | |||
1146 | if (this.options.cancelLink) { | ||
1147 | cancelLink = document.createElement('a'); | ||
1148 | cancelLink.href = '#'; | ||
1149 | cancelLink.appendChild(document.createTextNode(this.options.cancelText)); | ||
1150 | cancelLink.onclick = MochiKit.Base.bind(this.onclickCancel, this); | ||
1151 | this.form.appendChild(cancelLink); | ||
1152 | } | ||
1153 | }, | ||
1154 | |||
1155 | /** @id Ajax.InPlaceEditor.prototype.hasHTMLLineBreaks */ | ||
1156 | hasHTMLLineBreaks: function (string) { | ||
1157 | if (!this.options.handleLineBreaks) { | ||
1158 | return false; | ||
1159 | } | ||
1160 | return string.match(/<br/i) || string.match(/<p>/i); | ||
1161 | }, | ||
1162 | |||
1163 | /** @id Ajax.InPlaceEditor.prototype.convertHTMLLineBreaks */ | ||
1164 | convertHTMLLineBreaks: function (string) { | ||
1165 | return string.replace(/<br>/gi, '\n').replace(/<br\/>/gi, '\n').replace(/<\/p>/gi, '\n').replace(/<p>/gi, ''); | ||
1166 | }, | ||
1167 | |||
1168 | /** @id Ajax.InPlaceEditor.prototype.createEditField */ | ||
1169 | createEditField: function () { | ||
1170 | var text; | ||
1171 | if (this.options.loadTextURL) { | ||
1172 | text = this.options.loadingText; | ||
1173 | } else { | ||
1174 | text = this.getText(); | ||
1175 | } | ||
1176 | |||
1177 | var obj = this; | ||
1178 | |||
1179 | if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) { | ||
1180 | this.options.textarea = false; | ||
1181 | var textField = document.createElement('input'); | ||
1182 | textField.obj = this; | ||
1183 | textField.type = 'text'; | ||
1184 | textField.name = 'value'; | ||
1185 | textField.value = text; | ||
1186 | textField.style.backgroundColor = this.options.highlightcolor; | ||
1187 | var size = this.options.size || this.options.cols || 0; | ||
1188 | if (size !== 0) { | ||
1189 | textField.size = size; | ||
1190 | } | ||
1191 | if (this.options.submitOnBlur) { | ||
1192 | textField.onblur = MochiKit.Base.bind(this.onSubmit, this); | ||
1193 | } | ||
1194 | this.editField = textField; | ||
1195 | } else { | ||
1196 | this.options.textarea = true; | ||
1197 | var textArea = document.createElement('textarea'); | ||
1198 | textArea.obj = this; | ||
1199 | textArea.name = 'value'; | ||
1200 | textArea.value = this.convertHTMLLineBreaks(text); | ||
1201 | textArea.rows = this.options.rows; | ||
1202 | textArea.cols = this.options.cols || 40; | ||
1203 | if (this.options.submitOnBlur) { | ||
1204 | textArea.onblur = MochiKit.Base.bind(this.onSubmit, this); | ||
1205 | } | ||
1206 | this.editField = textArea; | ||
1207 | } | ||
1208 | |||
1209 | if (this.options.loadTextURL) { | ||
1210 | this.loadExternalText(); | ||
1211 | } | ||
1212 | this.form.appendChild(this.editField); | ||
1213 | }, | ||
1214 | |||
1215 | /** @id Ajax.InPlaceEditor.prototype.getText */ | ||
1216 | getText: function () { | ||
1217 | return this.element.innerHTML; | ||
1218 | }, | ||
1219 | |||
1220 | /** @id Ajax.InPlaceEditor.prototype.loadExternalText */ | ||
1221 | loadExternalText: function () { | ||
1222 | MochiKit.DOM.addElementClass(this.form, this.options.loadingClassName); | ||
1223 | this.editField.disabled = true; | ||
1224 | new Ajax.Request( | ||
1225 | this.options.loadTextURL, | ||
1226 | MochiKit.Base.update({ | ||
1227 | asynchronous: true, | ||
1228 | onComplete: MochiKit.Base.bind(this.onLoadedExternalText, this) | ||
1229 | }, this.options.ajaxOptions) | ||
1230 | ); | ||
1231 | }, | ||
1232 | |||
1233 | /** @id Ajax.InPlaceEditor.prototype.onLoadedExternalText */ | ||
1234 | onLoadedExternalText: function (transport) { | ||
1235 | MochiKit.DOM.removeElementClass(this.form, this.options.loadingClassName); | ||
1236 | this.editField.disabled = false; | ||
1237 | this.editField.value = MochiKit.Base.stripTags(transport); | ||
1238 | }, | ||
1239 | |||
1240 | /** @id Ajax.InPlaceEditor.prototype.onclickCancel */ | ||
1241 | onclickCancel: function () { | ||
1242 | this.onComplete(); | ||
1243 | this.leaveEditMode(); | ||
1244 | return false; | ||
1245 | }, | ||
1246 | |||
1247 | /** @id Ajax.InPlaceEditor.prototype.onFailure */ | ||
1248 | onFailure: function (transport) { | ||
1249 | this.options.onFailure(transport); | ||
1250 | if (this.oldInnerHTML) { | ||
1251 | this.element.innerHTML = this.oldInnerHTML; | ||
1252 | this.oldInnerHTML = null; | ||
1253 | } | ||
1254 | return false; | ||
1255 | }, | ||
1256 | |||
1257 | /** @id Ajax.InPlaceEditor.prototype.onSubmit */ | ||
1258 | onSubmit: function () { | ||
1259 | // onLoading resets these so we need to save them away for the Ajax call | ||
1260 | var form = this.form; | ||
1261 | var value = this.editField.value; | ||
1262 | |||
1263 | // do this first, sometimes the ajax call returns before we get a | ||
1264 | // chance to switch on Saving which means this will actually switch on | ||
1265 | // Saving *after* we have left edit mode causing Saving to be | ||
1266 | // displayed indefinitely | ||
1267 | this.onLoading(); | ||
1268 | |||
1269 | new Ajax.Updater( | ||
1270 | { | ||
1271 | success: this.element, | ||
1272 | // dont update on failure (this could be an option) | ||
1273 | failure: null | ||
1274 | }, | ||
1275 | this.url, | ||
1276 | MochiKit.Base.update({ | ||
1277 | parameters: this.options.callback(form, value), | ||
1278 | onComplete: MochiKit.Base.bind(this.onComplete, this), | ||
1279 | onFailure: MochiKit.Base.bind(this.onFailure, this) | ||
1280 | }, this.options.ajaxOptions) | ||
1281 | ); | ||
1282 | // stop the event to avoid a page refresh in Safari | ||
1283 | if (arguments.length > 1) { | ||
1284 | arguments[0].stop(); | ||
1285 | } | ||
1286 | return false; | ||
1287 | }, | ||
1288 | |||
1289 | /** @id Ajax.InPlaceEditor.prototype.onLoading */ | ||
1290 | onLoading: function () { | ||
1291 | this.saving = true; | ||
1292 | this.removeForm(); | ||
1293 | this.leaveHover(); | ||
1294 | this.showSaving(); | ||
1295 | }, | ||
1296 | |||
1297 | /** @id Ajax.InPlaceEditor.prototype.onSaving */ | ||
1298 | showSaving: function () { | ||
1299 | this.oldInnerHTML = this.element.innerHTML; | ||
1300 | this.element.innerHTML = this.options.savingText; | ||
1301 | MochiKit.DOM.addElementClass(this.element, this.options.savingClassName); | ||
1302 | this.element.style.backgroundColor = this.originalBackground; | ||
1303 | MochiKit.Style.showElement(this.element); | ||
1304 | }, | ||
1305 | |||
1306 | /** @id Ajax.InPlaceEditor.prototype.removeForm */ | ||
1307 | removeForm: function () { | ||
1308 | if (this.form) { | ||
1309 | if (this.form.parentNode) { | ||
1310 | MochiKit.DOM.removeElement(this.form); | ||
1311 | } | ||
1312 | this.form = null; | ||
1313 | } | ||
1314 | }, | ||
1315 | |||
1316 | /** @id Ajax.InPlaceEditor.prototype.enterHover */ | ||
1317 | enterHover: function () { | ||
1318 | if (this.saving) { | ||
1319 | return; | ||
1320 | } | ||
1321 | this.element.style.backgroundColor = this.options.highlightcolor; | ||
1322 | if (this.effect) { | ||
1323 | this.effect.cancel(); | ||
1324 | } | ||
1325 | MochiKit.DOM.addElementClass(this.element, this.options.hoverClassName) | ||
1326 | }, | ||
1327 | |||
1328 | /** @id Ajax.InPlaceEditor.prototype.leaveHover */ | ||
1329 | leaveHover: function () { | ||
1330 | if (this.options.backgroundColor) { | ||
1331 | this.element.style.backgroundColor = this.oldBackground; | ||
1332 | } | ||
1333 | MochiKit.DOM.removeElementClass(this.element, this.options.hoverClassName) | ||
1334 | if (this.saving) { | ||
1335 | return; | ||
1336 | } | ||
1337 | this.effect = new MochiKit.Visual.Highlight(this.element, { | ||
1338 | startcolor: this.options.highlightcolor, | ||
1339 | endcolor: this.options.highlightendcolor, | ||
1340 | restorecolor: this.originalBackground | ||
1341 | }); | ||
1342 | }, | ||
1343 | |||
1344 | /** @id Ajax.InPlaceEditor.prototype.leaveEditMode */ | ||
1345 | leaveEditMode: function () { | ||
1346 | MochiKit.DOM.removeElementClass(this.element, this.options.savingClassName); | ||
1347 | this.removeForm(); | ||
1348 | this.leaveHover(); | ||
1349 | this.element.style.backgroundColor = this.originalBackground; | ||
1350 | MochiKit.Style.showElement(this.element); | ||
1351 | if (this.options.externalControl) { | ||
1352 | MochiKit.Style.showElement(this.options.externalControl); | ||
1353 | } | ||
1354 | this.editing = false; | ||
1355 | this.saving = false; | ||
1356 | this.oldInnerHTML = null; | ||
1357 | this.onLeaveEditMode(); | ||
1358 | }, | ||
1359 | |||
1360 | /** @id Ajax.InPlaceEditor.prototype.onComplete */ | ||
1361 | onComplete: function (transport) { | ||
1362 | this.leaveEditMode(); | ||
1363 | MochiKit.Base.bind(this.options.onComplete, this)(transport, this.element); | ||
1364 | }, | ||
1365 | |||
1366 | /** @id Ajax.InPlaceEditor.prototype.onEnterEditMode */ | ||
1367 | onEnterEditMode: function () {}, | ||
1368 | |||
1369 | /** @id Ajax.InPlaceEditor.prototype.onLeaveEditMode */ | ||
1370 | onLeaveEditMode: function () {}, | ||
1371 | |||
1372 | /** @id Ajax.InPlaceEditor.prototype.dispose */ | ||
1373 | dispose: function () { | ||
1374 | if (this.oldInnerHTML) { | ||
1375 | this.element.innerHTML = this.oldInnerHTML; | ||
1376 | } | ||
1377 | this.leaveEditMode(); | ||
1378 | MochiKit.Signal.disconnect(this.onclickListener); | ||
1379 | MochiKit.Signal.disconnect(this.mouseoverListener); | ||
1380 | MochiKit.Signal.disconnect(this.mouseoutListener); | ||
1381 | if (this.options.externalControl) { | ||
1382 | MochiKit.Signal.disconnect(this.onclickListenerExternal); | ||
1383 | MochiKit.Signal.disconnect(this.mouseoverListenerExternal); | ||
1384 | MochiKit.Signal.disconnect(this.mouseoutListenerExternal); | ||
1385 | } | ||
1386 | } | ||
1387 | }; | ||
1388 | |||