##// END OF EJS Templates
Upgraded to Prototype 1.6.0.1....
Jean-Philippe Lang -
r1580:75a5dbd01dd4
parent child
Show More
@@ -28,11 +28,11 ContextMenu.prototype = {
28 RightClick: function(e) {
28 RightClick: function(e) {
29 this.hideMenu();
29 this.hideMenu();
30 // do not show the context menu on links
30 // do not show the context menu on links
31 if (Event.findElement(e, 'a') != document) { return; }
31 if (Event.findElement(e, 'a') != document && Event.findElement(e, 'a') != undefined) { return; }
32 // right-click simulated by Alt+Click with Opera
32 // right-click simulated by Alt+Click with Opera
33 if (window.opera && !e.altKey) { return; }
33 if (window.opera && !e.altKey) { return; }
34 var tr = Event.findElement(e, 'tr');
34 var tr = Event.findElement(e, 'tr');
35 if ((tr == document) || !tr.hasClassName('hascontextmenu')) { return; }
35 if (tr == document || tr == undefined || !tr.hasClassName('hascontextmenu')) { return; }
36 Event.stop(e);
36 Event.stop(e);
37 if (!this.isSelected(tr)) {
37 if (!this.isSelected(tr)) {
38 this.unselectAll();
38 this.unselectAll();
@@ -44,14 +44,14 ContextMenu.prototype = {
44
44
45 Click: function(e) {
45 Click: function(e) {
46 this.hideMenu();
46 this.hideMenu();
47 if (Event.findElement(e, 'a') != document) { return; }
47 if (Event.findElement(e, 'a') != document && Event.findElement(e, 'a') != undefined ) { return; }
48 if (window.opera && e.altKey) { return; }
48 if (window.opera && e.altKey) { return; }
49 if (Event.isLeftClick(e) || (navigator.appVersion.match(/\bMSIE\b/))) {
49 if (Event.isLeftClick(e) || (navigator.appVersion.match(/\bMSIE\b/))) {
50 var tr = Event.findElement(e, 'tr');
50 var tr = Event.findElement(e, 'tr');
51 if (tr!=document && tr.hasClassName('hascontextmenu')) {
51 if (tr!=document && tr.hasClassName('hascontextmenu')) {
52 // a row was clicked, check if the click was on checkbox
52 // a row was clicked, check if the click was on checkbox
53 var box = Event.findElement(e, 'input');
53 var box = Event.findElement(e, 'input');
54 if (box!=document) {
54 if (box!=document && box!=undefined) {
55 // a checkbox may be clicked
55 // a checkbox may be clicked
56 if (box.checked) {
56 if (box.checked) {
57 tr.addClassName('context-menu-selection');
57 tr.addClassName('context-menu-selection');
This diff has been collapsed as it changes many lines, (832 lines changed) Show them Hide them
@@ -1,6 +1,6
1 // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
1 // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 // (c) 2005, 2006 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
2 // (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3 // (c) 2005, 2006 Jon Tirsen (http://www.tirsen.com)
3 // (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
4 // Contributors:
4 // Contributors:
5 // Richard Livsey
5 // Richard Livsey
6 // Rahul Bhargava
6 // Rahul Bhargava
@@ -38,16 +38,17 if(typeof Effect == 'undefined')
38 throw("controls.js requires including script.aculo.us' effects.js library");
38 throw("controls.js requires including script.aculo.us' effects.js library");
39
39
40 var Autocompleter = {}
40 var Autocompleter = { }
41 Autocompleter.Base = function() {};
41 Autocompleter.Base = Class.create({
42 Autocompleter.Base.prototype = {
43 baseInitialize: function(element, update, options) {
42 baseInitialize: function(element, update, options) {
44 this.element = $(element);
43 element = $(element)
44 this.element = element;
45 this.update = $(update);
45 this.update = $(update);
46 this.hasFocus = false;
46 this.hasFocus = false;
47 this.changed = false;
47 this.changed = false;
48 this.active = false;
48 this.active = false;
49 this.index = 0;
49 this.index = 0;
50 this.entryCount = 0;
50 this.entryCount = 0;
51 this.oldElementValue = this.element.value;
51
52
52 if(this.setOptions)
53 if(this.setOptions)
53 this.setOptions(options);
54 this.setOptions(options);
@@ -74,6 +75,9 Autocompleter.Base.prototype = {
74
75
75 if(typeof(this.options.tokens) == 'string')
76 if(typeof(this.options.tokens) == 'string')
76 this.options.tokens = new Array(this.options.tokens);
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 this.observer = null;
82 this.observer = null;
79
83
@@ -81,15 +85,14 Autocompleter.Base.prototype = {
81
85
82 Element.hide(this.update);
86 Element.hide(this.update);
83
87
84 Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
88 Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
85 Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
89 Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
86 },
90 },
87
91
88 show: function() {
92 show: function() {
89 if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
93 if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
90 if(!this.iefix &&
94 if(!this.iefix &&
91 (navigator.appVersion.indexOf('MSIE')>0) &&
95 (Prototype.Browser.IE) &&
92 (navigator.userAgent.indexOf('Opera')<0) &&
93 (Element.getStyle(this.update, 'position')=='absolute')) {
96 (Element.getStyle(this.update, 'position')=='absolute')) {
94 new Insertion.After(this.update,
97 new Insertion.After(this.update,
95 '<iframe id="' + this.update.id + '_iefix" '+
98 '<iframe id="' + this.update.id + '_iefix" '+
@@ -139,17 +142,17 Autocompleter.Base.prototype = {
139 case Event.KEY_UP:
142 case Event.KEY_UP:
140 this.markPrevious();
143 this.markPrevious();
141 this.render();
144 this.render();
142 if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
145 Event.stop(event);
143 return;
146 return;
144 case Event.KEY_DOWN:
147 case Event.KEY_DOWN:
145 this.markNext();
148 this.markNext();
146 this.render();
149 this.render();
147 if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
150 Event.stop(event);
148 return;
151 return;
149 }
152 }
150 else
153 else
151 if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
154 if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
152 (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
155 (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
153
156
154 this.changed = true;
157 this.changed = true;
155 this.hasFocus = true;
158 this.hasFocus = true;
@@ -195,7 +198,6 Autocompleter.Base.prototype = {
195 this.index==i ?
198 this.index==i ?
196 Element.addClassName(this.getEntry(i),"selected") :
199 Element.addClassName(this.getEntry(i),"selected") :
197 Element.removeClassName(this.getEntry(i),"selected");
200 Element.removeClassName(this.getEntry(i),"selected");
198
199 if(this.hasFocus) {
201 if(this.hasFocus) {
200 this.show();
202 this.show();
201 this.active = true;
203 this.active = true;
@@ -238,21 +240,22 Autocompleter.Base.prototype = {
238 }
240 }
239 var value = '';
241 var value = '';
240 if (this.options.select) {
242 if (this.options.select) {
241 var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
243 var nodes = $(selectedElement).select('.' + this.options.select) || [];
242 if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
244 if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
243 } else
245 } else
244 value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
246 value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
245
247
246 var lastTokenPos = this.findLastToken();
248 var bounds = this.getTokenBounds();
247 if (lastTokenPos != -1) {
249 if (bounds[0] != -1) {
248 var newValue = this.element.value.substr(0, lastTokenPos + 1);
250 var newValue = this.element.value.substr(0, bounds[0]);
249 var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
251 var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
250 if (whitespace)
252 if (whitespace)
251 newValue += whitespace[0];
253 newValue += whitespace[0];
252 this.element.value = newValue + value;
254 this.element.value = newValue + value + this.element.value.substr(bounds[1]);
253 } else {
255 } else {
254 this.element.value = value;
256 this.element.value = value;
255 }
257 }
258 this.oldElementValue = this.element.value;
256 this.element.focus();
259 this.element.focus();
257
260
258 if (this.options.afterUpdateElement)
261 if (this.options.afterUpdateElement)
@@ -296,39 +299,48 Autocompleter.Base.prototype = {
296
299
297 onObserverEvent: function() {
300 onObserverEvent: function() {
298 this.changed = false;
301 this.changed = false;
302 this.tokenBounds = null;
299 if(this.getToken().length>=this.options.minChars) {
303 if(this.getToken().length>=this.options.minChars) {
300 this.startIndicator();
301 this.getUpdatedChoices();
304 this.getUpdatedChoices();
302 } else {
305 } else {
303 this.active = false;
306 this.active = false;
304 this.hide();
307 this.hide();
305 }
308 }
309 this.oldElementValue = this.element.value;
306 },
310 },
307
311
308 getToken: function() {
312 getToken: function() {
309 var tokenPos = this.findLastToken();
313 var bounds = this.getTokenBounds();
310 if (tokenPos != -1)
314 return this.element.value.substring(bounds[0], bounds[1]).strip();
311 var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
315 },
312 else
316
313 var ret = this.element.value;
317 getTokenBounds: function() {
314
318 if (null != this.tokenBounds) return this.tokenBounds;
315 return /\n/.test(ret) ? '' : ret;
319 var value = this.element.value;
316 },
320 if (value.strip().empty()) return [-1, 0];
317
321 var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
318 findLastToken: function() {
322 var offset = (diff == this.oldElementValue.length ? 1 : 0);
319 var lastTokenPos = -1;
323 var prevTokenPos = -1, nextTokenPos = value.length;
320
324 var tp;
321 for (var i=0; i<this.options.tokens.length; i++) {
325 for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
322 var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
326 tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
323 if (thisTokenPos > lastTokenPos)
327 if (tp > prevTokenPos) prevTokenPos = tp;
324 lastTokenPos = thisTokenPos;
328 tp = value.indexOf(this.options.tokens[index], diff + offset);
325 }
329 if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
326 return lastTokenPos;
330 }
327 }
331 return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
328 }
332 }
333 });
329
334
330 Ajax.Autocompleter = Class.create();
335 Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
331 Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
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 initialize: function(element, update, url, options) {
344 initialize: function(element, update, url, options) {
333 this.baseInitialize(element, update, options);
345 this.baseInitialize(element, update, options);
334 this.options.asynchronous = true;
346 this.options.asynchronous = true;
@@ -338,7 +350,9 Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.pro
338 },
350 },
339
351
340 getUpdatedChoices: function() {
352 getUpdatedChoices: function() {
341 entry = encodeURIComponent(this.options.paramName) + '=' +
353 this.startIndicator();
354
355 var entry = encodeURIComponent(this.options.paramName) + '=' +
342 encodeURIComponent(this.getToken());
356 encodeURIComponent(this.getToken());
343
357
344 this.options.parameters = this.options.callback ?
358 this.options.parameters = this.options.callback ?
@@ -353,7 +367,6 Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.pro
353 onComplete: function(request) {
367 onComplete: function(request) {
354 this.updateChoices(request.responseText);
368 this.updateChoices(request.responseText);
355 }
369 }
356
357 });
370 });
358
371
359 // The local array autocompleter. Used when you'd prefer to
372 // The local array autocompleter. Used when you'd prefer to
@@ -391,8 +404,7 Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.pro
391 // In that case, the other options above will not apply unless
404 // In that case, the other options above will not apply unless
392 // you support them.
405 // you support them.
393
406
394 Autocompleter.Local = Class.create();
407 Autocompleter.Local = Class.create(Autocompleter.Base, {
395 Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
396 initialize: function(element, update, array, options) {
408 initialize: function(element, update, array, options) {
397 this.baseInitialize(element, update, options);
409 this.baseInitialize(element, update, options);
398 this.options.array = array;
410 this.options.array = array;
@@ -452,9 +464,8 Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
452 }
464 }
453 });
465 });
454
466
455 // AJAX in-place editor
467 // AJAX in-place editor and collection editor
456 //
468 // Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
457 // see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
458
469
459 // Use this if you notice weird scrolling problems on some browsers,
470 // Use this if you notice weird scrolling problems on some browsers,
460 // the DOM might be a bit confused when this gets called so do this
471 // the DOM might be a bit confused when this gets called so do this
@@ -465,353 +476,472 Field.scrollFreeActivate = function(field) {
465 }, 1);
476 }, 1);
466 }
477 }
467
478
468 Ajax.InPlaceEditor = Class.create();
479 Ajax.InPlaceEditor = Class.create({
469 Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
470 Ajax.InPlaceEditor.prototype = {
471 initialize: function(element, url, options) {
480 initialize: function(element, url, options) {
472 this.url = url;
481 this.url = url;
473 this.element = $(element);
482 this.element = element = $(element);
474
483 this.prepareOptions();
475 this.options = Object.extend({
484 this._controls = { };
476 paramName: "value",
485 arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
477 okButton: true,
486 Object.extend(this.options, options || { });
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) {
487 if (!this.options.formId && this.element.id) {
508 this.options.formId = this.element.id + "-inplaceeditor";
488 this.options.formId = this.element.id + '-inplaceeditor';
509 if ($(this.options.formId)) {
489 if ($(this.options.formId))
510 // there's already a form with that name, don't specify an id
490 this.options.formId = '';
511 this.options.formId = null;
512 }
491 }
513 }
492 if (this.options.externalControl)
514
515 if (this.options.externalControl) {
516 this.options.externalControl = $(this.options.externalControl);
493 this.options.externalControl = $(this.options.externalControl);
517 }
494 if (!this.options.externalControl)
518
495 this.options.externalControlOnly = false;
519 this.originalBackground = Element.getStyle(this.element, 'background-color');
496 this._originalBackground = this.element.getStyle('background-color') || 'transparent';
520 if (!this.originalBackground) {
521 this.originalBackground = "transparent";
522 }
523
524 this.element.title = this.options.clickToEditText;
497 this.element.title = this.options.clickToEditText;
525
498 this._boundCancelHandler = this.handleFormCancellation.bind(this);
526 this.onclickListener = this.enterEditMode.bindAsEventListener(this);
499 this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
527 this.mouseoverListener = this.enterHover.bindAsEventListener(this);
500 this._boundFailureHandler = this.handleAJAXFailure.bind(this);
528 this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
501 this._boundSubmitHandler = this.handleFormSubmission.bind(this);
529 Event.observe(this.element, 'click', this.onclickListener);
502 this._boundWrapperHandler = this.wrapUp.bind(this);
530 Event.observe(this.element, 'mouseover', this.mouseoverListener);
503 this.registerListeners();
531 Event.observe(this.element, 'mouseout', this.mouseoutListener);
504 },
532 if (this.options.externalControl) {
505 checkForEscapeOrReturn: function(e) {
533 Event.observe(this.options.externalControl, 'click', this.onclickListener);
506 if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
534 Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
507 if (Event.KEY_ESC == e.keyCode)
535 Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
508 this.handleFormCancellation(e);
536 }
509 else if (Event.KEY_RETURN == e.keyCode)
537 },
510 this.handleFormSubmission(e);
538 enterEditMode: function(evt) {
511 },
539 if (this.saving) return;
512 createControl: function(mode, handler, extraClasses) {
540 if (this.editing) return;
513 var control = this.options[mode + 'Control'];
541 this.editing = true;
514 var text = this.options[mode + 'Text'];
542 this.onEnterEditMode();
515 if ('button' == control) {
543 if (this.options.externalControl) {
516 var btn = document.createElement('input');
544 Element.hide(this.options.externalControl);
517 btn.type = 'submit';
545 }
518 btn.value = text;
546 Element.hide(this.element);
519 btn.className = 'editor_' + mode + '_button';
547 this.createForm();
520 if ('cancel' == mode)
548 this.element.parentNode.insertBefore(this.form, this.element);
521 btn.onclick = this._boundCancelHandler;
549 if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
522 this._form.appendChild(btn);
550 // stop the event to avoid a page refresh in Safari
523 this._controls[mode] = btn;
551 if (evt) {
524 } else if ('link' == control) {
552 Event.stop(evt);
525 var link = document.createElement('a');
553 }
526 link.href = '#';
554 return false;
527 link.appendChild(document.createTextNode(text));
555 },
528 link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
556 createForm: function() {
529 link.className = 'editor_' + mode + '_link';
557 this.form = document.createElement("form");
530 if (extraClasses)
558 this.form.id = this.options.formId;
531 link.className += ' ' + extraClasses;
559 Element.addClassName(this.form, this.options.formClassName)
532 this._form.appendChild(link);
560 this.form.onsubmit = this.onSubmit.bind(this);
533 this._controls[mode] = link;
561
562 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 }
534 }
585 },
535 },
586 hasHTMLLineBreaks: function(string) {
587 if (!this.options.handleLineBreaks) return false;
588 return string.match(/<br/i) || string.match(/<p>/i);
589 },
590 convertHTMLLineBreaks: function(string) {
591 return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
592 },
593 createEditField: function() {
536 createEditField: function() {
594 var text;
537 var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
595 if(this.options.loadTextURL) {
538 var fld;
596 text = this.options.loadingText;
539 if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
597 } else {
540 fld = document.createElement('input');
598 text = this.getText();
541 fld.type = 'text';
599 }
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;
542 var size = this.options.size || this.options.cols || 0;
613 if (size != 0) textField.size = size;
543 if (0 < size) fld.size = size;
614 if (this.options.submitOnBlur)
615 textField.onblur = this.onSubmit.bind(this);
616 this.editField = textField;
617 } else {
544 } else {
618 this.options.textarea = true;
545 fld = document.createElement('textarea');
619 var textArea = document.createElement("textarea");
546 fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
620 textArea.obj = this;
547 fld.cols = this.options.cols || 40;
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 }
548 }
630
549 fld.name = this.options.paramName;
631 if(this.options.loadTextURL) {
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)
632 this.loadExternalText();
556 this.loadExternalText();
633 }
557 this._form.appendChild(this._controls.editor);
634 this.form.appendChild(this.editField);
558 },
559 createForm: function() {
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;
570 this.createEditField();
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);
599 },
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');
635 },
605 },
636 getText: function() {
606 getText: function() {
637 return this.element.innerHTML;
607 return this.element.innerHTML;
638 },
608 },
639 loadExternalText: function() {
609 handleAJAXFailure: function(transport) {
640 Element.addClassName(this.form, this.options.loadingClassName);
610 this.triggerCallback('onFailure', transport);
641 this.editField.disabled = true;
611 if (this._oldInnerHTML) {
642 new Ajax.Request(
612 this.element.innerHTML = this._oldInnerHTML;
643 this.options.loadTextURL,
613 this._oldInnerHTML = null;
644 Object.extend({
614 }
645 asynchronous: true,
615 },
646 onComplete: this.onLoadedExternalText.bind(this)
616 handleFormCancellation: function(e) {
647 }, this.options.ajaxOptions)
617 this.wrapUp();
648 );
618 if (e) Event.stop(e);
649 },
619 },
650 onLoadedExternalText: function(transport) {
620 handleFormSubmission: function(e) {
651 Element.removeClassName(this.form, this.options.loadingClassName);
621 var form = this._form;
652 this.editField.disabled = false;
622 var value = $F(this._controls.editor);
653 this.editField.value = transport.responseText.stripTags();
623 this.prepareSubmission();
654 Field.scrollFreeActivate(this.editField);
624 var params = this.options.callback(form, value) || '';
655 },
625 if (Object.isString(params))
656 onclickCancel: function() {
626 params = params.toQueryParams();
657 this.onComplete();
627 params.editorId = this.element.id;
658 this.leaveEditMode();
628 if (this.options.htmlResponse) {
659 return false;
629 var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
660 },
630 Object.extend(options, {
661 onFailure: function(transport) {
631 parameters: params,
662 this.options.onFailure(transport);
632 onComplete: this._boundWrapperHandler,
663 if (this.oldInnerHTML) {
633 onFailure: this._boundFailureHandler
664 this.element.innerHTML = this.oldInnerHTML;
634 });
665 this.oldInnerHTML = null;
635 new Ajax.Updater({ success: this.element }, this.url, options);
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 {
636 } else {
689 new Ajax.Updater(
637 var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
690 { success: this.element,
638 Object.extend(options, {
691 // don't update on failure (this could be an option)
639 parameters: params,
692 failure: null },
640 onComplete: this._boundWrapperHandler,
693 this.url, Object.extend({
641 onFailure: this._boundFailureHandler
694 parameters: this.options.callback(form, value),
642 });
695 onComplete: this.onComplete.bind(this),
643 new Ajax.Request(this.url, options);
696 onFailure: this.onFailure.bind(this)
644 }
697 }, this.options.ajaxOptions));
645 if (e) Event.stop(e);
698 }
646 },
699 // stop the event to avoid a page refresh in Safari
647 leaveEditMode: function() {
700 if (arguments.length > 1) {
648 this.element.removeClassName(this.options.savingClassName);
701 Event.stop(arguments[0]);
649 this.removeForm();
702 }
650 this.leaveHover();
703 return false;
651 this.element.style.backgroundColor = this._originalBackground;
704 },
652 this.element.show();
705 onLoading: function() {
653 if (this.options.externalControl)
706 this.saving = true;
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');
665 },
666 loadExternalText: function() {
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 this.removeForm();
700 this.removeForm();
708 this.leaveHover();
701 this.leaveHover();
709 this.showSaving();
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 showSaving: function() {
722 showSaving: function() {
712 this.oldInnerHTML = this.element.innerHTML;
723 this._oldInnerHTML = this.element.innerHTML;
713 this.element.innerHTML = this.options.savingText;
724 this.element.innerHTML = this.options.savingText;
714 Element.addClassName(this.element, this.options.savingClassName);
725 this.element.addClassName(this.options.savingClassName);
715 this.element.style.backgroundColor = this.originalBackground;
726 this.element.style.backgroundColor = this._originalBackground;
716 Element.show(this.element);
727 this.element.show();
717 },
728 },
718 removeForm: function() {
729 triggerCallback: function(cbName, arg) {
719 if(this.form) {
730 if ('function' == typeof this.options[cbName]) {
720 if (this.form.parentNode) Element.remove(this.form);
731 this.options[cbName](this, arg);
721 this.form = null;
722 }
732 }
723 },
733 },
724 enterHover: function() {
734 unregisterListeners: function() {
725 if (this.saving) return;
735 $H(this._listeners).each(function(pair) {
726 this.element.style.backgroundColor = this.options.highlightcolor;
736 if (!this.options.externalControlOnly)
727 if (this.effect) {
737 this.element.stopObserving(pair.key, pair.value);
728 this.effect.cancel();
738 if (this.options.externalControl)
729 }
739 this.options.externalControl.stopObserving(pair.key, pair.value);
730 Element.addClassName(this.element, this.options.hoverClassName)
740 }.bind(this));
731 },
741 },
732 leaveHover: function() {
742 wrapUp: function(transport) {
733 if (this.options.backgroundColor) {
743 this.leaveEditMode();
734 this.element.style.backgroundColor = this.oldBackground;
744 // Can't use triggerCallback due to backward compatibility: requires
745 // binding + direct element
746 this._boundComplete(transport, this.element);
735 }
747 }
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 });
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);
743 },
758 },
744 leaveEditMode: function() {
759
745 Element.removeClassName(this.element, this.options.savingClassName);
760 createEditField: function() {
746 this.removeForm();
761 var list = document.createElement('select');
747 this.leaveHover();
762 list.name = this.options.paramName;
748 this.element.style.backgroundColor = this.originalBackground;
763 list.size = 1;
749 Element.show(this.element);
764 this._controls.editor = list;
750 if (this.options.externalControl) {
765 this._collection = this.options.collection || [];
751 Element.show(this.options.externalControl);
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
788 });
789 new Ajax.Request(this.options.loadCollectionURL, options);
790 },
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;
801 tempOption.update((text || '').stripScripts().stripTags());
754 this.saving = false;
755 this.oldInnerHTML = null;
756 this.onLeaveEditMode();
757 },
802 },
758 onComplete: function(transport) {
803
759 this.leaveEditMode();
804 checkForExternalText: function() {
760 this.options.onComplete.bind(this)(transport, this.element);
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 }
777 }
778 };
779
811
780 Ajax.InPlaceCollectionEditor = Class.create();
812 loadExternalText: function() {
781 Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
813 this.showLoadingText(this.options.loadingText);
782 Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
814 var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
783 createEditField: function() {
815 Object.extend(options, {
784 if (!this.cached_selectTag) {
816 parameters: 'editorId=' + encodeURIComponent(this.element.id),
785 var selectTag = document.createElement("select");
817 onComplete: Prototype.emptyFunction,
786 var collection = this.options.collection || [];
818 onSuccess: function(transport) {
787 var optionTag;
819 this._text = transport.responseText.strip();
788 collection.each(function(e,i) {
820 this.buildOptionList();
789 optionTag = document.createElement("option");
821 }.bind(this),
790 optionTag.value = (e instanceof Array) ? e[0] : e;
822 onFailure: this.onFailure
791 if((typeof this.options.value == 'undefined') &&
823 });
792 ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
824 new Ajax.Request(this.options.loadTextURL, options);
793 if(this.options.value==optionTag.value) optionTag.selected = true;
825 },
794 optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
826
795 selectTag.appendChild(optionTag);
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);
796 }.bind(this));
844 }.bind(this));
797 this.cached_selectTag = selectTag;
845 this._controls.editor.disabled = false;
846 Field.scrollFreeActivate(this._controls.editor);
798 }
847 }
848 });
849
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;
869 Object.extend(Ajax.InPlaceEditor, {
801 if(this.options.loadTextURL) this.loadExternalText();
870 DefaultOptions: {
802 this.form.appendChild(this.editField);
871 ajaxOptions: { },
803 this.options.callback = function(form, value) {
872 autoRows: 3, // Use when multi-line w/ rows == 1
804 return "value=" + encodeURIComponent(value);
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
936 Ajax.InPlaceCollectionEditor.DefaultOptions = {
937 loadingCollectionText: 'Loading options...'
938 };
939
809 // Delayed observer, like Form.Element.Observer,
940 // Delayed observer, like Form.Element.Observer,
810 // but waits for delay after last key input
941 // but waits for delay after last key input
811 // Ideal for live-search fields
942 // Ideal for live-search fields
812
943
813 Form.Element.DelayedObserver = Class.create();
944 Form.Element.DelayedObserver = Class.create({
814 Form.Element.DelayedObserver.prototype = {
815 initialize: function(element, delay, callback) {
945 initialize: function(element, delay, callback) {
816 this.delay = delay || 0.5;
946 this.delay = delay || 0.5;
817 this.element = $(element);
947 this.element = $(element);
@@ -830,4 +960,4 Form.Element.DelayedObserver.prototype = {
830 this.timer = null;
960 this.timer = null;
831 this.callback(this.element, $F(this.element));
961 this.callback(this.element, $F(this.element));
832 }
962 }
833 };
963 });
@@ -1,10 +1,10
1 // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
1 // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 // (c) 2005, 2006 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
2 // (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
3 //
3 //
4 // script.aculo.us is freely distributable under the terms of an MIT-style license.
4 // script.aculo.us is freely distributable under the terms of an MIT-style license.
5 // For details, see the script.aculo.us web site: http://script.aculo.us/
5 // For details, see the script.aculo.us web site: http://script.aculo.us/
6
6
7 if(typeof Effect == 'undefined')
7 if(Object.isUndefined(Effect))
8 throw("dragdrop.js requires including script.aculo.us' effects.js library");
8 throw("dragdrop.js requires including script.aculo.us' effects.js library");
9
9
10 var Droppables = {
10 var Droppables = {
@@ -26,8 +26,7 var Droppables = {
26 if(options.containment) {
26 if(options.containment) {
27 options._containers = [];
27 options._containers = [];
28 var containment = options.containment;
28 var containment = options.containment;
29 if((typeof containment == 'object') &&
29 if(Object.isArray(containment)) {
30 (containment.constructor == Array)) {
31 containment.each( function(c) { options._containers.push($(c)) });
30 containment.each( function(c) { options._containers.push($(c)) });
32 } else {
31 } else {
33 options._containers.push($(containment));
32 options._containers.push($(containment));
@@ -87,21 +86,23 var Droppables = {
87
86
88 show: function(point, element) {
87 show: function(point, element) {
89 if(!this.drops.length) return;
88 if(!this.drops.length) return;
90 var affected = [];
89 var drop, affected = [];
91
90
92 if(this.last_active) this.deactivate(this.last_active);
93 this.drops.each( function(drop) {
91 this.drops.each( function(drop) {
94 if(Droppables.isAffected(point, element, drop))
92 if(Droppables.isAffected(point, element, drop))
95 affected.push(drop);
93 affected.push(drop);
96 });
94 });
97
95
98 if(affected.length>0) {
96 if(affected.length>0)
99 drop = Droppables.findDeepestChild(affected);
97 drop = Droppables.findDeepestChild(affected);
98
99 if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
100 if (drop) {
100 Position.within(drop.element, point[0], point[1]);
101 Position.within(drop.element, point[0], point[1]);
101 if(drop.onHover)
102 if(drop.onHover)
102 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
103 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
103
104
104 Droppables.activate(drop);
105 if (drop != this.last_active) Droppables.activate(drop);
105 }
106 }
106 },
107 },
107
108
@@ -110,8 +111,10 var Droppables = {
110 Position.prepare();
111 Position.prepare();
111
112
112 if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
113 if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
113 if (this.last_active.onDrop)
114 if (this.last_active.onDrop) {
114 this.last_active.onDrop(element, this.last_active.element, event);
115 this.last_active.onDrop(element, this.last_active.element, event);
116 return true;
117 }
115 },
118 },
116
119
117 reset: function() {
120 reset: function() {
@@ -219,10 +222,7 var Draggables = {
219
222
220 /*--------------------------------------------------------------------------*/
223 /*--------------------------------------------------------------------------*/
221
224
222 var Draggable = Class.create();
225 var Draggable = Class.create({
223 Draggable._dragging = {};
224
225 Draggable.prototype = {
226 initialize: function(element) {
226 initialize: function(element) {
227 var defaults = {
227 var defaults = {
228 handle: false,
228 handle: false,
@@ -233,7 +233,7 Draggable.prototype = {
233 });
233 });
234 },
234 },
235 endeffect: function(element) {
235 endeffect: function(element) {
236 var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
236 var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
237 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
237 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
238 queue: {scope:'_draggable', position:'end'},
238 queue: {scope:'_draggable', position:'end'},
239 afterFinish: function(){
239 afterFinish: function(){
@@ -243,6 +243,7 Draggable.prototype = {
243 },
243 },
244 zindex: 1000,
244 zindex: 1000,
245 revert: false,
245 revert: false,
246 quiet: false,
246 scroll: false,
247 scroll: false,
247 scrollSensitivity: 20,
248 scrollSensitivity: 20,
248 scrollSpeed: 15,
249 scrollSpeed: 15,
@@ -250,7 +251,7 Draggable.prototype = {
250 delay: 0
251 delay: 0
251 };
252 };
252
253
253 if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
254 if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
254 Object.extend(defaults, {
255 Object.extend(defaults, {
255 starteffect: function(element) {
256 starteffect: function(element) {
256 element._opacity = Element.getOpacity(element);
257 element._opacity = Element.getOpacity(element);
@@ -263,7 +264,7 Draggable.prototype = {
263
264
264 this.element = $(element);
265 this.element = $(element);
265
266
266 if(options.handle && (typeof options.handle == 'string'))
267 if(options.handle && Object.isString(options.handle))
267 this.handle = this.element.down('.'+options.handle, 0);
268 this.handle = this.element.down('.'+options.handle, 0);
268
269
269 if(!this.handle) this.handle = $(options.handle);
270 if(!this.handle) this.handle = $(options.handle);
@@ -276,7 +277,6 Draggable.prototype = {
276
277
277 Element.makePositioned(this.element); // fix IE
278 Element.makePositioned(this.element); // fix IE
278
279
279 this.delta = this.currentDelta();
280 this.options = options;
280 this.options = options;
281 this.dragging = false;
281 this.dragging = false;
282
282
@@ -298,17 +298,17 Draggable.prototype = {
298 },
298 },
299
299
300 initDrag: function(event) {
300 initDrag: function(event) {
301 if(typeof Draggable._dragging[this.element] != 'undefined' &&
301 if(!Object.isUndefined(Draggable._dragging[this.element]) &&
302 Draggable._dragging[this.element]) return;
302 Draggable._dragging[this.element]) return;
303 if(Event.isLeftClick(event)) {
303 if(Event.isLeftClick(event)) {
304 // abort on form elements, fixes a Firefox issue
304 // abort on form elements, fixes a Firefox issue
305 var src = Event.element(event);
305 var src = Event.element(event);
306 if(src.tagName && (
306 if((tag_name = src.tagName.toUpperCase()) && (
307 src.tagName=='INPUT' ||
307 tag_name=='INPUT' ||
308 src.tagName=='SELECT' ||
308 tag_name=='SELECT' ||
309 src.tagName=='OPTION' ||
309 tag_name=='OPTION' ||
310 src.tagName=='BUTTON' ||
310 tag_name=='BUTTON' ||
311 src.tagName=='TEXTAREA')) return;
311 tag_name=='TEXTAREA')) return;
312
312
313 var pointer = [Event.pointerX(event), Event.pointerY(event)];
313 var pointer = [Event.pointerX(event), Event.pointerY(event)];
314 var pos = Position.cumulativeOffset(this.element);
314 var pos = Position.cumulativeOffset(this.element);
@@ -321,6 +321,8 Draggable.prototype = {
321
321
322 startDrag: function(event) {
322 startDrag: function(event) {
323 this.dragging = true;
323 this.dragging = true;
324 if(!this.delta)
325 this.delta = this.currentDelta();
324
326
325 if(this.options.zindex) {
327 if(this.options.zindex) {
326 this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
328 this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
@@ -329,6 +331,8 Draggable.prototype = {
329
331
330 if(this.options.ghosting) {
332 if(this.options.ghosting) {
331 this._clone = this.element.cloneNode(true);
333 this._clone = this.element.cloneNode(true);
334 this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
335 if (!this.element._originallyAbsolute)
332 Position.absolutize(this.element);
336 Position.absolutize(this.element);
333 this.element.parentNode.insertBefore(this._clone, this.element);
337 this.element.parentNode.insertBefore(this._clone, this.element);
334 }
338 }
@@ -351,8 +355,12 Draggable.prototype = {
351
355
352 updateDrag: function(event, pointer) {
356 updateDrag: function(event, pointer) {
353 if(!this.dragging) this.startDrag(event);
357 if(!this.dragging) this.startDrag(event);
358
359 if(!this.options.quiet){
354 Position.prepare();
360 Position.prepare();
355 Droppables.show(pointer, this.element);
361 Droppables.show(pointer, this.element);
362 }
363
356 Draggables.notify('onDrag', this, event);
364 Draggables.notify('onDrag', this, event);
357
365
358 this.draw(pointer);
366 this.draw(pointer);
@@ -380,7 +388,7 Draggable.prototype = {
380 }
388 }
381
389
382 // fix AppleWebKit rendering
390 // fix AppleWebKit rendering
383 if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
391 if(Prototype.Browser.WebKit) window.scrollBy(0,0);
384
392
385 Event.stop(event);
393 Event.stop(event);
386 },
394 },
@@ -388,20 +396,34 Draggable.prototype = {
388 finishDrag: function(event, success) {
396 finishDrag: function(event, success) {
389 this.dragging = false;
397 this.dragging = false;
390
398
399 if(this.options.quiet){
400 Position.prepare();
401 var pointer = [Event.pointerX(event), Event.pointerY(event)];
402 Droppables.show(pointer, this.element);
403 }
404
391 if(this.options.ghosting) {
405 if(this.options.ghosting) {
406 if (!this.element._originallyAbsolute)
392 Position.relativize(this.element);
407 Position.relativize(this.element);
408 delete this.element._originallyAbsolute;
393 Element.remove(this._clone);
409 Element.remove(this._clone);
394 this._clone = null;
410 this._clone = null;
395 }
411 }
396
412
397 if(success) Droppables.fire(event, this.element);
413 var dropped = false;
414 if(success) {
415 dropped = Droppables.fire(event, this.element);
416 if (!dropped) dropped = false;
417 }
418 if(dropped && this.options.onDropped) this.options.onDropped(this.element);
398 Draggables.notify('onEnd', this, event);
419 Draggables.notify('onEnd', this, event);
399
420
400 var revert = this.options.revert;
421 var revert = this.options.revert;
401 if(revert && typeof revert == 'function') revert = revert(this.element);
422 if(revert && Object.isFunction(revert)) revert = revert(this.element);
402
423
403 var d = this.currentDelta();
424 var d = this.currentDelta();
404 if(revert && this.options.reverteffect) {
425 if(revert && this.options.reverteffect) {
426 if (dropped == 0 || revert != 'failure')
405 this.options.reverteffect(this.element,
427 this.options.reverteffect(this.element,
406 d[1]-this.delta[1], d[0]-this.delta[0]);
428 d[1]-this.delta[1], d[0]-this.delta[0]);
407 } else {
429 } else {
@@ -451,15 +473,15 Draggable.prototype = {
451 }.bind(this));
473 }.bind(this));
452
474
453 if(this.options.snap) {
475 if(this.options.snap) {
454 if(typeof this.options.snap == 'function') {
476 if(Object.isFunction(this.options.snap)) {
455 p = this.options.snap(p[0],p[1],this);
477 p = this.options.snap(p[0],p[1],this);
456 } else {
478 } else {
457 if(this.options.snap instanceof Array) {
479 if(Object.isArray(this.options.snap)) {
458 p = p.map( function(v, i) {
480 p = p.map( function(v, i) {
459 return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
481 return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
460 } else {
482 } else {
461 p = p.map( function(v) {
483 p = p.map( function(v) {
462 return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
484 return (v/this.options.snap).round()*this.options.snap }.bind(this))
463 }
485 }
464 }}
486 }}
465
487
@@ -543,12 +565,13 Draggable.prototype = {
543 }
565 }
544 return { top: T, left: L, width: W, height: H };
566 return { top: T, left: L, width: W, height: H };
545 }
567 }
546 }
568 });
569
570 Draggable._dragging = { };
547
571
548 /*--------------------------------------------------------------------------*/
572 /*--------------------------------------------------------------------------*/
549
573
550 var SortableObserver = Class.create();
574 var SortableObserver = Class.create({
551 SortableObserver.prototype = {
552 initialize: function(element, observer) {
575 initialize: function(element, observer) {
553 this.element = $(element);
576 this.element = $(element);
554 this.observer = observer;
577 this.observer = observer;
@@ -564,7 +587,7 SortableObserver.prototype = {
564 if(this.lastValue != Sortable.serialize(this.element))
587 if(this.lastValue != Sortable.serialize(this.element))
565 this.observer(this.element)
588 this.observer(this.element)
566 }
589 }
567 }
590 });
568
591
569 var Sortable = {
592 var Sortable = {
570 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
593 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
@@ -572,7 +595,7 var Sortable = {
572 sortables: {},
595 sortables: { },
573
596
574 _findRootElement: function(element) {
597 _findRootElement: function(element) {
575 while (element.tagName != "BODY") {
598 while (element.tagName.toUpperCase() != "BODY") {
576 if(element.id && Sortable.sortables[element.id]) return element;
599 if(element.id && Sortable.sortables[element.id]) return element;
577 element = element.parentNode;
600 element = element.parentNode;
578 }
601 }
@@ -612,10 +635,17 var Sortable = {
612 delay: 0,
635 delay: 0,
613 hoverclass: null,
636 hoverclass: null,
614 ghosting: false,
637 ghosting: false,
638 quiet: false,
615 scroll: false,
639 scroll: false,
616 scrollSensitivity: 20,
640 scrollSensitivity: 20,
617 scrollSpeed: 15,
641 scrollSpeed: 15,
618 format: this.SERIALIZE_RULE,
642 format: this.SERIALIZE_RULE,
643
644 // these take arrays of elements or ids and can be
645 // used for better initialization performance
646 elements: false,
647 handles: false,
648
619 onChange: Prototype.emptyFunction,
649 onChange: Prototype.emptyFunction,
620 onUpdate: Prototype.emptyFunction
650 onUpdate: Prototype.emptyFunction
621 }, arguments[1] || {});
651 }, arguments[1] || { });
@@ -626,6 +656,7 var Sortable = {
626 // build options for the draggables
656 // build options for the draggables
627 var options_for_draggable = {
657 var options_for_draggable = {
628 revert: true,
658 revert: true,
659 quiet: options.quiet,
629 scroll: options.scroll,
660 scroll: options.scroll,
630 scrollSpeed: options.scrollSpeed,
661 scrollSpeed: options.scrollSpeed,
631 scrollSensitivity: options.scrollSensitivity,
662 scrollSensitivity: options.scrollSensitivity,
@@ -679,10 +710,9 var Sortable = {
679 options.droppables.push(element);
710 options.droppables.push(element);
680 }
711 }
681
712
682 (this.findElements(element, options) || []).each( function(e) {
713 (options.elements || this.findElements(element, options) || []).each( function(e,i) {
683 // handles are per-draggable
714 var handle = options.handles ? $(options.handles[i]) :
684 var handle = options.handle ?
715 (options.handle ? $(e).select('.' + options.handle)[0] : e);
685 $(e).down('.'+options.handle,0) : e;
686 options.draggables.push(
716 options.draggables.push(
687 new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
717 new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
688 Droppables.add(e, options_for_droppable);
718 Droppables.add(e, options_for_droppable);
This diff has been collapsed as it changes many lines, (596 lines changed) Show them Hide them
@@ -1,4 +1,4
1 // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
1 // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 // Contributors:
2 // Contributors:
3 // Justin Palmer (http://encytemedia.com/)
3 // Justin Palmer (http://encytemedia.com/)
4 // Mark Pilgrim (http://diveintomark.org/)
4 // Mark Pilgrim (http://diveintomark.org/)
@@ -21,7 +21,7 String.prototype.parseColor = function() {
21 }
21 }
22 }
22 }
23 return(color.length==7 ? color : (arguments[0] || this));
23 return (color.length==7 ? color : (arguments[0] || this));
24 }
24 };
25
25
26 /*--------------------------------------------------------------------------*/
26 /*--------------------------------------------------------------------------*/
27
27
@@ -30,7 +30,7 Element.collectTextNodes = function(element) {
30 return (node.nodeType==3 ? node.nodeValue :
30 return (node.nodeType==3 ? node.nodeValue :
31 (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
31 (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
32 }).flatten().join('');
32 }).flatten().join('');
33 }
33 };
34
34
35 Element.collectTextNodesIgnoreClass = function(element, className) {
35 Element.collectTextNodesIgnoreClass = function(element, className) {
36 return $A($(element).childNodes).collect( function(node) {
36 return $A($(element).childNodes).collect( function(node) {
@@ -38,47 +38,18 Element.collectTextNodesIgnoreClass = function(element, className) {
38 ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
38 ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
39 Element.collectTextNodesIgnoreClass(node, className) : ''));
39 Element.collectTextNodesIgnoreClass(node, className) : ''));
40 }).flatten().join('');
40 }).flatten().join('');
41 }
41 };
42
42
43 Element.setContentZoom = function(element, percent) {
43 Element.setContentZoom = function(element, percent) {
44 element = $(element);
44 element = $(element);
45 element.setStyle({fontSize: (percent/100) + 'em'});
45 element.setStyle({fontSize: (percent/100) + 'em'});
46 if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
46 if (Prototype.Browser.WebKit) window.scrollBy(0,0);
47 return element;
48 }
49
50 Element.getOpacity = function(element){
51 element = $(element);
52 var opacity;
53 if (opacity = element.getStyle('opacity'))
54 return parseFloat(opacity);
55 if (opacity = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
56 if(opacity[1]) return parseFloat(opacity[1]) / 100;
57 return 1.0;
58 }
59
60 Element.setOpacity = function(element, value){
61 element= $(element);
62 if (value == 1){
63 element.setStyle({ opacity:
64 (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
65 0.999999 : 1.0 });
66 if(/MSIE/.test(navigator.userAgent) && !window.opera)
67 element.setStyle({filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
68 } else {
69 if(value < 0.00001) value = 0;
70 element.setStyle({opacity: value});
71 if(/MSIE/.test(navigator.userAgent) && !window.opera)
72 element.setStyle(
73 { filter: element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
74 'alpha(opacity='+value*100+')' });
75 }
76 return element;
47 return element;
77 }
48 };
78
49
79 Element.getInlineOpacity = function(element){
50 Element.getInlineOpacity = function(element){
80 return $(element).style.opacity || '';
51 return $(element).style.opacity || '';
81 }
52 };
82
53
83 Element.forceRerendering = function(element) {
54 Element.forceRerendering = function(element) {
84 try {
55 try {
@@ -91,31 +62,63 Element.forceRerendering = function(element) {
91
62
92 /*--------------------------------------------------------------------------*/
63 /*--------------------------------------------------------------------------*/
93
64
94 Array.prototype.call = function() {
95 var args = arguments;
96 this.each(function(f){ f.apply(this, args) });
97 }
98
99 /*--------------------------------------------------------------------------*/
100
101 var Effect = {
65 var Effect = {
102 _elementDoesNotExistError: {
66 _elementDoesNotExistError: {
103 name: 'ElementDoesNotExistError',
67 name: 'ElementDoesNotExistError',
104 message: 'The specified DOM element does not exist, but is required for this effect to operate'
68 message: 'The specified DOM element does not exist, but is required for this effect to operate'
105 },
69 },
70 Transitions: {
71 linear: Prototype.K,
72 sinoidal: function(pos) {
73 return (-Math.cos(pos*Math.PI)/2) + 0.5;
74 },
75 reverse: function(pos) {
76 return 1-pos;
77 },
78 flicker: function(pos) {
79 var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
80 return pos > 1 ? 1 : pos;
81 },
82 wobble: function(pos) {
83 return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
84 },
85 pulse: function(pos, pulses) {
86 pulses = pulses || 5;
87 return (
88 ((pos % (1/pulses)) * pulses).round() == 0 ?
89 ((pos * pulses * 2) - (pos * pulses * 2).floor()) :
90 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
91 );
92 },
93 spring: function(pos) {
94 return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
95 },
96 none: function(pos) {
97 return 0;
98 },
99 full: function(pos) {
100 return 1;
101 }
102 },
103 DefaultOptions: {
104 duration: 1.0, // seconds
105 fps: 100, // 100= assume 66fps max.
106 sync: false, // true for combining
107 from: 0.0,
108 to: 1.0,
109 delay: 0.0,
110 queue: 'parallel'
111 },
106 tagifyText: function(element) {
112 tagifyText: function(element) {
107 if(typeof Builder == 'undefined')
108 throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
109
110 var tagifyStyle = 'position:relative';
113 var tagifyStyle = 'position:relative';
111 if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
114 if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
112
115
113 element = $(element);
116 element = $(element);
114 $A(element.childNodes).each( function(child) {
117 $A(element.childNodes).each( function(child) {
115 if(child.nodeType==3) {
118 if (child.nodeType==3) {
116 child.nodeValue.toArray().each( function(character) {
119 child.nodeValue.toArray().each( function(character) {
117 element.insertBefore(
120 element.insertBefore(
118 Builder.node('span',{style: tagifyStyle},
121 new Element('span', {style: tagifyStyle}).update(
119 character == ' ' ? String.fromCharCode(160) : character),
122 character == ' ' ? String.fromCharCode(160) : character),
120 child);
123 child);
121 });
124 });
@@ -126,7 +129,7 var Effect = {
126 multiple: function(element, effect) {
129 multiple: function(element, effect) {
127 var elements;
130 var elements;
128 if(((typeof element == 'object') ||
131 if (((typeof element == 'object') ||
129 (typeof element == 'function')) &&
132 Object.isFunction(element)) &&
130 (element.length))
133 (element.length))
131 elements = element;
134 elements = element;
132 else
135 else
@@ -158,44 +161,11 var Effect = {
158 }
161 }
159 };
162 };
160
163
161 var Effect2 = Effect; // deprecated
164 Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
162
163 /* ------------- transitions ------------- */
164
165 Effect.Transitions = {
166 linear: Prototype.K,
167 sinoidal: function(pos) {
168 return (-Math.cos(pos*Math.PI)/2) + 0.5;
169 },
170 reverse: function(pos) {
171 return 1-pos;
172 },
173 flicker: function(pos) {
174 return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
175 },
176 wobble: function(pos) {
177 return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
178 },
179 pulse: function(pos, pulses) {
180 pulses = pulses || 5;
181 return (
182 Math.round((pos % (1/pulses)) * pulses) == 0 ?
183 ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) :
184 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
185 );
186 },
187 none: function(pos) {
188 return 0;
189 },
190 full: function(pos) {
191 return 1;
192 }
193 };
194
165
195 /* ------------- core effects ------------- */
166 /* ------------- core effects ------------- */
196
167
197 Effect.ScopedQueue = Class.create();
168 Effect.ScopedQueue = Class.create(Enumerable, {
198 Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
199 initialize: function() {
169 initialize: function() {
200 this.effects = [];
170 this.effects = [];
201 this.interval = null;
171 this.interval = null;
@@ -206,7 +176,7 Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
206 add: function(effect) {
176 add: function(effect) {
207 var timestamp = new Date().getTime();
177 var timestamp = new Date().getTime();
208
178
209 var position = (typeof effect.options.queue == 'string') ?
179 var position = Object.isString(effect.options.queue) ?
210 effect.options.queue : effect.options.queue.position;
180 effect.options.queue : effect.options.queue.position;
211
181
212 switch(position) {
182 switch(position) {
@@ -233,7 +203,7 Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
233 this.effects.push(effect);
203 this.effects.push(effect);
234
204
235 if(!this.interval)
205 if (!this.interval)
236 this.interval = setInterval(this.loop.bind(this), 40);
206 this.interval = setInterval(this.loop.bind(this), 15);
237 },
207 },
238 remove: function(effect) {
208 remove: function(effect) {
239 this.effects = this.effects.reject(function(e) { return e==effect });
209 this.effects = this.effects.reject(function(e) { return e==effect });
@@ -244,46 +214,57 Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
244 },
214 },
245 loop: function() {
215 loop: function() {
246 var timePos = new Date().getTime();
216 var timePos = new Date().getTime();
247 this.effects.invoke('loop', timePos);
217 for(var i=0, len=this.effects.length;i<len;i++)
218 this.effects[i] && this.effects[i].loop(timePos);
248 }
219 }
249 });
220 });
250
221
251 Effect.Queues = {
222 Effect.Queues = {
252 instances: $H(),
223 instances: $H(),
253 get: function(queueName) {
224 get: function(queueName) {
254 if(typeof queueName != 'string') return queueName;
225 if (!Object.isString(queueName)) return queueName;
255
256 if(!this.instances[queueName])
257 this.instances[queueName] = new Effect.ScopedQueue();
258
226
259 return this.instances[queueName];
227 return this.instances.get(queueName) ||
260 }
228 this.instances.set(queueName, new Effect.ScopedQueue());
261 }
229 }
230 };
262 Effect.Queue = Effect.Queues.get('global');
231 Effect.Queue = Effect.Queues.get('global');
263
232
264 Effect.DefaultOptions = {
233 Effect.Base = Class.create({
265 transition: Effect.Transitions.sinoidal,
266 duration: 1.0, // seconds
267 fps: 25.0, // max. 25fps due to Effect.Queue implementation
268 sync: false, // true for combining
269 from: 0.0,
270 to: 1.0,
271 delay: 0.0,
272 queue: 'parallel'
273 }
274
275 Effect.Base = function() {};
276 Effect.Base.prototype = {
277 position: null,
234 position: null,
278 start: function(options) {
235 start: function(options) {
236 function codeForEvent(options,eventName){
237 return (
238 (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
239 (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
240 );
241 }
242 if (options && options.transition === false) options.transition = Effect.Transitions.linear;
279 this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
243 this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
280 this.currentFrame = 0;
244 this.currentFrame = 0;
281 this.state = 'idle';
245 this.state = 'idle';
282 this.startOn = this.options.delay*1000;
246 this.startOn = this.options.delay*1000;
283 this.finishOn = this.startOn + (this.options.duration*1000);
247 this.finishOn = this.startOn+(this.options.duration*1000);
248 this.fromToDelta = this.options.to-this.options.from;
249 this.totalTime = this.finishOn-this.startOn;
250 this.totalFrames = this.options.fps*this.options.duration;
251
252 eval('this.render = function(pos){ '+
253 'if (this.state=="idle"){this.state="running";'+
254 codeForEvent(this.options,'beforeSetup')+
255 (this.setup ? 'this.setup();':'')+
256 codeForEvent(this.options,'afterSetup')+
257 '};if (this.state=="running"){'+
258 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
259 'this.position=pos;'+
260 codeForEvent(this.options,'beforeUpdate')+
261 (this.update ? 'this.update(pos);':'')+
262 codeForEvent(this.options,'afterUpdate')+
263 '}}');
264
284 this.event('beforeStart');
265 this.event('beforeStart');
285 if(!this.options.sync)
266 if (!this.options.sync)
286 Effect.Queues.get(typeof this.options.queue == 'string' ?
267 Effect.Queues.get(Object.isString(this.options.queue) ?
287 'global' : this.options.queue.scope).add(this);
268 'global' : this.options.queue.scope).add(this);
288 },
269 },
289 loop: function(timePos) {
270 loop: function(timePos) {
@@ -296,34 +277,17 Effect.Base.prototype = {
296 this.event('afterFinish');
277 this.event('afterFinish');
297 return;
278 return;
298 }
279 }
299 var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
280 var pos = (timePos - this.startOn) / this.totalTime,
300 var frame = Math.round(pos * this.options.fps * this.options.duration);
281 frame = (pos * this.totalFrames).round();
301 if(frame > this.currentFrame) {
282 if (frame > this.currentFrame) {
302 this.render(pos);
283 this.render(pos);
303 this.currentFrame = frame;
284 this.currentFrame = frame;
304 }
285 }
305 }
286 }
306 },
287 },
307 render: function(pos) {
308 if(this.state == 'idle') {
309 this.state = 'running';
310 this.event('beforeSetup');
311 if(this.setup) this.setup();
312 this.event('afterSetup');
313 }
314 if(this.state == 'running') {
315 if(this.options.transition) pos = this.options.transition(pos);
316 pos *= (this.options.to-this.options.from);
317 pos += this.options.from;
318 this.position = pos;
319 this.event('beforeUpdate');
320 if(this.update) this.update(pos);
321 this.event('afterUpdate');
322 }
323 },
324 cancel: function() {
288 cancel: function() {
325 if(!this.options.sync)
289 if (!this.options.sync)
326 Effect.Queues.get(typeof this.options.queue == 'string' ?
290 Effect.Queues.get(Object.isString(this.options.queue) ?
327 'global' : this.options.queue.scope).remove(this);
291 'global' : this.options.queue.scope).remove(this);
328 this.state = 'finished';
292 this.state = 'finished';
329 },
293 },
@@ -332,12 +296,14 Effect.Base.prototype = {
332 if(this.options[eventName]) this.options[eventName](this);
296 if (this.options[eventName]) this.options[eventName](this);
333 },
297 },
334 inspect: function() {
298 inspect: function() {
335 return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
299 var data = $H();
336 }
300 for(property in this)
301 if (!Object.isFunction(this[property])) data.set(property, this[property]);
302 return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
337 }
303 }
304 });
338
305
339 Effect.Parallel = Class.create();
306 Effect.Parallel = Class.create(Effect.Base, {
340 Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
341 initialize: function(effects) {
307 initialize: function(effects) {
342 this.effects = effects || [];
308 this.effects = effects || [];
343 this.start(arguments[1]);
309 this.start(arguments[1]);
@@ -356,24 +322,34 Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
356 }
322 }
357 });
323 });
358
324
359 Effect.Event = Class.create();
325 Effect.Tween = Class.create(Effect.Base, {
360 Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
326 initialize: function(object, from, to) {
327 object = Object.isString(object) ? $(object) : object;
328 var args = $A(arguments), method = args.last(),
329 options = args.length == 5 ? args[3] : null;
330 this.method = Object.isFunction(method) ? method.bind(object) :
331 Object.isFunction(object[method]) ? object[method].bind(object) :
332 function(value) { object[method] = value };
333 this.start(Object.extend({ from: from, to: to }, options || { }));
334 },
335 update: function(position) {
336 this.method(position);
337 }
338 });
339
340 Effect.Event = Class.create(Effect.Base, {
361 initialize: function() {
341 initialize: function() {
362 var options = Object.extend({
342 this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
363 duration: 0
364 }, arguments[0] || {});
365 this.start(options);
366 },
343 },
367 update: Prototype.emptyFunction
344 update: Prototype.emptyFunction
368 });
345 });
369
346
370 Effect.Opacity = Class.create();
347 Effect.Opacity = Class.create(Effect.Base, {
371 Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
372 initialize: function(element) {
348 initialize: function(element) {
373 this.element = $(element);
349 this.element = $(element);
374 if(!this.element) throw(Effect._elementDoesNotExistError);
350 if (!this.element) throw(Effect._elementDoesNotExistError);
375 // make this work on IE on elements without 'layout'
351 // make this work on IE on elements without 'layout'
376 if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
352 if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
377 this.element.setStyle({zoom: 1});
353 this.element.setStyle({zoom: 1});
378 var options = Object.extend({
354 var options = Object.extend({
379 from: this.element.getOpacity() || 0.0,
355 from: this.element.getOpacity() || 0.0,
@@ -386,8 +362,7 Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
386 }
362 }
387 });
363 });
388
364
389 Effect.Move = Class.create();
365 Effect.Move = Class.create(Effect.Base, {
390 Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
391 initialize: function(element) {
366 initialize: function(element) {
392 this.element = $(element);
367 this.element = $(element);
393 if(!this.element) throw(Effect._elementDoesNotExistError);
368 if (!this.element) throw(Effect._elementDoesNotExistError);
@@ -399,23 +374,18 Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
399 this.start(options);
374 this.start(options);
400 },
375 },
401 setup: function() {
376 setup: function() {
402 // Bug in Opera: Opera returns the "real" position of a static element or
403 // relative element that does not have top/left explicitly set.
404 // ==> Always set top and left for position relative elements in your stylesheets
405 // (to 0 if you do not need them)
406 this.element.makePositioned();
377 this.element.makePositioned();
407 this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
378 this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
408 this.originalTop = parseFloat(this.element.getStyle('top') || '0');
379 this.originalTop = parseFloat(this.element.getStyle('top') || '0');
409 if(this.options.mode == 'absolute') {
380 if (this.options.mode == 'absolute') {
410 // absolute movement, so we need to calc deltaX and deltaY
411 this.options.x = this.options.x - this.originalLeft;
381 this.options.x = this.options.x - this.originalLeft;
412 this.options.y = this.options.y - this.originalTop;
382 this.options.y = this.options.y - this.originalTop;
413 }
383 }
414 },
384 },
415 update: function(position) {
385 update: function(position) {
416 this.element.setStyle({
386 this.element.setStyle({
417 left: Math.round(this.options.x * position + this.originalLeft) + 'px',
387 left: (this.options.x * position + this.originalLeft).round() + 'px',
418 top: Math.round(this.options.y * position + this.originalTop) + 'px'
388 top: (this.options.y * position + this.originalTop).round() + 'px'
419 });
389 });
420 }
390 }
421 });
391 });
@@ -426,8 +396,7 Effect.MoveBy = function(element, toTop, toLeft) {
426 Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
396 Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
427 };
397 };
428
398
429 Effect.Scale = Class.create();
399 Effect.Scale = Class.create(Effect.Base, {
430 Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
431 initialize: function(element, percent) {
400 initialize: function(element, percent) {
432 this.element = $(element);
401 this.element = $(element);
433 if(!this.element) throw(Effect._elementDoesNotExistError);
402 if (!this.element) throw(Effect._elementDoesNotExistError);
@@ -484,8 +453,8 Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
484 },
453 },
485 setDimensions: function(height, width) {
454 setDimensions: function(height, width) {
486 var d = {};
455 var d = { };
487 if(this.options.scaleX) d.width = Math.round(width) + 'px';
456 if (this.options.scaleX) d.width = width.round() + 'px';
488 if(this.options.scaleY) d.height = Math.round(height) + 'px';
457 if (this.options.scaleY) d.height = height.round() + 'px';
489 if(this.options.scaleFromCenter) {
458 if (this.options.scaleFromCenter) {
490 var topd = (height - this.dims[0])/2;
459 var topd = (height - this.dims[0])/2;
491 var leftd = (width - this.dims[1])/2;
460 var leftd = (width - this.dims[1])/2;
@@ -501,8 +470,7 Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
501 }
470 }
502 });
471 });
503
472
504 Effect.Highlight = Class.create();
473 Effect.Highlight = Class.create(Effect.Base, {
505 Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
506 initialize: function(element) {
474 initialize: function(element) {
507 this.element = $(element);
475 this.element = $(element);
508 if(!this.element) throw(Effect._elementDoesNotExistError);
476 if (!this.element) throw(Effect._elementDoesNotExistError);
@@ -513,9 +481,11 Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype),
513 // Prevent executing on elements not in the layout flow
481 // Prevent executing on elements not in the layout flow
514 if(this.element.getStyle('display')=='none') { this.cancel(); return; }
482 if (this.element.getStyle('display')=='none') { this.cancel(); return; }
515 // Disable background image during the effect
483 // Disable background image during the effect
516 this.oldStyle = {
484 this.oldStyle = { };
517 backgroundImage: this.element.getStyle('background-image') };
485 if (!this.options.keepBackgroundImage) {
486 this.oldStyle.backgroundImage = this.element.getStyle('background-image');
518 this.element.setStyle({backgroundImage: 'none'});
487 this.element.setStyle({backgroundImage: 'none'});
488 }
519 if(!this.options.endcolor)
489 if (!this.options.endcolor)
520 this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
490 this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
521 if(!this.options.restorecolor)
491 if (!this.options.restorecolor)
@@ -526,7 +496,7 Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype),
526 },
496 },
527 update: function(position) {
497 update: function(position) {
528 this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
498 this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
529 return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
499 return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
530 },
500 },
531 finish: function() {
501 finish: function() {
532 this.element.setStyle(Object.extend(this.oldStyle, {
502 this.element.setStyle(Object.extend(this.oldStyle, {
@@ -535,30 +505,21 Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype),
535 }
505 }
536 });
506 });
537
507
538 Effect.ScrollTo = Class.create();
508 Effect.ScrollTo = function(element) {
539 Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
509 var options = arguments[1] || { },
540 initialize: function(element) {
510 scrollOffsets = document.viewport.getScrollOffsets(),
541 this.element = $(element);
511 elementOffsets = $(element).cumulativeOffset(),
542 this.start(arguments[1] || {});
512 max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();
543 },
513
544 setup: function() {
514 if (options.offset) elementOffsets[1] += options.offset;
545 Position.prepare();
515
546 var offsets = Position.cumulativeOffset(this.element);
516 return new Effect.Tween(null,
547 if(this.options.offset) offsets[1] += this.options.offset;
517 scrollOffsets.top,
548 var max = window.innerHeight ?
518 elementOffsets[1] > max ? max : elementOffsets[1],
549 window.height - window.innerHeight :
519 options,
550 document.body.scrollHeight -
520 function(p){ scrollTo(scrollOffsets.left, p.round()) }
551 (document.documentElement.clientHeight ?
521 );
552 document.documentElement.clientHeight : document.body.clientHeight);
522 };
553 this.scrollStart = Position.deltaY;
554 this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
555 },
556 update: function(position) {
557 Position.prepare();
558 window.scrollTo(Position.deltaX,
559 this.scrollStart + (position*this.delta));
560 }
561 });
562
523
563 /* ------------- combination effects ------------- */
524 /* ------------- combination effects ------------- */
564
525
@@ -571,9 +532,10 Effect.Fade = function(element) {
571 afterFinishInternal: function(effect) {
532 afterFinishInternal: function(effect) {
572 if(effect.options.to!=0) return;
533 if (effect.options.to!=0) return;
573 effect.element.hide().setStyle({opacity: oldOpacity});
534 effect.element.hide().setStyle({opacity: oldOpacity});
574 }}, arguments[1] || {});
575 return new Effect.Opacity(element,options);
576 }
535 }
536 }, arguments[1] || { });
537 return new Effect.Opacity(element,options);
538 };
577
539
578 Effect.Appear = function(element) {
540 Effect.Appear = function(element) {
579 element = $(element);
541 element = $(element);
@@ -588,7 +550,7 Effect.Appear = function(element) {
588 effect.element.setOpacity(effect.options.from).show();
550 effect.element.setOpacity(effect.options.from).show();
589 }}, arguments[1] || {});
551 }}, arguments[1] || { });
590 return new Effect.Opacity(element,options);
552 return new Effect.Opacity(element,options);
591 }
553 };
592
554
593 Effect.Puff = function(element) {
555 Effect.Puff = function(element) {
594 element = $(element);
556 element = $(element);
@@ -612,7 +574,7 Effect.Puff = function(element) {
612 effect.effects[0].element.hide().setStyle(oldStyle); }
574 effect.effects[0].element.hide().setStyle(oldStyle); }
613 }, arguments[1] || {})
575 }, arguments[1] || { })
614 );
576 );
615 }
577 };
616
578
617 Effect.BlindUp = function(element) {
579 Effect.BlindUp = function(element) {
618 element = $(element);
580 element = $(element);
@@ -626,7 +588,7 Effect.BlindUp = function(element) {
626 }
588 }
627 }, arguments[1] || {})
589 }, arguments[1] || { })
628 );
590 );
629 }
591 };
630
592
631 Effect.BlindDown = function(element) {
593 Effect.BlindDown = function(element) {
632 element = $(element);
594 element = $(element);
@@ -644,7 +606,7 Effect.BlindDown = function(element) {
644 effect.element.undoClipping();
606 effect.element.undoClipping();
645 }
607 }
646 }, arguments[1] || {}));
608 }, arguments[1] || { }));
647 }
609 };
648
610
649 Effect.SwitchOff = function(element) {
611 Effect.SwitchOff = function(element) {
650 element = $(element);
612 element = $(element);
@@ -666,7 +628,7 Effect.SwitchOff = function(element) {
666 })
628 })
667 }
629 }
668 }, arguments[1] || {}));
630 }, arguments[1] || { }));
669 }
631 };
670
632
671 Effect.DropOut = function(element) {
633 Effect.DropOut = function(element) {
672 element = $(element);
634 element = $(element);
@@ -686,28 +648,34 Effect.DropOut = function(element) {
686 effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
648 effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
687 }
649 }
688 }, arguments[1] || {}));
650 }, arguments[1] || { }));
689 }
651 };
690
652
691 Effect.Shake = function(element) {
653 Effect.Shake = function(element) {
692 element = $(element);
654 element = $(element);
655 var options = Object.extend({
656 distance: 20,
657 duration: 0.5
658 }, arguments[1] || {});
659 var distance = parseFloat(options.distance);
660 var split = parseFloat(options.duration) / 10.0;
693 var oldStyle = {
661 var oldStyle = {
694 top: element.getStyle('top'),
662 top: element.getStyle('top'),
695 left: element.getStyle('left') };
663 left: element.getStyle('left') };
696 return new Effect.Move(element,
664 return new Effect.Move(element,
697 { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
665 { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) {
698 new Effect.Move(effect.element,
666 new Effect.Move(effect.element,
699 { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
667 { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
700 new Effect.Move(effect.element,
668 new Effect.Move(effect.element,
701 { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
669 { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
702 new Effect.Move(effect.element,
670 new Effect.Move(effect.element,
703 { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
671 { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
704 new Effect.Move(effect.element,
672 new Effect.Move(effect.element,
705 { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
673 { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
706 new Effect.Move(effect.element,
674 new Effect.Move(effect.element,
707 { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
675 { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
708 effect.element.undoPositioned().setStyle(oldStyle);
676 effect.element.undoPositioned().setStyle(oldStyle);
709 }}) }}) }}) }}) }}) }});
677 }}) }}) }}) }}) }}) }});
710 }
678 };
711
679
712 Effect.SlideDown = function(element) {
680 Effect.SlideDown = function(element) {
713 element = $(element).cleanWhitespace();
681 element = $(element).cleanWhitespace();
@@ -735,18 +703,20 Effect.SlideDown = function(element) {
735 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
703 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
736 }, arguments[1] || {})
704 }, arguments[1] || { })
737 );
705 );
738 }
706 };
739
707
740 Effect.SlideUp = function(element) {
708 Effect.SlideUp = function(element) {
741 element = $(element).cleanWhitespace();
709 element = $(element).cleanWhitespace();
742 var oldInnerBottom = element.down().getStyle('bottom');
710 var oldInnerBottom = element.down().getStyle('bottom');
711 var elementDimensions = element.getDimensions();
743 return new Effect.Scale(element, window.opera ? 0 : 1,
712 return new Effect.Scale(element, window.opera ? 0 : 1,
744 Object.extend({ scaleContent: false,
713 Object.extend({ scaleContent: false,
745 scaleX: false,
714 scaleX: false,
746 scaleMode: 'box',
715 scaleMode: 'box',
747 scaleFrom: 100,
716 scaleFrom: 100,
717 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
748 restoreAfterFinish: true,
718 restoreAfterFinish: true,
749 beforeStartInternal: function(effect) {
719 afterSetup: function(effect) {
750 effect.element.makePositioned();
720 effect.element.makePositioned();
751 effect.element.down().makePositioned();
721 effect.element.down().makePositioned();
752 if(window.opera) effect.element.setStyle({top: ''});
722 if (window.opera) effect.element.setStyle({top: ''});
@@ -757,12 +727,12 Effect.SlideUp = function(element) {
757 (effect.dims[0] - effect.element.clientHeight) + 'px' });
727 (effect.dims[0] - effect.element.clientHeight) + 'px' });
758 },
728 },
759 afterFinishInternal: function(effect) {
729 afterFinishInternal: function(effect) {
760 effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
730 effect.element.hide().undoClipping().undoPositioned();
761 effect.element.down().undoPositioned();
731 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
762 }
732 }
763 }, arguments[1] || {})
733 }, arguments[1] || { })
764 );
734 );
765 }
735 };
766
736
767 // Bug in opera makes the TD containing this element expand for a instance after finish
737 // Bug in opera makes the TD containing this element expand for a instance after finish
768 Effect.Squish = function(element) {
738 Effect.Squish = function(element) {
@@ -775,7 +745,7 Effect.Squish = function(element) {
775 effect.element.hide().undoClipping();
745 effect.element.hide().undoClipping();
776 }
746 }
777 });
747 });
778 }
748 };
779
749
780 Effect.Grow = function(element) {
750 Effect.Grow = function(element) {
781 element = $(element);
751 element = $(element);
@@ -849,7 +819,7 Effect.Grow = function(element) {
849 )
819 )
850 }
820 }
851 });
821 });
852 }
822 };
853
823
854 Effect.Shrink = function(element) {
824 Effect.Shrink = function(element) {
855 element = $(element);
825 element = $(element);
@@ -903,7 +873,7 Effect.Shrink = function(element) {
903 effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
873 effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
904 }, options)
874 }, options)
905 );
875 );
906 }
876 };
907
877
908 Effect.Pulsate = function(element) {
878 Effect.Pulsate = function(element) {
909 element = $(element);
879 element = $(element);
@@ -916,7 +886,7 Effect.Pulsate = function(element) {
916 Object.extend(Object.extend({ duration: 2.0, from: 0,
886 Object.extend(Object.extend({ duration: 2.0, from: 0,
917 afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
887 afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
918 }, options), {transition: reverser}));
888 }, options), {transition: reverser}));
919 }
889 };
920
890
921 Effect.Fold = function(element) {
891 Effect.Fold = function(element) {
922 element = $(element);
892 element = $(element);
@@ -939,16 +909,37 Effect.Fold = function(element) {
939 }}, arguments[1] || {}));
909 }}, arguments[1] || { }));
940 };
910 };
941
911
942 Effect.Morph = Class.create();
912 Effect.Morph = Class.create(Effect.Base, {
943 Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
944 initialize: function(element) {
913 initialize: function(element) {
945 this.element = $(element);
914 this.element = $(element);
946 if(!this.element) throw(Effect._elementDoesNotExistError);
915 if (!this.element) throw(Effect._elementDoesNotExistError);
947 var options = Object.extend({
916 var options = Object.extend({
948 style: ''
917 style: { }
949 }, arguments[1] || {});
918 }, arguments[1] || { });
919
920 if (!Object.isString(options.style)) this.style = $H(options.style);
921 else {
922 if (options.style.include(':'))
923 this.style = options.style.parseStyle();
924 else {
925 this.element.addClassName(options.style);
926 this.style = $H(this.element.getStyles());
927 this.element.removeClassName(options.style);
928 var css = this.element.getStyles();
929 this.style = this.style.reject(function(style) {
930 return style.value == css[style.key];
931 });
932 options.afterFinishInternal = function(effect) {
933 effect.element.addClassName(effect.options.style);
934 effect.transforms.each(function(transform) {
935 effect.element.style[transform.style] = '';
936 });
937 }
938 }
939 }
950 this.start(options);
940 this.start(options);
951 },
941 },
942
952 setup: function(){
943 setup: function(){
953 function parseColor(color){
944 function parseColor(color){
954 if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
945 if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
@@ -957,16 +948,29 Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
957 return parseInt( color.slice(i*2+1,i*2+3), 16 )
948 return parseInt( color.slice(i*2+1,i*2+3), 16 )
958 });
949 });
959 }
950 }
960 this.transforms = this.options.style.parseStyle().map(function(property){
951 this.transforms = this.style.map(function(pair){
961 var originalValue = this.element.getStyle(property[0]);
952 var property = pair[0], value = pair[1], unit = null;
962 return $H({
953
963 style: property[0],
954 if (value.parseColor('#zzzzzz') != '#zzzzzz') {
964 originalValue: property[1].unit=='color' ?
955 value = value.parseColor();
965 parseColor(originalValue) : parseFloat(originalValue || 0),
956 unit = 'color';
966 targetValue: property[1].unit=='color' ?
957 } else if (property == 'opacity') {
967 parseColor(property[1].value) : property[1].value,
958 value = parseFloat(value);
968 unit: property[1].unit
959 if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
969 });
960 this.element.setStyle({zoom: 1});
961 } else if (Element.CSS_LENGTH.test(value)) {
962 var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
963 value = parseFloat(components[1]);
964 unit = (components.length == 3) ? components[2] : null;
965 }
966
967 var originalValue = this.element.getStyle(property);
968 return {
969 style: property.camelize(),
970 originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
971 targetValue: unit=='color' ? parseColor(value) : value,
972 unit: unit
973 };
970 }.bind(this)).reject(function(transform){
974 }.bind(this)).reject(function(transform){
971 return (
975 return (
972 (transform.originalValue == transform.targetValue) ||
976 (transform.originalValue == transform.targetValue) ||
@@ -978,22 +982,24 Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
978 });
982 });
979 },
983 },
980 update: function(position) {
984 update: function(position) {
981 var style = $H(), value = null;
985 var style = { }, transform, i = this.transforms.length;
982 this.transforms.each(function(transform){
986 while(i--)
983 value = transform.unit=='color' ?
987 style[(transform = this.transforms[i]).style] =
984 $R(0,2).inject('#',function(m,v,i){
988 transform.unit=='color' ? '#'+
985 return m+(Math.round(transform.originalValue[i]+
989 (Math.round(transform.originalValue[0]+
986 (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) :
990 (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
987 transform.originalValue + Math.round(
991 (Math.round(transform.originalValue[1]+
988 ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
992 (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
989 style[transform.style] = value;
993 (Math.round(transform.originalValue[2]+
990 });
994 (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
991 this.element.setStyle(style);
995 (transform.originalValue +
996 (transform.targetValue - transform.originalValue) * position).toFixed(3) +
997 (transform.unit === null ? '' : transform.unit);
998 this.element.setStyle(style, true);
992 }
999 }
993 });
1000 });
994
1001
995 Effect.Transform = Class.create();
1002 Effect.Transform = Class.create({
996 Object.extend(Effect.Transform.prototype, {
997 initialize: function(tracks){
1003 initialize: function(tracks){
998 this.tracks = [];
1004 this.tracks = [];
999 this.options = arguments[1] || {};
1005 this.options = arguments[1] || { };
@@ -1001,9 +1007,10 Object.extend(Effect.Transform.prototype, {
1001 },
1007 },
1002 addTracks: function(tracks){
1008 addTracks: function(tracks){
1003 tracks.each(function(track){
1009 tracks.each(function(track){
1004 var data = $H(track).values().first();
1010 track = $H(track);
1011 var data = track.values().first();
1005 this.tracks.push($H({
1012 this.tracks.push($H({
1006 ids: $H(track).keys().first(),
1013 ids: track.keys().first(),
1007 effect: Effect.Morph,
1014 effect: Effect.Morph,
1008 options: { style: data }
1015 options: { style: data }
1009 }));
1016 }));
@@ -1013,76 +1020,101 Object.extend(Effect.Transform.prototype, {
1013 play: function(){
1020 play: function(){
1014 return new Effect.Parallel(
1021 return new Effect.Parallel(
1015 this.tracks.map(function(track){
1022 this.tracks.map(function(track){
1016 var elements = [$(track.ids) || $$(track.ids)].flatten();
1023 var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
1017 return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
1024 var elements = [$(ids) || $$(ids)].flatten();
1025 return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
1018 }).flatten(),
1026 }).flatten(),
1019 this.options
1027 this.options
1020 );
1028 );
1021 }
1029 }
1022 });
1030 });
1023
1031
1024 Element.CSS_PROPERTIES = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage',
1032 Element.CSS_PROPERTIES = $w(
1025 'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle',
1033 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
1026 'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth',
1034 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
1027 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor',
1035 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
1028 'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content',
1036 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
1029 'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction',
1037 'fontSize fontWeight height left letterSpacing lineHeight ' +
1030 'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch',
1038 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
1031 'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight',
1039 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
1032 'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight',
1040 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
1033 'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity',
1041 'right textIndent top width wordSpacing zIndex');
1034 'orphans', 'outlineColor', 'outlineOffset', 'outlineStyle', 'outlineWidth', 'overflowX', 'overflowY',
1035 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'page', 'pageBreakAfter', 'pageBreakBefore',
1036 'pageBreakInside', 'pauseAfter', 'pauseBefore', 'pitch', 'pitchRange', 'position', 'quotes',
1037 'richness', 'right', 'size', 'speakHeader', 'speakNumeral', 'speakPunctuation', 'speechRate', 'stress',
1038 'tableLayout', 'textAlign', 'textDecoration', 'textIndent', 'textShadow', 'textTransform', 'top',
1039 'unicodeBidi', 'verticalAlign', 'visibility', 'voiceFamily', 'volume', 'whiteSpace', 'widows',
1040 'width', 'wordSpacing', 'zIndex'];
1041
1042
1042 Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1043 Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1043
1044
1045 String.__parseStyleElement = document.createElement('div');
1044 String.prototype.parseStyle = function(){
1046 String.prototype.parseStyle = function(){
1045 var element = Element.extend(document.createElement('div'));
1047 var style, styleRules = $H();
1046 element.innerHTML = '<div style="' + this + '"></div>';
1048 if (Prototype.Browser.WebKit)
1047 var style = element.down().style, styleRules = $H();
1049 style = new Element('div',{style:this}).style;
1050 else {
1051 String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
1052 style = String.__parseStyleElement.childNodes[0].style;
1053 }
1048
1054
1049 Element.CSS_PROPERTIES.each(function(property){
1055 Element.CSS_PROPERTIES.each(function(property){
1050 if(style[property]) styleRules[property] = style[property];
1056 if (style[property]) styleRules.set(property, style[property]);
1051 });
1057 });
1052
1058
1053 var result = $H();
1059 if (Prototype.Browser.IE && this.include('opacity'))
1054
1060 styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
1055 styleRules.each(function(pair){
1056 var property = pair[0], value = pair[1], unit = null;
1057
1058 if(value.parseColor('#zzzzzz') != '#zzzzzz') {
1059 value = value.parseColor();
1060 unit = 'color';
1061 } else if(Element.CSS_LENGTH.test(value))
1062 var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
1063 value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;
1064
1061
1065 result[property.underscore().dasherize()] = $H({ value:value, unit:unit });
1062 return styleRules;
1066 }.bind(this));
1063 };
1067
1064
1068 return result;
1065 if (document.defaultView && document.defaultView.getComputedStyle) {
1066 Element.getStyles = function(element) {
1067 var css = document.defaultView.getComputedStyle($(element), null);
1068 return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
1069 styles[property] = css[property];
1070 return styles;
1071 });
1072 };
1073 } else {
1074 Element.getStyles = function(element) {
1075 element = $(element);
1076 var css = element.currentStyle, styles;
1077 styles = Element.CSS_PROPERTIES.inject({ }, function(hash, property) {
1078 hash.set(property, css[property]);
1079 return hash;
1080 });
1081 if (!styles.opacity) styles.set('opacity', element.getOpacity());
1082 return styles;
1083 };
1069 };
1084 };
1070
1085
1071 Element.morph = function(element, style) {
1086 Effect.Methods = {
1087 morph: function(element, style) {
1088 element = $(element);
1072 new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
1089 new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
1073 return element;
1090 return element;
1091 },
1092 visualEffect: function(element, effect, options) {
1093 element = $(element)
1094 var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
1095 new Effect[klass](element, options);
1096 return element;
1097 },
1098 highlight: function(element, options) {
1099 element = $(element);
1100 new Effect.Highlight(element, options);
1101 return element;
1102 }
1074 };
1103 };
1075
1104
1076 ['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
1105 $w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
1077 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each(
1106 'pulsate shake puff squish switchOff dropOut').each(
1078 function(f) { Element.Methods[f] = Element[f]; }
1107 function(effect) {
1108 Effect.Methods[effect] = function(element, options){
1109 element = $(element);
1110 Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
1111 return element;
1112 }
1113 }
1079 );
1114 );
1080
1115
1081 Element.Methods.visualEffect = function(element, effect, options) {
1116 $w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
1082 s = effect.gsub(/_/, '-').camelize();
1117 function(f) { Effect.Methods[f] = Element[f]; }
1083 effect_class = s.charAt(0).toUpperCase() + s.substring(1);
1118 );
1084 new Effect[effect_class](element, options);
1085 return $(element);
1086 };
1087
1119
1088 Element.addMethods(); No newline at end of file
1120 Element.addMethods(Effect.Methods);
This diff has been collapsed as it changes many lines, (3744 lines changed) Show them Hide them
@@ -1,43 +1,114
1 /* Prototype JavaScript framework, version 1.5.0
1 /* Prototype JavaScript framework, version 1.6.0.1
2 * (c) 2005-2007 Sam Stephenson
2 * (c) 2005-2007 Sam Stephenson
3 *
3 *
4 * Prototype is freely distributable under the terms of an MIT-style license.
4 * Prototype is freely distributable under the terms of an MIT-style license.
5 * For details, see the Prototype web site: http://prototype.conio.net/
5 * For details, see the Prototype web site: http://www.prototypejs.org/
6 *
6 *
7 /*--------------------------------------------------------------------------*/
7 *--------------------------------------------------------------------------*/
8
8
9 var Prototype = {
9 var Prototype = {
10 Version: '1.5.0',
10 Version: '1.6.0.1',
11
12 Browser: {
13 IE: !!(window.attachEvent && !window.opera),
14 Opera: !!window.opera,
15 WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
16 Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
17 MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
18 },
19
11 BrowserFeatures: {
20 BrowserFeatures: {
12 XPath: !!document.evaluate
21 XPath: !!document.evaluate,
22 ElementExtensions: !!window.HTMLElement,
23 SpecificElementExtensions:
24 document.createElement('div').__proto__ &&
25 document.createElement('div').__proto__ !==
26 document.createElement('form').__proto__
13 },
27 },
14
28
15 ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
29 ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
30 JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
31
16 emptyFunction: function() {},
32 emptyFunction: function() { },
17 K: function(x) { return x }
33 K: function(x) { return x }
18 }
34 };
35
36 if (Prototype.Browser.MobileSafari)
37 Prototype.BrowserFeatures.SpecificElementExtensions = false;
19
38
39
40 /* Based on Alex Arnell's inheritance implementation. */
20 var Class = {
41 var Class = {
21 create: function() {
42 create: function() {
22 return function() {
43 var parent = null, properties = $A(arguments);
44 if (Object.isFunction(properties[0]))
45 parent = properties.shift();
46
47 function klass() {
23 this.initialize.apply(this, arguments);
48 this.initialize.apply(this, arguments);
24 }
49 }
50
51 Object.extend(klass, Class.Methods);
52 klass.superclass = parent;
53 klass.subclasses = [];
54
55 if (parent) {
56 var subclass = function() { };
57 subclass.prototype = parent.prototype;
58 klass.prototype = new subclass;
59 parent.subclasses.push(klass);
60 }
61
62 for (var i = 0; i < properties.length; i++)
63 klass.addMethods(properties[i]);
64
65 if (!klass.prototype.initialize)
66 klass.prototype.initialize = Prototype.emptyFunction;
67
68 klass.prototype.constructor = klass;
69
70 return klass;
71 }
72 };
73
74 Class.Methods = {
75 addMethods: function(source) {
76 var ancestor = this.superclass && this.superclass.prototype;
77 var properties = Object.keys(source);
78
79 if (!Object.keys({ toString: true }).length)
80 properties.push("toString", "valueOf");
81
82 for (var i = 0, length = properties.length; i < length; i++) {
83 var property = properties[i], value = source[property];
84 if (ancestor && Object.isFunction(value) &&
85 value.argumentNames().first() == "$super") {
86 var method = value, value = Object.extend((function(m) {
87 return function() { return ancestor[m].apply(this, arguments) };
88 })(property).wrap(method), {
89 valueOf: function() { return method },
90 toString: function() { return method.toString() }
91 });
92 }
93 this.prototype[property] = value;
25 }
94 }
95
96 return this;
26 }
97 }
98 };
27
99
28 var Abstract = new Object();
100 var Abstract = { };
29
101
30 Object.extend = function(destination, source) {
102 Object.extend = function(destination, source) {
31 for (var property in source) {
103 for (var property in source)
32 destination[property] = source[property];
104 destination[property] = source[property];
33 }
34 return destination;
105 return destination;
35 }
106 };
36
107
37 Object.extend(Object, {
108 Object.extend(Object, {
38 inspect: function(object) {
109 inspect: function(object) {
39 try {
110 try {
40 if (object === undefined) return 'undefined';
111 if (Object.isUndefined(object)) return 'undefined';
41 if (object === null) return 'null';
112 if (object === null) return 'null';
42 return object.inspect ? object.inspect() : object.toString();
113 return object.inspect ? object.inspect() : object.toString();
43 } catch (e) {
114 } catch (e) {
@@ -46,6 +117,37 Object.extend(Object, {
46 }
117 }
47 },
118 },
48
119
120 toJSON: function(object) {
121 var type = typeof object;
122 switch (type) {
123 case 'undefined':
124 case 'function':
125 case 'unknown': return;
126 case 'boolean': return object.toString();
127 }
128
129 if (object === null) return 'null';
130 if (object.toJSON) return object.toJSON();
131 if (Object.isElement(object)) return;
132
133 var results = [];
134 for (var property in object) {
135 var value = Object.toJSON(object[property]);
136 if (!Object.isUndefined(value))
137 results.push(property.toJSON() + ': ' + value);
138 }
139
140 return '{' + results.join(', ') + '}';
141 },
142
143 toQueryString: function(object) {
144 return $H(object).toQueryString();
145 },
146
147 toHTML: function(object) {
148 return object && object.toHTML ? object.toHTML() : String.interpret(object);
149 },
150
49 keys: function(object) {
151 keys: function(object) {
50 var keys = [];
152 var keys = [];
51 for (var property in object)
153 for (var property in object)
@@ -62,40 +164,100 Object.extend(Object, {
62
164
63 clone: function(object) {
165 clone: function(object) {
64 return Object.extend({}, object);
166 return Object.extend({ }, object);
167 },
168
169 isElement: function(object) {
170 return object && object.nodeType == 1;
171 },
172
173 isArray: function(object) {
174 return object && object.constructor === Array;
175 },
176
177 isHash: function(object) {
178 return object instanceof Hash;
179 },
180
181 isFunction: function(object) {
182 return typeof object == "function";
183 },
184
185 isString: function(object) {
186 return typeof object == "string";
187 },
188
189 isNumber: function(object) {
190 return typeof object == "number";
191 },
192
193 isUndefined: function(object) {
194 return typeof object == "undefined";
65 }
195 }
66 });
196 });
67
197
68 Function.prototype.bind = function() {
198 Object.extend(Function.prototype, {
199 argumentNames: function() {
200 var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
201 return names.length == 1 && !names[0] ? [] : names;
202 },
203
204 bind: function() {
205 if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
69 var __method = this, args = $A(arguments), object = args.shift();
206 var __method = this, args = $A(arguments), object = args.shift();
70 return function() {
207 return function() {
71 return __method.apply(object, args.concat($A(arguments)));
208 return __method.apply(object, args.concat($A(arguments)));
72 }
209 }
73 }
210 },
74
211
75 Function.prototype.bindAsEventListener = function(object) {
212 bindAsEventListener: function() {
76 var __method = this, args = $A(arguments), object = args.shift();
213 var __method = this, args = $A(arguments), object = args.shift();
77 return function(event) {
214 return function(event) {
78 return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
215 return __method.apply(object, [event || window.event].concat(args));
79 }
216 }
217 },
218
219 curry: function() {
220 if (!arguments.length) return this;
221 var __method = this, args = $A(arguments);
222 return function() {
223 return __method.apply(this, args.concat($A(arguments)));
80 }
224 }
225 },
81
226
82 Object.extend(Number.prototype, {
227 delay: function() {
83 toColorPart: function() {
228 var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
84 var digits = this.toString(16);
229 return window.setTimeout(function() {
85 if (this < 16) return '0' + digits;
230 return __method.apply(__method, args);
86 return digits;
231 }, timeout);
87 },
232 },
88
233
89 succ: function() {
234 wrap: function(wrapper) {
90 return this + 1;
235 var __method = this;
236 return function() {
237 return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
238 }
91 },
239 },
92
240
93 times: function(iterator) {
241 methodize: function() {
94 $R(0, this, true).each(iterator);
242 if (this._methodized) return this._methodized;
95 return this;
243 var __method = this;
244 return this._methodized = function() {
245 return __method.apply(null, [this].concat($A(arguments)));
246 };
96 }
247 }
97 });
248 });
98
249
250 Function.prototype.defer = Function.prototype.delay.curry(0.01);
251
252 Date.prototype.toJSON = function() {
253 return '"' + this.getUTCFullYear() + '-' +
254 (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
255 this.getUTCDate().toPaddedString(2) + 'T' +
256 this.getUTCHours().toPaddedString(2) + ':' +
257 this.getUTCMinutes().toPaddedString(2) + ':' +
258 this.getUTCSeconds().toPaddedString(2) + 'Z"';
259 };
260
99 var Try = {
261 var Try = {
100 these: function() {
262 these: function() {
101 var returnValue;
263 var returnValue;
@@ -110,12 +272,17 var Try = {
110
272
111 return returnValue;
273 return returnValue;
112 }
274 }
113 }
275 };
276
277 RegExp.prototype.match = RegExp.prototype.test;
278
279 RegExp.escape = function(str) {
280 return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
281 };
114
282
115 /*--------------------------------------------------------------------------*/
283 /*--------------------------------------------------------------------------*/
116
284
117 var PeriodicalExecuter = Class.create();
285 var PeriodicalExecuter = Class.create({
118 PeriodicalExecuter.prototype = {
119 initialize: function(callback, frequency) {
286 initialize: function(callback, frequency) {
120 this.callback = callback;
287 this.callback = callback;
121 this.frequency = frequency;
288 this.frequency = frequency;
@@ -128,6 +295,10 PeriodicalExecuter.prototype = {
128 this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
295 this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
129 },
296 },
130
297
298 execute: function() {
299 this.callback(this);
300 },
301
131 stop: function() {
302 stop: function() {
132 if (!this.timer) return;
303 if (!this.timer) return;
133 clearInterval(this.timer);
304 clearInterval(this.timer);
@@ -138,16 +309,26 PeriodicalExecuter.prototype = {
138 if (!this.currentlyExecuting) {
309 if (!this.currentlyExecuting) {
139 try {
310 try {
140 this.currentlyExecuting = true;
311 this.currentlyExecuting = true;
141 this.callback(this);
312 this.execute();
142 } finally {
313 } finally {
143 this.currentlyExecuting = false;
314 this.currentlyExecuting = false;
144 }
315 }
145 }
316 }
146 }
317 }
147 }
318 });
148 String.interpret = function(value){
319 Object.extend(String, {
320 interpret: function(value) {
149 return value == null ? '' : String(value);
321 return value == null ? '' : String(value);
322 },
323 specialChar: {
324 '\b': '\\b',
325 '\t': '\\t',
326 '\n': '\\n',
327 '\f': '\\f',
328 '\r': '\\r',
329 '\\': '\\\\'
150 }
330 }
331 });
151
332
152 Object.extend(String.prototype, {
333 Object.extend(String.prototype, {
153 gsub: function(pattern, replacement) {
334 gsub: function(pattern, replacement) {
@@ -168,7 +349,7 Object.extend(String.prototype, {
168
349
169 sub: function(pattern, replacement, count) {
350 sub: function(pattern, replacement, count) {
170 replacement = this.gsub.prepareReplacement(replacement);
351 replacement = this.gsub.prepareReplacement(replacement);
171 count = count === undefined ? 1 : count;
352 count = Object.isUndefined(count) ? 1 : count;
172
353
173 return this.gsub(pattern, function(match) {
354 return this.gsub(pattern, function(match) {
174 if (--count < 0) return match[0];
355 if (--count < 0) return match[0];
@@ -178,14 +359,14 Object.extend(String.prototype, {
178
359
179 scan: function(pattern, iterator) {
360 scan: function(pattern, iterator) {
180 this.gsub(pattern, iterator);
361 this.gsub(pattern, iterator);
181 return this;
362 return String(this);
182 },
363 },
183
364
184 truncate: function(length, truncation) {
365 truncate: function(length, truncation) {
185 length = length || 30;
366 length = length || 30;
186 truncation = truncation === undefined ? '...' : truncation;
367 truncation = Object.isUndefined(truncation) ? '...' : truncation;
187 return this.length > length ?
368 return this.length > length ?
188 this.slice(0, length - truncation.length) + truncation : this;
369 this.slice(0, length - truncation.length) + truncation : String(this);
189 },
370 },
190
371
191 strip: function() {
372 strip: function() {
@@ -213,14 +394,13 Object.extend(String.prototype, {
213 },
394 },
214
395
215 escapeHTML: function() {
396 escapeHTML: function() {
216 var div = document.createElement('div');
397 var self = arguments.callee;
217 var text = document.createTextNode(this);
398 self.text.data = this;
218 div.appendChild(text);
399 return self.div.innerHTML;
219 return div.innerHTML;
220 },
400 },
221
401
222 unescapeHTML: function() {
402 unescapeHTML: function() {
223 var div = document.createElement('div');
403 var div = new Element('div');
224 div.innerHTML = this.stripTags();
404 div.innerHTML = this.stripTags();
225 return div.childNodes[0] ? (div.childNodes.length > 1 ?
405 return div.childNodes[0] ? (div.childNodes.length > 1 ?
226 $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
406 $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
@@ -233,15 +413,15 Object.extend(String.prototype, {
233
413
234 return match[1].split(separator || '&').inject({}, function(hash, pair) {
414 return match[1].split(separator || '&').inject({ }, function(hash, pair) {
235 if ((pair = pair.split('='))[0]) {
415 if ((pair = pair.split('='))[0]) {
236 var name = decodeURIComponent(pair[0]);
416 var key = decodeURIComponent(pair.shift());
237 var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
417 var value = pair.length > 1 ? pair.join('=') : pair[0];
418 if (value != undefined) value = decodeURIComponent(value);
238
419
239 if (hash[name] !== undefined) {
420 if (key in hash) {
240 if (hash[name].constructor != Array)
421 if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
241 hash[name] = [hash[name]];
422 hash[key].push(value);
242 if (value) hash[name].push(value);
243 }
423 }
244 else hash[name] = value;
424 else hash[key] = value;
245 }
425 }
246 return hash;
426 return hash;
247 });
427 });
@@ -256,6 +436,10 Object.extend(String.prototype, {
256 String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
436 String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
257 },
437 },
258
438
439 times: function(count) {
440 return count < 1 ? '' : new Array(count + 1).join(this);
441 },
442
259 camelize: function() {
443 camelize: function() {
260 var parts = this.split('-'), len = parts.length;
444 var parts = this.split('-'), len = parts.length;
261 if (len == 1) return parts[0];
445 if (len == 1) return parts[0];
@@ -283,52 +467,131 Object.extend(String.prototype, {
283 },
467 },
284
468
285 inspect: function(useDoubleQuotes) {
469 inspect: function(useDoubleQuotes) {
286 var escapedString = this.replace(/\\/g, '\\\\');
470 var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
287 if (useDoubleQuotes)
471 var character = String.specialChar[match[0]];
288 return '"' + escapedString.replace(/"/g, '\\"') + '"';
472 return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
289 else
473 });
474 if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
290 return "'" + escapedString.replace(/'/g, '\\\'') + "'";
475 return "'" + escapedString.replace(/'/g, '\\\'') + "'";
476 },
477
478 toJSON: function() {
479 return this.inspect(true);
480 },
481
482 unfilterJSON: function(filter) {
483 return this.sub(filter || Prototype.JSONFilter, '#{1}');
484 },
485
486 isJSON: function() {
487 var str = this;
488 if (str.blank()) return false;
489 str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
490 return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
491 },
492
493 evalJSON: function(sanitize) {
494 var json = this.unfilterJSON();
495 try {
496 if (!sanitize || json.isJSON()) return eval('(' + json + ')');
497 } catch (e) { }
498 throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
499 },
500
501 include: function(pattern) {
502 return this.indexOf(pattern) > -1;
503 },
504
505 startsWith: function(pattern) {
506 return this.indexOf(pattern) === 0;
507 },
508
509 endsWith: function(pattern) {
510 var d = this.length - pattern.length;
511 return d >= 0 && this.lastIndexOf(pattern) === d;
512 },
513
514 empty: function() {
515 return this == '';
516 },
517
518 blank: function() {
519 return /^\s*$/.test(this);
520 },
521
522 interpolate: function(object, pattern) {
523 return new Template(this, pattern).evaluate(object);
524 }
525 });
526
527 if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
528 escapeHTML: function() {
529 return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
530 },
531 unescapeHTML: function() {
532 return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
291 }
533 }
292 });
534 });
293
535
294 String.prototype.gsub.prepareReplacement = function(replacement) {
536 String.prototype.gsub.prepareReplacement = function(replacement) {
295 if (typeof replacement == 'function') return replacement;
537 if (Object.isFunction(replacement)) return replacement;
296 var template = new Template(replacement);
538 var template = new Template(replacement);
297 return function(match) { return template.evaluate(match) };
539 return function(match) { return template.evaluate(match) };
298 }
540 };
299
541
300 String.prototype.parseQuery = String.prototype.toQueryParams;
542 String.prototype.parseQuery = String.prototype.toQueryParams;
301
543
302 var Template = Class.create();
544 Object.extend(String.prototype.escapeHTML, {
303 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
545 div: document.createElement('div'),
304 Template.prototype = {
546 text: document.createTextNode('')
547 });
548
549 with (String.prototype.escapeHTML) div.appendChild(text);
550
551 var Template = Class.create({
305 initialize: function(template, pattern) {
552 initialize: function(template, pattern) {
306 this.template = template.toString();
553 this.template = template.toString();
307 this.pattern = pattern || Template.Pattern;
554 this.pattern = pattern || Template.Pattern;
308 },
555 },
309
556
310 evaluate: function(object) {
557 evaluate: function(object) {
558 if (Object.isFunction(object.toTemplateReplacements))
559 object = object.toTemplateReplacements();
560
311 return this.template.gsub(this.pattern, function(match) {
561 return this.template.gsub(this.pattern, function(match) {
312 var before = match[1];
562 if (object == null) return '';
563
564 var before = match[1] || '';
313 if (before == '\\') return match[2];
565 if (before == '\\') return match[2];
314 return before + String.interpret(object[match[3]]);
566
315 });
567 var ctx = object, expr = match[3];
568 var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
569 match = pattern.exec(expr);
570 if (match == null) return before;
571
572 while (match != null) {
573 var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
574 ctx = ctx[comp];
575 if (null == ctx || '' == match[3]) break;
576 expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
577 match = pattern.exec(expr);
316 }
578 }
579
580 return before + String.interpret(ctx);
581 }.bind(this));
317 }
582 }
583 });
584 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
318
585
319 var $break = new Object();
586 var $break = { };
320 var $continue = new Object();
321
587
322 var Enumerable = {
588 var Enumerable = {
323 each: function(iterator) {
589 each: function(iterator, context) {
324 var index = 0;
590 var index = 0;
591 iterator = iterator.bind(context);
325 try {
592 try {
326 this._each(function(value) {
593 this._each(function(value) {
327 try {
328 iterator(value, index++);
594 iterator(value, index++);
329 } catch (e) {
330 if (e != $continue) throw e;
331 }
332 });
595 });
333 } catch (e) {
596 } catch (e) {
334 if (e != $break) throw e;
597 if (e != $break) throw e;
@@ -336,40 +599,45 var Enumerable = {
336 return this;
599 return this;
337 },
600 },
338
601
339 eachSlice: function(number, iterator) {
602 eachSlice: function(number, iterator, context) {
603 iterator = iterator ? iterator.bind(context) : Prototype.K;
340 var index = -number, slices = [], array = this.toArray();
604 var index = -number, slices = [], array = this.toArray();
341 while ((index += number) < array.length)
605 while ((index += number) < array.length)
342 slices.push(array.slice(index, index+number));
606 slices.push(array.slice(index, index+number));
343 return slices.map(iterator);
607 return slices.collect(iterator, context);
344 },
608 },
345
609
346 all: function(iterator) {
610 all: function(iterator, context) {
611 iterator = iterator ? iterator.bind(context) : Prototype.K;
347 var result = true;
612 var result = true;
348 this.each(function(value, index) {
613 this.each(function(value, index) {
349 result = result && !!(iterator || Prototype.K)(value, index);
614 result = result && !!iterator(value, index);
350 if (!result) throw $break;
615 if (!result) throw $break;
351 });
616 });
352 return result;
617 return result;
353 },
618 },
354
619
355 any: function(iterator) {
620 any: function(iterator, context) {
621 iterator = iterator ? iterator.bind(context) : Prototype.K;
356 var result = false;
622 var result = false;
357 this.each(function(value, index) {
623 this.each(function(value, index) {
358 if (result = !!(iterator || Prototype.K)(value, index))
624 if (result = !!iterator(value, index))
359 throw $break;
625 throw $break;
360 });
626 });
361 return result;
627 return result;
362 },
628 },
363
629
364 collect: function(iterator) {
630 collect: function(iterator, context) {
631 iterator = iterator ? iterator.bind(context) : Prototype.K;
365 var results = [];
632 var results = [];
366 this.each(function(value, index) {
633 this.each(function(value, index) {
367 results.push((iterator || Prototype.K)(value, index));
634 results.push(iterator(value, index));
368 });
635 });
369 return results;
636 return results;
370 },
637 },
371
638
372 detect: function(iterator) {
639 detect: function(iterator, context) {
640 iterator = iterator.bind(context);
373 var result;
641 var result;
374 this.each(function(value, index) {
642 this.each(function(value, index) {
375 if (iterator(value, index)) {
643 if (iterator(value, index)) {
@@ -380,7 +648,8 var Enumerable = {
380 return result;
648 return result;
381 },
649 },
382
650
383 findAll: function(iterator) {
651 findAll: function(iterator, context) {
652 iterator = iterator.bind(context);
384 var results = [];
653 var results = [];
385 this.each(function(value, index) {
654 this.each(function(value, index) {
386 if (iterator(value, index))
655 if (iterator(value, index))
@@ -389,17 +658,24 var Enumerable = {
389 return results;
658 return results;
390 },
659 },
391
660
392 grep: function(pattern, iterator) {
661 grep: function(filter, iterator, context) {
662 iterator = iterator ? iterator.bind(context) : Prototype.K;
393 var results = [];
663 var results = [];
664
665 if (Object.isString(filter))
666 filter = new RegExp(filter);
667
394 this.each(function(value, index) {
668 this.each(function(value, index) {
395 var stringValue = value.toString();
669 if (filter.match(value))
396 if (stringValue.match(pattern))
670 results.push(iterator(value, index));
397 results.push((iterator || Prototype.K)(value, index));
671 });
398 })
399 return results;
672 return results;
400 },
673 },
401
674
402 include: function(object) {
675 include: function(object) {
676 if (Object.isFunction(this.indexOf))
677 if (this.indexOf(object) != -1) return true;
678
403 var found = false;
679 var found = false;
404 this.each(function(value) {
680 this.each(function(value) {
405 if (value == object) {
681 if (value == object) {
@@ -411,14 +687,15 var Enumerable = {
411 },
687 },
412
688
413 inGroupsOf: function(number, fillWith) {
689 inGroupsOf: function(number, fillWith) {
414 fillWith = fillWith === undefined ? null : fillWith;
690 fillWith = Object.isUndefined(fillWith) ? null : fillWith;
415 return this.eachSlice(number, function(slice) {
691 return this.eachSlice(number, function(slice) {
416 while(slice.length < number) slice.push(fillWith);
692 while(slice.length < number) slice.push(fillWith);
417 return slice;
693 return slice;
418 });
694 });
419 },
695 },
420
696
421 inject: function(memo, iterator) {
697 inject: function(memo, iterator, context) {
698 iterator = iterator.bind(context);
422 this.each(function(value, index) {
699 this.each(function(value, index) {
423 memo = iterator(memo, value, index);
700 memo = iterator(memo, value, index);
424 });
701 });
@@ -432,30 +709,33 var Enumerable = {
432 });
709 });
433 },
710 },
434
711
435 max: function(iterator) {
712 max: function(iterator, context) {
713 iterator = iterator ? iterator.bind(context) : Prototype.K;
436 var result;
714 var result;
437 this.each(function(value, index) {
715 this.each(function(value, index) {
438 value = (iterator || Prototype.K)(value, index);
716 value = iterator(value, index);
439 if (result == undefined || value >= result)
717 if (result == null || value >= result)
440 result = value;
718 result = value;
441 });
719 });
442 return result;
720 return result;
443 },
721 },
444
722
445 min: function(iterator) {
723 min: function(iterator, context) {
724 iterator = iterator ? iterator.bind(context) : Prototype.K;
446 var result;
725 var result;
447 this.each(function(value, index) {
726 this.each(function(value, index) {
448 value = (iterator || Prototype.K)(value, index);
727 value = iterator(value, index);
449 if (result == undefined || value < result)
728 if (result == null || value < result)
450 result = value;
729 result = value;
451 });
730 });
452 return result;
731 return result;
453 },
732 },
454
733
455 partition: function(iterator) {
734 partition: function(iterator, context) {
735 iterator = iterator ? iterator.bind(context) : Prototype.K;
456 var trues = [], falses = [];
736 var trues = [], falses = [];
457 this.each(function(value, index) {
737 this.each(function(value, index) {
458 ((iterator || Prototype.K)(value, index) ?
738 (iterator(value, index) ?
459 trues : falses).push(value);
739 trues : falses).push(value);
460 });
740 });
461 return [trues, falses];
741 return [trues, falses];
@@ -463,13 +743,14 var Enumerable = {
463
743
464 pluck: function(property) {
744 pluck: function(property) {
465 var results = [];
745 var results = [];
466 this.each(function(value, index) {
746 this.each(function(value) {
467 results.push(value[property]);
747 results.push(value[property]);
468 });
748 });
469 return results;
749 return results;
470 },
750 },
471
751
472 reject: function(iterator) {
752 reject: function(iterator, context) {
753 iterator = iterator.bind(context);
473 var results = [];
754 var results = [];
474 this.each(function(value, index) {
755 this.each(function(value, index) {
475 if (!iterator(value, index))
756 if (!iterator(value, index))
@@ -478,7 +759,8 var Enumerable = {
478 return results;
759 return results;
479 },
760 },
480
761
481 sortBy: function(iterator) {
762 sortBy: function(iterator, context) {
763 iterator = iterator.bind(context);
482 return this.map(function(value, index) {
764 return this.map(function(value, index) {
483 return {value: value, criteria: iterator(value, index)};
765 return {value: value, criteria: iterator(value, index)};
484 }).sort(function(left, right) {
766 }).sort(function(left, right) {
@@ -493,7 +775,7 var Enumerable = {
493
775
494 zip: function() {
776 zip: function() {
495 var iterator = Prototype.K, args = $A(arguments);
777 var iterator = Prototype.K, args = $A(arguments);
496 if (typeof args.last() == 'function')
778 if (Object.isFunction(args.last()))
497 iterator = args.pop();
779 iterator = args.pop();
498
780
499 var collections = [this].concat(args).map($A);
781 var collections = [this].concat(args).map($A);
@@ -509,31 +791,42 var Enumerable = {
509 inspect: function() {
791 inspect: function() {
510 return '#<Enumerable:' + this.toArray().inspect() + '>';
792 return '#<Enumerable:' + this.toArray().inspect() + '>';
511 }
793 }
512 }
794 };
513
795
514 Object.extend(Enumerable, {
796 Object.extend(Enumerable, {
515 map: Enumerable.collect,
797 map: Enumerable.collect,
516 find: Enumerable.detect,
798 find: Enumerable.detect,
517 select: Enumerable.findAll,
799 select: Enumerable.findAll,
800 filter: Enumerable.findAll,
518 member: Enumerable.include,
801 member: Enumerable.include,
519 entries: Enumerable.toArray
802 entries: Enumerable.toArray,
803 every: Enumerable.all,
804 some: Enumerable.any
520 });
805 });
521 var $A = Array.from = function(iterable) {
806 function $A(iterable) {
522 if (!iterable) return [];
807 if (!iterable) return [];
523 if (iterable.toArray) {
808 if (iterable.toArray) return iterable.toArray();
524 return iterable.toArray();
809 var length = iterable.length, results = new Array(length);
525 } else {
810 while (length--) results[length] = iterable[length];
526 var results = [];
811 return results;
527 for (var i = 0, length = iterable.length; i < length; i++)
812 }
528 results.push(iterable[i]);
813
814 if (Prototype.Browser.WebKit) {
815 function $A(iterable) {
816 if (!iterable) return [];
817 if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
818 iterable.toArray) return iterable.toArray();
819 var length = iterable.length, results = new Array(length);
820 while (length--) results[length] = iterable[length];
529 return results;
821 return results;
530 }
822 }
531 }
823 }
532
824
825 Array.from = $A;
826
533 Object.extend(Array.prototype, Enumerable);
827 Object.extend(Array.prototype, Enumerable);
534
828
535 if (!Array.prototype._reverse)
829 if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
536 Array.prototype._reverse = Array.prototype.reverse;
537
830
538 Object.extend(Array.prototype, {
831 Object.extend(Array.prototype, {
539 _each: function(iterator) {
832 _each: function(iterator) {
@@ -562,7 +855,7 Object.extend(Array.prototype, {
562
855
563 flatten: function() {
856 flatten: function() {
564 return this.inject([], function(array, value) {
857 return this.inject([], function(array, value) {
565 return array.concat(value && value.constructor == Array ?
858 return array.concat(Object.isArray(value) ?
566 value.flatten() : [value]);
859 value.flatten() : [value]);
567 });
860 });
568 },
861 },
@@ -574,12 +867,6 Object.extend(Array.prototype, {
574 });
867 });
575 },
868 },
576
869
577 indexOf: function(object) {
578 for (var i = 0, length = this.length; i < length; i++)
579 if (this[i] == object) return i;
580 return -1;
581 },
582
583 reverse: function(inline) {
870 reverse: function(inline) {
584 return (inline !== false ? this : this.toArray())._reverse();
871 return (inline !== false ? this : this.toArray())._reverse();
585 },
872 },
@@ -588,9 +875,17 Object.extend(Array.prototype, {
588 return this.length > 1 ? this : this[0];
875 return this.length > 1 ? this : this[0];
589 },
876 },
590
877
591 uniq: function() {
878 uniq: function(sorted) {
592 return this.inject([], function(array, value) {
879 return this.inject([], function(array, value, index) {
593 return array.include(value) ? array : array.concat([value]);
880 if (0 == index || (sorted ? array.last() != value : !array.include(value)))
881 array.push(value);
882 return array;
883 });
884 },
885
886 intersect: function(array) {
887 return this.uniq().findAll(function(item) {
888 return array.detect(function(value) { return item === value });
594 });
889 });
595 },
890 },
596
891
@@ -604,22 +899,51 Object.extend(Array.prototype, {
604
899
605 inspect: function() {
900 inspect: function() {
606 return '[' + this.map(Object.inspect).join(', ') + ']';
901 return '[' + this.map(Object.inspect).join(', ') + ']';
902 },
903
904 toJSON: function() {
905 var results = [];
906 this.each(function(object) {
907 var value = Object.toJSON(object);
908 if (!Object.isUndefined(value)) results.push(value);
909 });
910 return '[' + results.join(', ') + ']';
607 }
911 }
608 });
912 });
609
913
914 // use native browser JS 1.6 implementation if available
915 if (Object.isFunction(Array.prototype.forEach))
916 Array.prototype._each = Array.prototype.forEach;
917
918 if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
919 i || (i = 0);
920 var length = this.length;
921 if (i < 0) i = length + i;
922 for (; i < length; i++)
923 if (this[i] === item) return i;
924 return -1;
925 };
926
927 if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
928 i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
929 var n = this.slice(0, i).reverse().indexOf(item);
930 return (n < 0) ? n : i - n - 1;
931 };
932
610 Array.prototype.toArray = Array.prototype.clone;
933 Array.prototype.toArray = Array.prototype.clone;
611
934
612 function $w(string){
935 function $w(string) {
936 if (!Object.isString(string)) return [];
613 string = string.strip();
937 string = string.strip();
614 return string ? string.split(/\s+/) : [];
938 return string ? string.split(/\s+/) : [];
615 }
939 }
616
940
617 if(window.opera){
941 if (Prototype.Browser.Opera){
618 Array.prototype.concat = function(){
942 Array.prototype.concat = function() {
619 var array = [];
943 var array = [];
620 for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
944 for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
621 for(var i = 0, length = arguments.length; i < length; i++) {
945 for (var i = 0, length = arguments.length; i < length; i++) {
622 if(arguments[i].constructor == Array) {
946 if (Object.isArray(arguments[i])) {
623 for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
947 for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
624 array.push(arguments[i][j]);
948 array.push(arguments[i][j]);
625 } else {
949 } else {
@@ -627,53 +951,78 if(window.opera){
627 }
951 }
628 }
952 }
629 return array;
953 return array;
630 }
631 }
632 var Hash = function(obj) {
633 Object.extend(this, obj || {});
634 };
954 };
955 }
956 Object.extend(Number.prototype, {
957 toColorPart: function() {
958 return this.toPaddedString(2, 16);
959 },
635
960
636 Object.extend(Hash, {
961 succ: function() {
637 toQueryString: function(obj) {
962 return this + 1;
638 var parts = [];
963 },
639
964
640 this.prototype._each.call(obj, function(pair) {
965 times: function(iterator) {
641 if (!pair.key) return;
966 $R(0, this, true).each(iterator);
967 return this;
968 },
642
969
643 if (pair.value && pair.value.constructor == Array) {
970 toPaddedString: function(length, radix) {
644 var values = pair.value.compact();
971 var string = this.toString(radix || 10);
645 if (values.length < 2) pair.value = values.reduce();
972 return '0'.times(length - string.length) + string;
646 else {
973 },
647 key = encodeURIComponent(pair.key);
974
648 values.each(function(value) {
975 toJSON: function() {
649 value = value != undefined ? encodeURIComponent(value) : '';
976 return isFinite(this) ? this.toString() : 'null';
650 parts.push(key + '=' + encodeURIComponent(value));
651 });
652 return;
653 }
654 }
977 }
655 if (pair.value == undefined) pair[1] = '';
656 parts.push(pair.map(encodeURIComponent).join('='));
657 });
978 });
658
979
659 return parts.join('&');
980 $w('abs round ceil floor').each(function(method){
660 }
981 Number.prototype[method] = Math[method].methodize();
661 });
982 });
983 function $H(object) {
984 return new Hash(object);
985 };
662
986
663 Object.extend(Hash.prototype, Enumerable);
987 var Hash = Class.create(Enumerable, (function() {
664 Object.extend(Hash.prototype, {
988
665 _each: function(iterator) {
989 function toQueryPair(key, value) {
666 for (var key in this) {
990 if (Object.isUndefined(value)) return key;
667 var value = this[key];
991 return key + '=' + encodeURIComponent(String.interpret(value));
668 if (value && value == Hash.prototype[key]) continue;
992 }
993
994 return {
995 initialize: function(object) {
996 this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
997 },
669
998
670 var pair = [key, value];
999 _each: function(iterator) {
1000 for (var key in this._object) {
1001 var value = this._object[key], pair = [key, value];
671 pair.key = key;
1002 pair.key = key;
672 pair.value = value;
1003 pair.value = value;
673 iterator(pair);
1004 iterator(pair);
674 }
1005 }
675 },
1006 },
676
1007
1008 set: function(key, value) {
1009 return this._object[key] = value;
1010 },
1011
1012 get: function(key) {
1013 return this._object[key];
1014 },
1015
1016 unset: function(key) {
1017 var value = this._object[key];
1018 delete this._object[key];
1019 return value;
1020 },
1021
1022 toObject: function() {
1023 return Object.clone(this._object);
1024 },
1025
677 keys: function() {
1026 keys: function() {
678 return this.pluck('key');
1027 return this.pluck('key');
679 },
1028 },
@@ -682,47 +1031,55 Object.extend(Hash.prototype, {
682 return this.pluck('value');
1031 return this.pluck('value');
683 },
1032 },
684
1033
685 merge: function(hash) {
1034 index: function(value) {
686 return $H(hash).inject(this, function(mergedHash, pair) {
1035 var match = this.detect(function(pair) {
687 mergedHash[pair.key] = pair.value;
1036 return pair.value === value;
688 return mergedHash;
689 });
1037 });
1038 return match && match.key;
690 },
1039 },
691
1040
692 remove: function() {
1041 merge: function(object) {
693 var result;
1042 return this.clone().update(object);
694 for(var i = 0, length = arguments.length; i < length; i++) {
1043 },
695 var value = this[arguments[i]];
1044
696 if (value !== undefined){
1045 update: function(object) {
697 if (result === undefined) result = value;
1046 return new Hash(object).inject(this, function(result, pair) {
698 else {
1047 result.set(pair.key, pair.value);
699 if (result.constructor != Array) result = [result];
700 result.push(value)
701 }
702 }
703 delete this[arguments[i]];
704 }
705 return result;
1048 return result;
1049 });
706 },
1050 },
707
1051
708 toQueryString: function() {
1052 toQueryString: function() {
709 return Hash.toQueryString(this);
1053 return this.map(function(pair) {
1054 var key = encodeURIComponent(pair.key), values = pair.value;
1055
1056 if (values && typeof values == 'object') {
1057 if (Object.isArray(values))
1058 return values.map(toQueryPair.curry(key)).join('&');
1059 }
1060 return toQueryPair(key, values);
1061 }).join('&');
710 },
1062 },
711
1063
712 inspect: function() {
1064 inspect: function() {
713 return '#<Hash:{' + this.map(function(pair) {
1065 return '#<Hash:{' + this.map(function(pair) {
714 return pair.map(Object.inspect).join(': ');
1066 return pair.map(Object.inspect).join(': ');
715 }).join(', ') + '}>';
1067 }).join(', ') + '}>';
1068 },
1069
1070 toJSON: function() {
1071 return Object.toJSON(this.toObject());
1072 },
1073
1074 clone: function() {
1075 return new Hash(this);
716 }
1076 }
717 });
1077 }
1078 })());
718
1079
719 function $H(object) {
1080 Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
720 if (object && object.constructor == Hash) return object;
1081 Hash.from = $H;
721 return new Hash(object);
1082 var ObjectRange = Class.create(Enumerable, {
722 };
723 ObjectRange = Class.create();
724 Object.extend(ObjectRange.prototype, Enumerable);
725 Object.extend(ObjectRange.prototype, {
726 initialize: function(start, end, exclusive) {
1083 initialize: function(start, end, exclusive) {
727 this.start = start;
1084 this.start = start;
728 this.end = end;
1085 this.end = end;
@@ -748,7 +1105,7 Object.extend(ObjectRange.prototype, {
748
1105
749 var $R = function(start, end, exclusive) {
1106 var $R = function(start, end, exclusive) {
750 return new ObjectRange(start, end, exclusive);
1107 return new ObjectRange(start, end, exclusive);
751 }
1108 };
752
1109
753 var Ajax = {
1110 var Ajax = {
754 getTransport: function() {
1111 getTransport: function() {
@@ -760,7 +1117,7 var Ajax = {
760 },
1117 },
761
1118
762 activeRequestCount: 0
1119 activeRequestCount: 0
763 }
1120 };
764
1121
765 Ajax.Responders = {
1122 Ajax.Responders = {
766 responders: [],
1123 responders: [],
@@ -780,7 +1137,7 Ajax.Responders = {
780
1137
781 dispatch: function(callback, request, transport, json) {
1138 dispatch: function(callback, request, transport, json) {
782 this.each(function(responder) {
1139 this.each(function(responder) {
783 if (typeof responder[callback] == 'function') {
1140 if (Object.isFunction(responder[callback])) {
784 try {
1141 try {
785 responder[callback].apply(responder, [request, transport, json]);
1142 responder[callback].apply(responder, [request, transport, json]);
786 } catch (e) {}
1143 } catch (e) { }
@@ -792,49 +1149,45 Ajax.Responders = {
792 Object.extend(Ajax.Responders, Enumerable);
1149 Object.extend(Ajax.Responders, Enumerable);
793
1150
794 Ajax.Responders.register({
1151 Ajax.Responders.register({
795 onCreate: function() {
1152 onCreate: function() { Ajax.activeRequestCount++ },
796 Ajax.activeRequestCount++;
1153 onComplete: function() { Ajax.activeRequestCount-- }
797 },
798 onComplete: function() {
799 Ajax.activeRequestCount--;
800 }
801 });
1154 });
802
1155
803 Ajax.Base = function() {};
1156 Ajax.Base = Class.create({
804 Ajax.Base.prototype = {
1157 initialize: function(options) {
805 setOptions: function(options) {
806 this.options = {
1158 this.options = {
807 method: 'post',
1159 method: 'post',
808 asynchronous: true,
1160 asynchronous: true,
809 contentType: 'application/x-www-form-urlencoded',
1161 contentType: 'application/x-www-form-urlencoded',
810 encoding: 'UTF-8',
1162 encoding: 'UTF-8',
811 parameters: ''
1163 parameters: '',
812 }
1164 evalJSON: true,
1165 evalJS: true
1166 };
813 Object.extend(this.options, options || {});
1167 Object.extend(this.options, options || { });
814
1168
815 this.options.method = this.options.method.toLowerCase();
1169 this.options.method = this.options.method.toLowerCase();
816 if (typeof this.options.parameters == 'string')
1170
1171 if (Object.isString(this.options.parameters))
817 this.options.parameters = this.options.parameters.toQueryParams();
1172 this.options.parameters = this.options.parameters.toQueryParams();
1173 else if (Object.isHash(this.options.parameters))
1174 this.options.parameters = this.options.parameters.toObject();
818 }
1175 }
819 }
1176 });
820
821 Ajax.Request = Class.create();
822 Ajax.Request.Events =
823 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
824
1177
825 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
1178 Ajax.Request = Class.create(Ajax.Base, {
826 _complete: false,
1179 _complete: false,
827
1180
828 initialize: function(url, options) {
1181 initialize: function($super, url, options) {
1182 $super(options);
829 this.transport = Ajax.getTransport();
1183 this.transport = Ajax.getTransport();
830 this.setOptions(options);
831 this.request(url);
1184 this.request(url);
832 },
1185 },
833
1186
834 request: function(url) {
1187 request: function(url) {
835 this.url = url;
1188 this.url = url;
836 this.method = this.options.method;
1189 this.method = this.options.method;
837 var params = this.options.parameters;
1190 var params = Object.clone(this.options.parameters);
838
1191
839 if (!['get', 'post'].include(this.method)) {
1192 if (!['get', 'post'].include(this.method)) {
840 // simulate other verbs over post
1193 // simulate other verbs over post
@@ -842,28 +1195,31 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
842 this.method = 'post';
1195 this.method = 'post';
843 }
1196 }
844
1197
845 params = Hash.toQueryString(params);
1198 this.parameters = params;
846 if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
847
1199
1200 if (params = Object.toQueryString(params)) {
848 // when GET, append parameters to URL
1201 // when GET, append parameters to URL
849 if (this.method == 'get' && params)
1202 if (this.method == 'get')
850 this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
1203 this.url += (this.url.include('?') ? '&' : '?') + params;
1204 else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1205 params += '&_=';
1206 }
851
1207
852 try {
1208 try {
853 Ajax.Responders.dispatch('onCreate', this, this.transport);
1209 var response = new Ajax.Response(this);
1210 if (this.options.onCreate) this.options.onCreate(response);
1211 Ajax.Responders.dispatch('onCreate', this, response);
854
1212
855 this.transport.open(this.method.toUpperCase(), this.url,
1213 this.transport.open(this.method.toUpperCase(), this.url,
856 this.options.asynchronous);
1214 this.options.asynchronous);
857
1215
858 if (this.options.asynchronous)
1216 if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
859 setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
860
1217
861 this.transport.onreadystatechange = this.onStateChange.bind(this);
1218 this.transport.onreadystatechange = this.onStateChange.bind(this);
862 this.setRequestHeaders();
1219 this.setRequestHeaders();
863
1220
864 var body = this.method == 'post' ? (this.options.postBody || params) : null;
1221 this.body = this.method == 'post' ? (this.options.postBody || params) : null;
865
1222 this.transport.send(this.body);
866 this.transport.send(body);
867
1223
868 /* Force Firefox to handle ready state 4 for synchronous requests */
1224 /* Force Firefox to handle ready state 4 for synchronous requests */
869 if (!this.options.asynchronous && this.transport.overrideMimeType)
1225 if (!this.options.asynchronous && this.transport.overrideMimeType)
@@ -905,7 +1261,7 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
905 if (typeof this.options.requestHeaders == 'object') {
1261 if (typeof this.options.requestHeaders == 'object') {
906 var extras = this.options.requestHeaders;
1262 var extras = this.options.requestHeaders;
907
1263
908 if (typeof extras.push == 'function')
1264 if (Object.isFunction(extras.push))
909 for (var i = 0, length = extras.length; i < length; i += 2)
1265 for (var i = 0, length = extras.length; i < length; i += 2)
910 headers[extras[i]] = extras[i+1];
1266 headers[extras[i]] = extras[i+1];
911 else
1267 else
@@ -917,32 +1273,39 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
917 },
1273 },
918
1274
919 success: function() {
1275 success: function() {
920 return !this.transport.status
1276 var status = this.getStatus();
921 || (this.transport.status >= 200 && this.transport.status < 300);
1277 return !status || (status >= 200 && status < 300);
1278 },
1279
1280 getStatus: function() {
1281 try {
1282 return this.transport.status || 0;
1283 } catch (e) { return 0 }
922 },
1284 },
923
1285
924 respondToReadyState: function(readyState) {
1286 respondToReadyState: function(readyState) {
925 var state = Ajax.Request.Events[readyState];
1287 var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
926 var transport = this.transport, json = this.evalJSON();
927
1288
928 if (state == 'Complete') {
1289 if (state == 'Complete') {
929 try {
1290 try {
930 this._complete = true;
1291 this._complete = true;
931 (this.options['on' + this.transport.status]
1292 (this.options['on' + response.status]
932 || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1293 || this.options['on' + (this.success() ? 'Success' : 'Failure')]
933 || Prototype.emptyFunction)(transport, json);
1294 || Prototype.emptyFunction)(response, response.headerJSON);
934 } catch (e) {
1295 } catch (e) {
935 this.dispatchException(e);
1296 this.dispatchException(e);
936 }
1297 }
937
1298
938 if ((this.getHeader('Content-type') || 'text/javascript').strip().
1299 var contentType = response.getHeader('Content-type');
939 match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
1300 if (this.options.evalJS == 'force'
1301 || (this.options.evalJS && contentType
1302 && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
940 this.evalResponse();
1303 this.evalResponse();
941 }
1304 }
942
1305
943 try {
1306 try {
944 (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
1307 (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
945 Ajax.Responders.dispatch('on' + state, this, transport, json);
1308 Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
946 } catch (e) {
1309 } catch (e) {
947 this.dispatchException(e);
1310 this.dispatchException(e);
948 }
1311 }
@@ -959,16 +1322,9 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
959 } catch (e) { return null }
1322 } catch (e) { return null }
960 },
1323 },
961
1324
962 evalJSON: function() {
1325 evalResponse: function() {
963 try {
1326 try {
964 var json = this.getHeader('X-JSON');
1327 return eval((this.transport.responseText || '').unfilterJSON());
965 return json ? eval('(' + json + ')') : null;
966 } catch (e) { return null }
967 },
968
969 evalResponse: function() {
970 try {
971 return eval(this.transport.responseText);
972 } catch (e) {
1328 } catch (e) {
973 this.dispatchException(e);
1329 this.dispatchException(e);
974 }
1330 }
@@ -980,51 +1336,120 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
980 }
1336 }
981 });
1337 });
982
1338
983 Ajax.Updater = Class.create();
1339 Ajax.Request.Events =
1340 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1341
1342 Ajax.Response = Class.create({
1343 initialize: function(request){
1344 this.request = request;
1345 var transport = this.transport = request.transport,
1346 readyState = this.readyState = transport.readyState;
1347
1348 if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1349 this.status = this.getStatus();
1350 this.statusText = this.getStatusText();
1351 this.responseText = String.interpret(transport.responseText);
1352 this.headerJSON = this._getHeaderJSON();
1353 }
1354
1355 if(readyState == 4) {
1356 var xml = transport.responseXML;
1357 this.responseXML = Object.isUndefined(xml) ? null : xml;
1358 this.responseJSON = this._getResponseJSON();
1359 }
1360 },
1361
1362 status: 0,
1363 statusText: '',
1364
1365 getStatus: Ajax.Request.prototype.getStatus,
1366
1367 getStatusText: function() {
1368 try {
1369 return this.transport.statusText || '';
1370 } catch (e) { return '' }
1371 },
1372
1373 getHeader: Ajax.Request.prototype.getHeader,
1374
1375 getAllHeaders: function() {
1376 try {
1377 return this.getAllResponseHeaders();
1378 } catch (e) { return null }
1379 },
1380
1381 getResponseHeader: function(name) {
1382 return this.transport.getResponseHeader(name);
1383 },
1384
1385 getAllResponseHeaders: function() {
1386 return this.transport.getAllResponseHeaders();
1387 },
1388
1389 _getHeaderJSON: function() {
1390 var json = this.getHeader('X-JSON');
1391 if (!json) return null;
1392 json = decodeURIComponent(escape(json));
1393 try {
1394 return json.evalJSON(this.request.options.sanitizeJSON);
1395 } catch (e) {
1396 this.request.dispatchException(e);
1397 }
1398 },
1399
1400 _getResponseJSON: function() {
1401 var options = this.request.options;
1402 if (!options.evalJSON || (options.evalJSON != 'force' &&
1403 !(this.getHeader('Content-type') || '').include('application/json')) ||
1404 this.responseText.blank())
1405 return null;
1406 try {
1407 return this.responseText.evalJSON(options.sanitizeJSON);
1408 } catch (e) {
1409 this.request.dispatchException(e);
1410 }
1411 }
1412 });
984
1413
985 Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
1414 Ajax.Updater = Class.create(Ajax.Request, {
986 initialize: function(container, url, options) {
1415 initialize: function($super, container, url, options) {
987 this.container = {
1416 this.container = {
988 success: (container.success || container),
1417 success: (container.success || container),
989 failure: (container.failure || (container.success ? null : container))
1418 failure: (container.failure || (container.success ? null : container))
990 }
1419 };
991
992 this.transport = Ajax.getTransport();
993 this.setOptions(options);
994
1420
995 var onComplete = this.options.onComplete || Prototype.emptyFunction;
1421 options = Object.clone(options);
996 this.options.onComplete = (function(transport, param) {
1422 var onComplete = options.onComplete;
997 this.updateContent();
1423 options.onComplete = (function(response, json) {
998 onComplete(transport, param);
1424 this.updateContent(response.responseText);
1425 if (Object.isFunction(onComplete)) onComplete(response, json);
999 }).bind(this);
1426 }).bind(this);
1000
1427
1001 this.request(url);
1428 $super(url, options);
1002 },
1429 },
1003
1430
1004 updateContent: function() {
1431 updateContent: function(responseText) {
1005 var receiver = this.container[this.success() ? 'success' : 'failure'];
1432 var receiver = this.container[this.success() ? 'success' : 'failure'],
1006 var response = this.transport.responseText;
1433 options = this.options;
1007
1434
1008 if (!this.options.evalScripts) response = response.stripScripts();
1435 if (!options.evalScripts) responseText = responseText.stripScripts();
1009
1436
1010 if (receiver = $(receiver)) {
1437 if (receiver = $(receiver)) {
1011 if (this.options.insertion)
1438 if (options.insertion) {
1012 new this.options.insertion(receiver, response);
1439 if (Object.isString(options.insertion)) {
1013 else
1440 var insertion = { }; insertion[options.insertion] = responseText;
1014 receiver.update(response);
1441 receiver.insert(insertion);
1015 }
1442 }
1016
1443 else options.insertion(receiver, responseText);
1017 if (this.success()) {
1444 }
1018 if (this.onComplete)
1445 else receiver.update(responseText);
1019 setTimeout(this.onComplete.bind(this), 10);
1020 }
1446 }
1021 }
1447 }
1022 });
1448 });
1023
1449
1024 Ajax.PeriodicalUpdater = Class.create();
1450 Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1025 Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
1451 initialize: function($super, container, url, options) {
1026 initialize: function(container, url, options) {
1452 $super(options);
1027 this.setOptions(options);
1028 this.onComplete = this.options.onComplete;
1453 this.onComplete = this.options.onComplete;
1029
1454
1030 this.frequency = (this.options.frequency || 2);
1455 this.frequency = (this.options.frequency || 2);
@@ -1048,15 +1473,14 Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
1048 (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1473 (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1049 },
1474 },
1050
1475
1051 updateComplete: function(request) {
1476 updateComplete: function(response) {
1052 if (this.options.decay) {
1477 if (this.options.decay) {
1053 this.decay = (request.responseText == this.lastText ?
1478 this.decay = (response.responseText == this.lastText ?
1054 this.decay * this.options.decay : 1);
1479 this.decay * this.options.decay : 1);
1055
1480
1056 this.lastText = request.responseText;
1481 this.lastText = response.responseText;
1057 }
1482 }
1058 this.timer = setTimeout(this.onTimerEvent.bind(this),
1483 this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1059 this.decay * this.frequency * 1000);
1060 },
1484 },
1061
1485
1062 onTimerEvent: function() {
1486 onTimerEvent: function() {
@@ -1069,7 +1493,7 function $(element) {
1069 elements.push($(arguments[i]));
1493 elements.push($(arguments[i]));
1070 return elements;
1494 return elements;
1071 }
1495 }
1072 if (typeof element == 'string')
1496 if (Object.isString(element))
1073 element = document.getElementById(element);
1497 element = document.getElementById(element);
1074 return Element.extend(element);
1498 return Element.extend(element);
1075 }
1499 }
@@ -1080,63 +1504,51 if (Prototype.BrowserFeatures.XPath) {
1080 var query = document.evaluate(expression, $(parentElement) || document,
1504 var query = document.evaluate(expression, $(parentElement) || document,
1081 null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1505 null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1082 for (var i = 0, length = query.snapshotLength; i < length; i++)
1506 for (var i = 0, length = query.snapshotLength; i < length; i++)
1083 results.push(query.snapshotItem(i));
1507 results.push(Element.extend(query.snapshotItem(i)));
1084 return results;
1508 return results;
1085 };
1509 };
1086 }
1510 }
1087
1511
1088 document.getElementsByClassName = function(className, parentElement) {
1089 if (Prototype.BrowserFeatures.XPath) {
1090 var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
1091 return document._getElementsByXPath(q, parentElement);
1092 } else {
1093 var children = ($(parentElement) || document.body).getElementsByTagName('*');
1094 var elements = [], child;
1095 for (var i = 0, length = children.length; i < length; i++) {
1096 child = children[i];
1097 if (Element.hasClassName(child, className))
1098 elements.push(Element.extend(child));
1099 }
1100 return elements;
1101 }
1102 };
1103
1104 /*--------------------------------------------------------------------------*/
1512 /*--------------------------------------------------------------------------*/
1105
1513
1106 if (!window.Element)
1514 if (!window.Node) var Node = { };
1107 var Element = new Object();
1515
1108
1516 if (!Node.ELEMENT_NODE) {
1109 Element.extend = function(element) {
1517 // DOM level 2 ECMAScript Language Binding
1110 if (!element || _nativeExtensions || element.nodeType == 3) return element;
1518 Object.extend(Node, {
1111
1519 ELEMENT_NODE: 1,
1112 if (!element._extended && element.tagName && element != window) {
1520 ATTRIBUTE_NODE: 2,
1113 var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
1521 TEXT_NODE: 3,
1114
1522 CDATA_SECTION_NODE: 4,
1115 if (element.tagName == 'FORM')
1523 ENTITY_REFERENCE_NODE: 5,
1116 Object.extend(methods, Form.Methods);
1524 ENTITY_NODE: 6,
1117 if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
1525 PROCESSING_INSTRUCTION_NODE: 7,
1118 Object.extend(methods, Form.Element.Methods);
1526 COMMENT_NODE: 8,
1119
1527 DOCUMENT_NODE: 9,
1120 Object.extend(methods, Element.Methods.Simulated);
1528 DOCUMENT_TYPE_NODE: 10,
1121
1529 DOCUMENT_FRAGMENT_NODE: 11,
1122 for (var property in methods) {
1530 NOTATION_NODE: 12
1123 var value = methods[property];
1531 });
1124 if (typeof value == 'function' && !(property in element))
1125 element[property] = cache.findOrStore(value);
1126 }
1127 }
1532 }
1128
1533
1129 element._extended = true;
1534 (function() {
1130 return element;
1535 var element = this.Element;
1131 };
1536 this.Element = function(tagName, attributes) {
1132
1537 attributes = attributes || { };
1133 Element.extend.cache = {
1538 tagName = tagName.toLowerCase();
1134 findOrStore: function(value) {
1539 var cache = Element.cache;
1135 return this[value] = this[value] || function() {
1540 if (Prototype.Browser.IE && attributes.name) {
1136 return value.apply(null, [this].concat($A(arguments)));
1541 tagName = '<' + tagName + ' name="' + attributes.name + '">';
1137 }
1542 delete attributes.name;
1543 return Element.writeAttribute(document.createElement(tagName), attributes);
1138 }
1544 }
1545 if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
1546 return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
1139 };
1547 };
1548 Object.extend(this.Element, element || { });
1549 }).call(window);
1550
1551 Element.cache = { };
1140
1552
1141 Element.Methods = {
1553 Element.Methods = {
1142 visible: function(element) {
1554 visible: function(element) {
@@ -1165,28 +1577,74 Element.Methods = {
1165 return element;
1577 return element;
1166 },
1578 },
1167
1579
1168 update: function(element, html) {
1580 update: function(element, content) {
1169 html = typeof html == 'undefined' ? '' : html.toString();
1581 element = $(element);
1170 $(element).innerHTML = html.stripScripts();
1582 if (content && content.toElement) content = content.toElement();
1171 setTimeout(function() {html.evalScripts()}, 10);
1583 if (Object.isElement(content)) return element.update().insert(content);
1584 content = Object.toHTML(content);
1585 element.innerHTML = content.stripScripts();
1586 content.evalScripts.bind(content).defer();
1172 return element;
1587 return element;
1173 },
1588 },
1174
1589
1175 replace: function(element, html) {
1590 replace: function(element, content) {
1176 element = $(element);
1591 element = $(element);
1177 html = typeof html == 'undefined' ? '' : html.toString();
1592 if (content && content.toElement) content = content.toElement();
1178 if (element.outerHTML) {
1593 else if (!Object.isElement(content)) {
1179 element.outerHTML = html.stripScripts();
1594 content = Object.toHTML(content);
1180 } else {
1181 var range = element.ownerDocument.createRange();
1595 var range = element.ownerDocument.createRange();
1182 range.selectNodeContents(element);
1596 range.selectNode(element);
1183 element.parentNode.replaceChild(
1597 content.evalScripts.bind(content).defer();
1184 range.createContextualFragment(html.stripScripts()), element);
1598 content = range.createContextualFragment(content.stripScripts());
1599 }
1600 element.parentNode.replaceChild(content, element);
1601 return element;
1602 },
1603
1604 insert: function(element, insertions) {
1605 element = $(element);
1606
1607 if (Object.isString(insertions) || Object.isNumber(insertions) ||
1608 Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
1609 insertions = {bottom:insertions};
1610
1611 var content, t, range;
1612
1613 for (position in insertions) {
1614 content = insertions[position];
1615 position = position.toLowerCase();
1616 t = Element._insertionTranslations[position];
1617
1618 if (content && content.toElement) content = content.toElement();
1619 if (Object.isElement(content)) {
1620 t.insert(element, content);
1621 continue;
1622 }
1623
1624 content = Object.toHTML(content);
1625
1626 range = element.ownerDocument.createRange();
1627 t.initializeRange(element, range);
1628 t.insert(element, range.createContextualFragment(content.stripScripts()));
1629
1630 content.evalScripts.bind(content).defer();
1185 }
1631 }
1186 setTimeout(function() {html.evalScripts()}, 10);
1632
1187 return element;
1633 return element;
1188 },
1634 },
1189
1635
1636 wrap: function(element, wrapper, attributes) {
1637 element = $(element);
1638 if (Object.isElement(wrapper))
1639 $(wrapper).writeAttribute(attributes || { });
1640 else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
1641 else wrapper = new Element('div', wrapper);
1642 if (element.parentNode)
1643 element.parentNode.replaceChild(wrapper, element);
1644 wrapper.appendChild(element);
1645 return wrapper;
1646 },
1647
1190 inspect: function(element) {
1648 inspect: function(element) {
1191 element = $(element);
1649 element = $(element);
1192 var result = '<' + element.tagName.toLowerCase();
1650 var result = '<' + element.tagName.toLowerCase();
@@ -1212,7 +1670,13 Element.Methods = {
1212 },
1670 },
1213
1671
1214 descendants: function(element) {
1672 descendants: function(element) {
1215 return $A($(element).getElementsByTagName('*'));
1673 return $(element).getElementsBySelector("*");
1674 },
1675
1676 firstDescendant: function(element) {
1677 element = $(element).firstChild;
1678 while (element && element.nodeType != 1) element = element.nextSibling;
1679 return $(element);
1216 },
1680 },
1217
1681
1218 immediateDescendants: function(element) {
1682 immediateDescendants: function(element) {
@@ -1236,48 +1700,96 Element.Methods = {
1236 },
1700 },
1237
1701
1238 match: function(element, selector) {
1702 match: function(element, selector) {
1239 if (typeof selector == 'string')
1703 if (Object.isString(selector))
1240 selector = new Selector(selector);
1704 selector = new Selector(selector);
1241 return selector.match($(element));
1705 return selector.match($(element));
1242 },
1706 },
1243
1707
1244 up: function(element, expression, index) {
1708 up: function(element, expression, index) {
1245 return Selector.findElement($(element).ancestors(), expression, index);
1709 element = $(element);
1710 if (arguments.length == 1) return $(element.parentNode);
1711 var ancestors = element.ancestors();
1712 return expression ? Selector.findElement(ancestors, expression, index) :
1713 ancestors[index || 0];
1246 },
1714 },
1247
1715
1248 down: function(element, expression, index) {
1716 down: function(element, expression, index) {
1249 return Selector.findElement($(element).descendants(), expression, index);
1717 element = $(element);
1718 if (arguments.length == 1) return element.firstDescendant();
1719 var descendants = element.descendants();
1720 return expression ? Selector.findElement(descendants, expression, index) :
1721 descendants[index || 0];
1250 },
1722 },
1251
1723
1252 previous: function(element, expression, index) {
1724 previous: function(element, expression, index) {
1253 return Selector.findElement($(element).previousSiblings(), expression, index);
1725 element = $(element);
1726 if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
1727 var previousSiblings = element.previousSiblings();
1728 return expression ? Selector.findElement(previousSiblings, expression, index) :
1729 previousSiblings[index || 0];
1254 },
1730 },
1255
1731
1256 next: function(element, expression, index) {
1732 next: function(element, expression, index) {
1257 return Selector.findElement($(element).nextSiblings(), expression, index);
1733 element = $(element);
1734 if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
1735 var nextSiblings = element.nextSiblings();
1736 return expression ? Selector.findElement(nextSiblings, expression, index) :
1737 nextSiblings[index || 0];
1258 },
1738 },
1259
1739
1260 getElementsBySelector: function() {
1740 select: function() {
1261 var args = $A(arguments), element = $(args.shift());
1741 var args = $A(arguments), element = $(args.shift());
1262 return Selector.findChildElements(element, args);
1742 return Selector.findChildElements(element, args);
1263 },
1743 },
1264
1744
1265 getElementsByClassName: function(element, className) {
1745 adjacent: function() {
1266 return document.getElementsByClassName(className, element);
1746 var args = $A(arguments), element = $(args.shift());
1747 return Selector.findChildElements(element.parentNode, args).without(element);
1748 },
1749
1750 identify: function(element) {
1751 element = $(element);
1752 var id = element.readAttribute('id'), self = arguments.callee;
1753 if (id) return id;
1754 do { id = 'anonymous_element_' + self.counter++ } while ($(id));
1755 element.writeAttribute('id', id);
1756 return id;
1267 },
1757 },
1268
1758
1269 readAttribute: function(element, name) {
1759 readAttribute: function(element, name) {
1270 element = $(element);
1760 element = $(element);
1271 if (document.all && !window.opera) {
1761 if (Prototype.Browser.IE) {
1272 var t = Element._attributeTranslations;
1762 var t = Element._attributeTranslations.read;
1273 if (t.values[name]) return t.values[name](element, name);
1763 if (t.values[name]) return t.values[name](element, name);
1274 if (t.names[name]) name = t.names[name];
1764 if (t.names[name]) name = t.names[name];
1275 var attribute = element.attributes[name];
1765 if (name.include(':')) {
1276 if(attribute) return attribute.nodeValue;
1766 return (!element.attributes || !element.attributes[name]) ? null :
1767 element.attributes[name].value;
1768 }
1277 }
1769 }
1278 return element.getAttribute(name);
1770 return element.getAttribute(name);
1279 },
1771 },
1280
1772
1773 writeAttribute: function(element, name, value) {
1774 element = $(element);
1775 var attributes = { }, t = Element._attributeTranslations.write;
1776
1777 if (typeof name == 'object') attributes = name;
1778 else attributes[name] = Object.isUndefined(value) ? true : value;
1779
1780 for (var attr in attributes) {
1781 name = t.names[attr] || attr;
1782 value = attributes[attr];
1783 if (t.values[attr]) name = t.values[attr](element, value);
1784 if (value === false || value === null)
1785 element.removeAttribute(name);
1786 else if (value === true)
1787 element.setAttribute(name, name);
1788 else element.setAttribute(name, value);
1789 }
1790 return element;
1791 },
1792
1281 getHeight: function(element) {
1793 getHeight: function(element) {
1282 return $(element).getDimensions().height;
1794 return $(element).getDimensions().height;
1283 },
1795 },
@@ -1293,39 +1805,28 Element.Methods = {
1293 hasClassName: function(element, className) {
1805 hasClassName: function(element, className) {
1294 if (!(element = $(element))) return;
1806 if (!(element = $(element))) return;
1295 var elementClassName = element.className;
1807 var elementClassName = element.className;
1296 if (elementClassName.length == 0) return false;
1808 return (elementClassName.length > 0 && (elementClassName == className ||
1297 if (elementClassName == className ||
1809 new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
1298 elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
1299 return true;
1300 return false;
1301 },
1810 },
1302
1811
1303 addClassName: function(element, className) {
1812 addClassName: function(element, className) {
1304 if (!(element = $(element))) return;
1813 if (!(element = $(element))) return;
1305 Element.classNames(element).add(className);
1814 if (!element.hasClassName(className))
1815 element.className += (element.className ? ' ' : '') + className;
1306 return element;
1816 return element;
1307 },
1817 },
1308
1818
1309 removeClassName: function(element, className) {
1819 removeClassName: function(element, className) {
1310 if (!(element = $(element))) return;
1820 if (!(element = $(element))) return;
1311 Element.classNames(element).remove(className);
1821 element.className = element.className.replace(
1822 new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
1312 return element;
1823 return element;
1313 },
1824 },
1314
1825
1315 toggleClassName: function(element, className) {
1826 toggleClassName: function(element, className) {
1316 if (!(element = $(element))) return;
1827 if (!(element = $(element))) return;
1317 Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
1828 return element[element.hasClassName(className) ?
1318 return element;
1829 'removeClassName' : 'addClassName'](className);
1319 },
1320
1321 observe: function() {
1322 Event.observe.apply(Event, arguments);
1323 return $A(arguments).first();
1324 },
1325
1326 stopObserving: function() {
1327 Event.stopObserving.apply(Event, arguments);
1328 return $A(arguments).first();
1329 },
1830 },
1330
1831
1331 // removes whitespace-only text node children
1832 // removes whitespace-only text node children
@@ -1342,74 +1843,76 Element.Methods = {
1342 },
1843 },
1343
1844
1344 empty: function(element) {
1845 empty: function(element) {
1345 return $(element).innerHTML.match(/^\s*$/);
1846 return $(element).innerHTML.blank();
1346 },
1847 },
1347
1848
1348 descendantOf: function(element, ancestor) {
1849 descendantOf: function(element, ancestor) {
1349 element = $(element), ancestor = $(ancestor);
1850 element = $(element), ancestor = $(ancestor);
1851 var originalAncestor = ancestor;
1852
1853 if (element.compareDocumentPosition)
1854 return (element.compareDocumentPosition(ancestor) & 8) === 8;
1855
1856 if (element.sourceIndex && !Prototype.Browser.Opera) {
1857 var e = element.sourceIndex, a = ancestor.sourceIndex,
1858 nextAncestor = ancestor.nextSibling;
1859 if (!nextAncestor) {
1860 do { ancestor = ancestor.parentNode; }
1861 while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
1862 }
1863 if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
1864 }
1865
1350 while (element = element.parentNode)
1866 while (element = element.parentNode)
1351 if (element == ancestor) return true;
1867 if (element == originalAncestor) return true;
1352 return false;
1868 return false;
1353 },
1869 },
1354
1870
1355 scrollTo: function(element) {
1871 scrollTo: function(element) {
1356 element = $(element);
1872 element = $(element);
1357 var pos = Position.cumulativeOffset(element);
1873 var pos = element.cumulativeOffset();
1358 window.scrollTo(pos[0], pos[1]);
1874 window.scrollTo(pos[0], pos[1]);
1359 return element;
1875 return element;
1360 },
1876 },
1361
1877
1362 getStyle: function(element, style) {
1878 getStyle: function(element, style) {
1363 element = $(element);
1879 element = $(element);
1364 if (['float','cssFloat'].include(style))
1880 style = style == 'float' ? 'cssFloat' : style.camelize();
1365 style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
1366 style = style.camelize();
1367 var value = element.style[style];
1881 var value = element.style[style];
1368 if (!value) {
1882 if (!value) {
1369 if (document.defaultView && document.defaultView.getComputedStyle) {
1370 var css = document.defaultView.getComputedStyle(element, null);
1883 var css = document.defaultView.getComputedStyle(element, null);
1371 value = css ? css[style] : null;
1884 value = css ? css[style] : null;
1372 } else if (element.currentStyle) {
1373 value = element.currentStyle[style];
1374 }
1375 }
1885 }
1886 if (style == 'opacity') return value ? parseFloat(value) : 1.0;
1887 return value == 'auto' ? null : value;
1888 },
1376
1889
1377 if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
1890 getOpacity: function(element) {
1378 value = element['offset'+style.capitalize()] + 'px';
1891 return $(element).getStyle('opacity');
1892 },
1379
1893
1380 if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
1894 setStyle: function(element, styles) {
1381 if (Element.getStyle(element, 'position') == 'static') value = 'auto';
1895 element = $(element);
1382 if(style == 'opacity') {
1896 var elementStyle = element.style, match;
1383 if(value) return parseFloat(value);
1897 if (Object.isString(styles)) {
1384 if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
1898 element.style.cssText += ';' + styles;
1385 if(value[1]) return parseFloat(value[1]) / 100;
1899 return styles.include('opacity') ?
1386 return 1.0;
1900 element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
1387 }
1901 }
1388 return value == 'auto' ? null : value;
1902 for (var property in styles)
1903 if (property == 'opacity') element.setOpacity(styles[property]);
1904 else
1905 elementStyle[(property == 'float' || property == 'cssFloat') ?
1906 (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
1907 property] = styles[property];
1908
1909 return element;
1389 },
1910 },
1390
1911
1391 setStyle: function(element, style) {
1912 setOpacity: function(element, value) {
1392 element = $(element);
1913 element = $(element);
1393 for (var name in style) {
1914 element.style.opacity = (value == 1 || value === '') ? '' :
1394 var value = style[name];
1915 (value < 0.00001) ? 0 : value;
1395 if(name == 'opacity') {
1396 if (value == 1) {
1397 value = (/Gecko/.test(navigator.userAgent) &&
1398 !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
1399 if(/MSIE/.test(navigator.userAgent) && !window.opera)
1400 element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1401 } else if(value == '') {
1402 if(/MSIE/.test(navigator.userAgent) && !window.opera)
1403 element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1404 } else {
1405 if(value < 0.00001) value = 0;
1406 if(/MSIE/.test(navigator.userAgent) && !window.opera)
1407 element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
1408 'alpha(opacity='+value*100+')';
1409 }
1410 } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
1411 element.style[name.camelize()] = value;
1412 }
1413 return element;
1916 return element;
1414 },
1917 },
1415
1918
@@ -1468,8 +1971,8 Element.Methods = {
1468 makeClipping: function(element) {
1971 makeClipping: function(element) {
1469 element = $(element);
1972 element = $(element);
1470 if (element._overflow) return element;
1973 if (element._overflow) return element;
1471 element._overflow = element.style.overflow || 'auto';
1974 element._overflow = Element.getStyle(element, 'overflow') || 'auto';
1472 if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1975 if (element._overflow !== 'hidden')
1473 element.style.overflow = 'hidden';
1976 element.style.overflow = 'hidden';
1474 return element;
1977 return element;
1475 },
1978 },
@@ -1480,393 +1983,1398 Element.Methods = {
1480 element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
1983 element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
1481 element._overflow = null;
1984 element._overflow = null;
1482 return element;
1985 return element;
1483 }
1484 };
1485
1486 Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
1487
1488 Element._attributeTranslations = {};
1489
1490 Element._attributeTranslations.names = {
1491 colspan: "colSpan",
1492 rowspan: "rowSpan",
1493 valign: "vAlign",
1494 datetime: "dateTime",
1495 accesskey: "accessKey",
1496 tabindex: "tabIndex",
1497 enctype: "encType",
1498 maxlength: "maxLength",
1499 readonly: "readOnly",
1500 longdesc: "longDesc"
1501 };
1502
1503 Element._attributeTranslations.values = {
1504 _getAttr: function(element, attribute) {
1505 return element.getAttribute(attribute, 2);
1506 },
1986 },
1507
1987
1508 _flag: function(element, attribute) {
1988 cumulativeOffset: function(element) {
1509 return $(element).hasAttribute(attribute) ? attribute : null;
1989 var valueT = 0, valueL = 0;
1990 do {
1991 valueT += element.offsetTop || 0;
1992 valueL += element.offsetLeft || 0;
1993 element = element.offsetParent;
1994 } while (element);
1995 return Element._returnOffset(valueL, valueT);
1510 },
1996 },
1511
1997
1512 style: function(element) {
1998 positionedOffset: function(element) {
1513 return element.style.cssText.toLowerCase();
1999 var valueT = 0, valueL = 0;
2000 do {
2001 valueT += element.offsetTop || 0;
2002 valueL += element.offsetLeft || 0;
2003 element = element.offsetParent;
2004 if (element) {
2005 if (element.tagName == 'BODY') break;
2006 var p = Element.getStyle(element, 'position');
2007 if (p == 'relative' || p == 'absolute') break;
2008 }
2009 } while (element);
2010 return Element._returnOffset(valueL, valueT);
1514 },
2011 },
1515
2012
1516 title: function(element) {
2013 absolutize: function(element) {
1517 var node = element.getAttributeNode('title');
2014 element = $(element);
1518 return node.specified ? node.nodeValue : null;
2015 if (element.getStyle('position') == 'absolute') return;
1519 }
2016 // Position.prepare(); // To be done manually by Scripty when it needs it.
1520 };
1521
2017
1522 Object.extend(Element._attributeTranslations.values, {
2018 var offsets = element.positionedOffset();
1523 href: Element._attributeTranslations.values._getAttr,
2019 var top = offsets[1];
1524 src: Element._attributeTranslations.values._getAttr,
2020 var left = offsets[0];
1525 disabled: Element._attributeTranslations.values._flag,
2021 var width = element.clientWidth;
1526 checked: Element._attributeTranslations.values._flag,
2022 var height = element.clientHeight;
1527 readonly: Element._attributeTranslations.values._flag,
1528 multiple: Element._attributeTranslations.values._flag
1529 });
1530
2023
1531 Element.Methods.Simulated = {
2024 element._originalLeft = left - parseFloat(element.style.left || 0);
1532 hasAttribute: function(element, attribute) {
2025 element._originalTop = top - parseFloat(element.style.top || 0);
1533 var t = Element._attributeTranslations;
2026 element._originalWidth = element.style.width;
1534 attribute = t.names[attribute] || attribute;
2027 element._originalHeight = element.style.height;
1535 return $(element).getAttributeNode(attribute).specified;
2028
1536 }
2029 element.style.position = 'absolute';
1537 };
2030 element.style.top = top + 'px';
2031 element.style.left = left + 'px';
2032 element.style.width = width + 'px';
2033 element.style.height = height + 'px';
2034 return element;
2035 },
1538
2036
1539 // IE is missing .innerHTML support for TABLE-related elements
2037 relativize: function(element) {
1540 if (document.all && !window.opera){
1541 Element.Methods.update = function(element, html) {
1542 element = $(element);
2038 element = $(element);
1543 html = typeof html == 'undefined' ? '' : html.toString();
2039 if (element.getStyle('position') == 'relative') return;
1544 var tagName = element.tagName.toUpperCase();
2040 // Position.prepare(); // To be done manually by Scripty when it needs it.
1545 if (['THEAD','TBODY','TR','TD'].include(tagName)) {
1546 var div = document.createElement('div');
1547 switch (tagName) {
1548 case 'THEAD':
1549 case 'TBODY':
1550 div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>';
1551 depth = 2;
1552 break;
1553 case 'TR':
1554 div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>';
1555 depth = 3;
1556 break;
1557 case 'TD':
1558 div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>';
1559 depth = 4;
1560 }
1561 $A(element.childNodes).each(function(node){
1562 element.removeChild(node)
1563 });
1564 depth.times(function(){ div = div.firstChild });
1565
2041
1566 $A(div.childNodes).each(
2042 element.style.position = 'relative';
1567 function(node){ element.appendChild(node) });
2043 var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
1568 } else {
2044 var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
1569 element.innerHTML = html.stripScripts();
2045
1570 }
2046 element.style.top = top + 'px';
1571 setTimeout(function() {html.evalScripts()}, 10);
2047 element.style.left = left + 'px';
2048 element.style.height = element._originalHeight;
2049 element.style.width = element._originalWidth;
1572 return element;
2050 return element;
1573 }
2051 },
1574 };
1575
2052
1576 Object.extend(Element, Element.Methods);
2053 cumulativeScrollOffset: function(element) {
2054 var valueT = 0, valueL = 0;
2055 do {
2056 valueT += element.scrollTop || 0;
2057 valueL += element.scrollLeft || 0;
2058 element = element.parentNode;
2059 } while (element);
2060 return Element._returnOffset(valueL, valueT);
2061 },
1577
2062
1578 var _nativeExtensions = false;
2063 getOffsetParent: function(element) {
2064 if (element.offsetParent) return $(element.offsetParent);
2065 if (element == document.body) return $(element);
1579
2066
1580 if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
2067 while ((element = element.parentNode) && element != document.body)
1581 ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
2068 if (Element.getStyle(element, 'position') != 'static')
1582 var className = 'HTML' + tag + 'Element';
2069 return $(element);
1583 if(window[className]) return;
1584 var klass = window[className] = {};
1585 klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
1586 });
1587
2070
1588 Element.addMethods = function(methods) {
2071 return $(document.body);
1589 Object.extend(Element.Methods, methods || {});
2072 },
1590
2073
1591 function copy(methods, destination, onlyIfAbsent) {
2074 viewportOffset: function(forElement) {
1592 onlyIfAbsent = onlyIfAbsent || false;
2075 var valueT = 0, valueL = 0;
1593 var cache = Element.extend.cache;
1594 for (var property in methods) {
1595 var value = methods[property];
1596 if (!onlyIfAbsent || !(property in destination))
1597 destination[property] = cache.findOrStore(value);
1598 }
1599 }
1600
2076
1601 if (typeof HTMLElement != 'undefined') {
2077 var element = forElement;
1602 copy(Element.Methods, HTMLElement.prototype);
2078 do {
1603 copy(Element.Methods.Simulated, HTMLElement.prototype, true);
2079 valueT += element.offsetTop || 0;
1604 copy(Form.Methods, HTMLFormElement.prototype);
2080 valueL += element.offsetLeft || 0;
1605 [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
1606 copy(Form.Element.Methods, klass.prototype);
1607 });
1608 _nativeExtensions = true;
1609 }
1610 }
1611
2081
1612 var Toggle = new Object();
2082 // Safari fix
1613 Toggle.display = Element.toggle;
2083 if (element.offsetParent == document.body &&
2084 Element.getStyle(element, 'position') == 'absolute') break;
1614
2085
1615 /*--------------------------------------------------------------------------*/
2086 } while (element = element.offsetParent);
1616
2087
1617 Abstract.Insertion = function(adjacency) {
2088 element = forElement;
1618 this.adjacency = adjacency;
2089 do {
2090 if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
2091 valueT -= element.scrollTop || 0;
2092 valueL -= element.scrollLeft || 0;
1619 }
2093 }
2094 } while (element = element.parentNode);
1620
2095
1621 Abstract.Insertion.prototype = {
2096 return Element._returnOffset(valueL, valueT);
1622 initialize: function(element, content) {
2097 },
1623 this.element = $(element);
1624 this.content = content.stripScripts();
1625
2098
1626 if (this.adjacency && this.element.insertAdjacentHTML) {
2099 clonePosition: function(element, source) {
1627 try {
2100 var options = Object.extend({
1628 this.element.insertAdjacentHTML(this.adjacency, this.content);
2101 setLeft: true,
1629 } catch (e) {
2102 setTop: true,
1630 var tagName = this.element.tagName.toUpperCase();
2103 setWidth: true,
1631 if (['TBODY', 'TR'].include(tagName)) {
2104 setHeight: true,
1632 this.insertContent(this.contentFromAnonymousTable()._reverse());
2105 offsetTop: 0,
1633 } else {
2106 offsetLeft: 0
1634 throw e;
2107 }, arguments[2] || { });
2108
2109 // find page position of source
2110 source = $(source);
2111 var p = source.viewportOffset();
2112
2113 // find coordinate system to use
2114 element = $(element);
2115 var delta = [0, 0];
2116 var parent = null;
2117 // delta [0,0] will do fine with position: fixed elements,
2118 // position:absolute needs offsetParent deltas
2119 if (Element.getStyle(element, 'position') == 'absolute') {
2120 parent = element.getOffsetParent();
2121 delta = parent.viewportOffset();
1635 }
2122 }
2123
2124 // correct by body offsets (fixes Safari)
2125 if (parent == document.body) {
2126 delta[0] -= document.body.offsetLeft;
2127 delta[1] -= document.body.offsetTop;
1636 }
2128 }
1637 } else {
2129
1638 this.range = this.element.ownerDocument.createRange();
2130 // set position
1639 if (this.initializeRange) this.initializeRange();
2131 if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
1640 this.insertContent([this.range.createContextualFragment(this.content)]);
2132 if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
2133 if (options.setWidth) element.style.width = source.offsetWidth + 'px';
2134 if (options.setHeight) element.style.height = source.offsetHeight + 'px';
2135 return element;
1641 }
2136 }
2137 };
2138
2139 Element.Methods.identify.counter = 1;
2140
2141 Object.extend(Element.Methods, {
2142 getElementsBySelector: Element.Methods.select,
2143 childElements: Element.Methods.immediateDescendants
2144 });
1642
2145
1643 setTimeout(function() {content.evalScripts()}, 10);
2146 Element._attributeTranslations = {
2147 write: {
2148 names: {
2149 className: 'class',
2150 htmlFor: 'for'
1644 },
2151 },
2152 values: { }
2153 }
2154 };
2155
2156
2157 if (!document.createRange || Prototype.Browser.Opera) {
2158 Element.Methods.insert = function(element, insertions) {
2159 element = $(element);
1645
2160
1646 contentFromAnonymousTable: function() {
2161 if (Object.isString(insertions) || Object.isNumber(insertions) ||
1647 var div = document.createElement('div');
2162 Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
1648 div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
2163 insertions = { bottom: insertions };
1649 return $A(div.childNodes[0].childNodes[0].childNodes);
2164
2165 var t = Element._insertionTranslations, content, position, pos, tagName;
2166
2167 for (position in insertions) {
2168 content = insertions[position];
2169 position = position.toLowerCase();
2170 pos = t[position];
2171
2172 if (content && content.toElement) content = content.toElement();
2173 if (Object.isElement(content)) {
2174 pos.insert(element, content);
2175 continue;
1650 }
2176 }
2177
2178 content = Object.toHTML(content);
2179 tagName = ((position == 'before' || position == 'after')
2180 ? element.parentNode : element).tagName.toUpperCase();
2181
2182 if (t.tags[tagName]) {
2183 var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2184 if (position == 'top' || position == 'after') fragments.reverse();
2185 fragments.each(pos.insert.curry(element));
1651 }
2186 }
2187 else element.insertAdjacentHTML(pos.adjacency, content.stripScripts());
1652
2188
1653 var Insertion = new Object();
2189 content.evalScripts.bind(content).defer();
2190 }
1654
2191
1655 Insertion.Before = Class.create();
2192 return element;
1656 Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
2193 };
1657 initializeRange: function() {
2194 }
1658 this.range.setStartBefore(this.element);
1659 },
1660
2195
1661 insertContent: function(fragments) {
2196 if (Prototype.Browser.Opera) {
1662 fragments.each((function(fragment) {
2197 Element.Methods.getStyle = Element.Methods.getStyle.wrap(
1663 this.element.parentNode.insertBefore(fragment, this.element);
2198 function(proceed, element, style) {
1664 }).bind(this));
2199 switch (style) {
2200 case 'left': case 'top': case 'right': case 'bottom':
2201 if (proceed(element, 'position') === 'static') return null;
2202 case 'height': case 'width':
2203 // returns '0px' for hidden elements; we want it to return null
2204 if (!Element.visible(element)) return null;
2205
2206 // returns the border-box dimensions rather than the content-box
2207 // dimensions, so we subtract padding and borders from the value
2208 var dim = parseInt(proceed(element, style), 10);
2209
2210 if (dim !== element['offset' + style.capitalize()])
2211 return dim + 'px';
2212
2213 var properties;
2214 if (style === 'height') {
2215 properties = ['border-top-width', 'padding-top',
2216 'padding-bottom', 'border-bottom-width'];
2217 }
2218 else {
2219 properties = ['border-left-width', 'padding-left',
2220 'padding-right', 'border-right-width'];
2221 }
2222 return properties.inject(dim, function(memo, property) {
2223 var val = proceed(element, property);
2224 return val === null ? memo : memo - parseInt(val, 10);
2225 }) + 'px';
2226 default: return proceed(element, style);
2227 }
2228 }
2229 );
2230
2231 Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
2232 function(proceed, element, attribute) {
2233 if (attribute === 'title') return element.title;
2234 return proceed(element, attribute);
2235 }
2236 );
1665 }
2237 }
2238
2239 else if (Prototype.Browser.IE) {
2240 $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
2241 Element.Methods[method] = Element.Methods[method].wrap(
2242 function(proceed, element) {
2243 element = $(element);
2244 var position = element.getStyle('position');
2245 if (position != 'static') return proceed(element);
2246 element.setStyle({ position: 'relative' });
2247 var value = proceed(element);
2248 element.setStyle({ position: position });
2249 return value;
2250 }
2251 );
1666 });
2252 });
1667
2253
1668 Insertion.Top = Class.create();
2254 Element.Methods.getStyle = function(element, style) {
1669 Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
2255 element = $(element);
1670 initializeRange: function() {
2256 style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
1671 this.range.selectNodeContents(this.element);
2257 var value = element.style[style];
1672 this.range.collapse(true);
2258 if (!value && element.currentStyle) value = element.currentStyle[style];
2259
2260 if (style == 'opacity') {
2261 if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2262 if (value[1]) return parseFloat(value[1]) / 100;
2263 return 1.0;
2264 }
2265
2266 if (value == 'auto') {
2267 if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
2268 return element['offset' + style.capitalize()] + 'px';
2269 return null;
2270 }
2271 return value;
2272 };
2273
2274 Element.Methods.setOpacity = function(element, value) {
2275 function stripAlpha(filter){
2276 return filter.replace(/alpha\([^\)]*\)/gi,'');
2277 }
2278 element = $(element);
2279 var currentStyle = element.currentStyle;
2280 if ((currentStyle && !currentStyle.hasLayout) ||
2281 (!currentStyle && element.style.zoom == 'normal'))
2282 element.style.zoom = 1;
2283
2284 var filter = element.getStyle('filter'), style = element.style;
2285 if (value == 1 || value === '') {
2286 (filter = stripAlpha(filter)) ?
2287 style.filter = filter : style.removeAttribute('filter');
2288 return element;
2289 } else if (value < 0.00001) value = 0;
2290 style.filter = stripAlpha(filter) +
2291 'alpha(opacity=' + (value * 100) + ')';
2292 return element;
2293 };
2294
2295 Element._attributeTranslations = {
2296 read: {
2297 names: {
2298 'class': 'className',
2299 'for': 'htmlFor'
2300 },
2301 values: {
2302 _getAttr: function(element, attribute) {
2303 return element.getAttribute(attribute, 2);
2304 },
2305 _getAttrNode: function(element, attribute) {
2306 var node = element.getAttributeNode(attribute);
2307 return node ? node.value : "";
2308 },
2309 _getEv: function(element, attribute) {
2310 attribute = element.getAttribute(attribute);
2311 return attribute ? attribute.toString().slice(23, -2) : null;
2312 },
2313 _flag: function(element, attribute) {
2314 return $(element).hasAttribute(attribute) ? attribute : null;
2315 },
2316 style: function(element) {
2317 return element.style.cssText.toLowerCase();
2318 },
2319 title: function(element) {
2320 return element.title;
2321 }
2322 }
2323 }
2324 };
2325
2326 Element._attributeTranslations.write = {
2327 names: Object.clone(Element._attributeTranslations.read.names),
2328 values: {
2329 checked: function(element, value) {
2330 element.checked = !!value;
1673 },
2331 },
1674
2332
1675 insertContent: function(fragments) {
2333 style: function(element, value) {
1676 fragments.reverse(false).each((function(fragment) {
2334 element.style.cssText = value ? value : '';
1677 this.element.insertBefore(fragment, this.element.firstChild);
1678 }).bind(this));
1679 }
2335 }
2336 }
2337 };
2338
2339 Element._attributeTranslations.has = {};
2340
2341 $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2342 'encType maxLength readOnly longDesc').each(function(attr) {
2343 Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
2344 Element._attributeTranslations.has[attr.toLowerCase()] = attr;
2345 });
2346
2347 (function(v) {
2348 Object.extend(v, {
2349 href: v._getAttr,
2350 src: v._getAttr,
2351 type: v._getAttr,
2352 action: v._getAttrNode,
2353 disabled: v._flag,
2354 checked: v._flag,
2355 readonly: v._flag,
2356 multiple: v._flag,
2357 onload: v._getEv,
2358 onunload: v._getEv,
2359 onclick: v._getEv,
2360 ondblclick: v._getEv,
2361 onmousedown: v._getEv,
2362 onmouseup: v._getEv,
2363 onmouseover: v._getEv,
2364 onmousemove: v._getEv,
2365 onmouseout: v._getEv,
2366 onfocus: v._getEv,
2367 onblur: v._getEv,
2368 onkeypress: v._getEv,
2369 onkeydown: v._getEv,
2370 onkeyup: v._getEv,
2371 onsubmit: v._getEv,
2372 onreset: v._getEv,
2373 onselect: v._getEv,
2374 onchange: v._getEv
1680 });
2375 });
2376 })(Element._attributeTranslations.read.values);
2377 }
2378
2379 else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
2380 Element.Methods.setOpacity = function(element, value) {
2381 element = $(element);
2382 element.style.opacity = (value == 1) ? 0.999999 :
2383 (value === '') ? '' : (value < 0.00001) ? 0 : value;
2384 return element;
2385 };
2386 }
2387
2388 else if (Prototype.Browser.WebKit) {
2389 Element.Methods.setOpacity = function(element, value) {
2390 element = $(element);
2391 element.style.opacity = (value == 1 || value === '') ? '' :
2392 (value < 0.00001) ? 0 : value;
2393
2394 if (value == 1)
2395 if(element.tagName == 'IMG' && element.width) {
2396 element.width++; element.width--;
2397 } else try {
2398 var n = document.createTextNode(' ');
2399 element.appendChild(n);
2400 element.removeChild(n);
2401 } catch (e) { }
2402
2403 return element;
2404 };
2405
2406 // Safari returns margins on body which is incorrect if the child is absolutely
2407 // positioned. For performance reasons, redefine Element#cumulativeOffset for
2408 // KHTML/WebKit only.
2409 Element.Methods.cumulativeOffset = function(element) {
2410 var valueT = 0, valueL = 0;
2411 do {
2412 valueT += element.offsetTop || 0;
2413 valueL += element.offsetLeft || 0;
2414 if (element.offsetParent == document.body)
2415 if (Element.getStyle(element, 'position') == 'absolute') break;
2416
2417 element = element.offsetParent;
2418 } while (element);
2419
2420 return Element._returnOffset(valueL, valueT);
2421 };
2422 }
2423
2424 if (Prototype.Browser.IE || Prototype.Browser.Opera) {
2425 // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
2426 Element.Methods.update = function(element, content) {
2427 element = $(element);
2428
2429 if (content && content.toElement) content = content.toElement();
2430 if (Object.isElement(content)) return element.update().insert(content);
2431
2432 content = Object.toHTML(content);
2433 var tagName = element.tagName.toUpperCase();
2434
2435 if (tagName in Element._insertionTranslations.tags) {
2436 $A(element.childNodes).each(function(node) { element.removeChild(node) });
2437 Element._getContentFromAnonymousElement(tagName, content.stripScripts())
2438 .each(function(node) { element.appendChild(node) });
2439 }
2440 else element.innerHTML = content.stripScripts();
1681
2441
1682 Insertion.Bottom = Class.create();
2442 content.evalScripts.bind(content).defer();
1683 Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
2443 return element;
1684 initializeRange: function() {
2444 };
1685 this.range.selectNodeContents(this.element);
2445 }
1686 this.range.collapse(this.element);
2446
2447 if (document.createElement('div').outerHTML) {
2448 Element.Methods.replace = function(element, content) {
2449 element = $(element);
2450
2451 if (content && content.toElement) content = content.toElement();
2452 if (Object.isElement(content)) {
2453 element.parentNode.replaceChild(content, element);
2454 return element;
2455 }
2456
2457 content = Object.toHTML(content);
2458 var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2459
2460 if (Element._insertionTranslations.tags[tagName]) {
2461 var nextSibling = element.next();
2462 var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2463 parent.removeChild(element);
2464 if (nextSibling)
2465 fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
2466 else
2467 fragments.each(function(node) { parent.appendChild(node) });
2468 }
2469 else element.outerHTML = content.stripScripts();
2470
2471 content.evalScripts.bind(content).defer();
2472 return element;
2473 };
2474 }
2475
2476 Element._returnOffset = function(l, t) {
2477 var result = [l, t];
2478 result.left = l;
2479 result.top = t;
2480 return result;
2481 };
2482
2483 Element._getContentFromAnonymousElement = function(tagName, html) {
2484 var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
2485 div.innerHTML = t[0] + html + t[1];
2486 t[2].times(function() { div = div.firstChild });
2487 return $A(div.childNodes);
2488 };
2489
2490 Element._insertionTranslations = {
2491 before: {
2492 adjacency: 'beforeBegin',
2493 insert: function(element, node) {
2494 element.parentNode.insertBefore(node, element);
2495 },
2496 initializeRange: function(element, range) {
2497 range.setStartBefore(element);
2498 }
2499 },
2500 top: {
2501 adjacency: 'afterBegin',
2502 insert: function(element, node) {
2503 element.insertBefore(node, element.firstChild);
2504 },
2505 initializeRange: function(element, range) {
2506 range.selectNodeContents(element);
2507 range.collapse(true);
2508 }
2509 },
2510 bottom: {
2511 adjacency: 'beforeEnd',
2512 insert: function(element, node) {
2513 element.appendChild(node);
2514 }
2515 },
2516 after: {
2517 adjacency: 'afterEnd',
2518 insert: function(element, node) {
2519 element.parentNode.insertBefore(node, element.nextSibling);
2520 },
2521 initializeRange: function(element, range) {
2522 range.setStartAfter(element);
2523 }
1687 },
2524 },
2525 tags: {
2526 TABLE: ['<table>', '</table>', 1],
2527 TBODY: ['<table><tbody>', '</tbody></table>', 2],
2528 TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
2529 TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2530 SELECT: ['<select>', '</select>', 1]
2531 }
2532 };
1688
2533
1689 insertContent: function(fragments) {
2534 (function() {
1690 fragments.each((function(fragment) {
2535 this.bottom.initializeRange = this.top.initializeRange;
1691 this.element.appendChild(fragment);
2536 Object.extend(this.tags, {
1692 }).bind(this));
2537 THEAD: this.tags.TBODY,
2538 TFOOT: this.tags.TBODY,
2539 TH: this.tags.TD
2540 });
2541 }).call(Element._insertionTranslations);
2542
2543 Element.Methods.Simulated = {
2544 hasAttribute: function(element, attribute) {
2545 attribute = Element._attributeTranslations.has[attribute] || attribute;
2546 var node = $(element).getAttributeNode(attribute);
2547 return node && node.specified;
2548 }
2549 };
2550
2551 Element.Methods.ByTag = { };
2552
2553 Object.extend(Element, Element.Methods);
2554
2555 if (!Prototype.BrowserFeatures.ElementExtensions &&
2556 document.createElement('div').__proto__) {
2557 window.HTMLElement = { };
2558 window.HTMLElement.prototype = document.createElement('div').__proto__;
2559 Prototype.BrowserFeatures.ElementExtensions = true;
2560 }
2561
2562 Element.extend = (function() {
2563 if (Prototype.BrowserFeatures.SpecificElementExtensions)
2564 return Prototype.K;
2565
2566 var Methods = { }, ByTag = Element.Methods.ByTag;
2567
2568 var extend = Object.extend(function(element) {
2569 if (!element || element._extendedByPrototype ||
2570 element.nodeType != 1 || element == window) return element;
2571
2572 var methods = Object.clone(Methods),
2573 tagName = element.tagName, property, value;
2574
2575 // extend methods for specific tags
2576 if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
2577
2578 for (property in methods) {
2579 value = methods[property];
2580 if (Object.isFunction(value) && !(property in element))
2581 element[property] = value.methodize();
2582 }
2583
2584 element._extendedByPrototype = Prototype.emptyFunction;
2585 return element;
2586
2587 }, {
2588 refresh: function() {
2589 // extend methods for all tags (Safari doesn't need this)
2590 if (!Prototype.BrowserFeatures.ElementExtensions) {
2591 Object.extend(Methods, Element.Methods);
2592 Object.extend(Methods, Element.Methods.Simulated);
2593 }
1693 }
2594 }
1694 });
2595 });
1695
2596
1696 Insertion.After = Class.create();
2597 extend.refresh();
1697 Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
2598 return extend;
1698 initializeRange: function() {
2599 })();
1699 this.range.setStartAfter(this.element);
2600
1700 },
2601 Element.hasAttribute = function(element, attribute) {
2602 if (element.hasAttribute) return element.hasAttribute(attribute);
2603 return Element.Methods.Simulated.hasAttribute(element, attribute);
2604 };
2605
2606 Element.addMethods = function(methods) {
2607 var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
2608
2609 if (!methods) {
2610 Object.extend(Form, Form.Methods);
2611 Object.extend(Form.Element, Form.Element.Methods);
2612 Object.extend(Element.Methods.ByTag, {
2613 "FORM": Object.clone(Form.Methods),
2614 "INPUT": Object.clone(Form.Element.Methods),
2615 "SELECT": Object.clone(Form.Element.Methods),
2616 "TEXTAREA": Object.clone(Form.Element.Methods)
2617 });
2618 }
2619
2620 if (arguments.length == 2) {
2621 var tagName = methods;
2622 methods = arguments[1];
2623 }
1701
2624
1702 insertContent: function(fragments) {
2625 if (!tagName) Object.extend(Element.Methods, methods || { });
1703 fragments.each((function(fragment) {
2626 else {
1704 this.element.parentNode.insertBefore(fragment,
2627 if (Object.isArray(tagName)) tagName.each(extend);
1705 this.element.nextSibling);
2628 else extend(tagName);
1706 }).bind(this));
2629 }
2630
2631 function extend(tagName) {
2632 tagName = tagName.toUpperCase();
2633 if (!Element.Methods.ByTag[tagName])
2634 Element.Methods.ByTag[tagName] = { };
2635 Object.extend(Element.Methods.ByTag[tagName], methods);
2636 }
2637
2638 function copy(methods, destination, onlyIfAbsent) {
2639 onlyIfAbsent = onlyIfAbsent || false;
2640 for (var property in methods) {
2641 var value = methods[property];
2642 if (!Object.isFunction(value)) continue;
2643 if (!onlyIfAbsent || !(property in destination))
2644 destination[property] = value.methodize();
2645 }
2646 }
2647
2648 function findDOMClass(tagName) {
2649 var klass;
2650 var trans = {
2651 "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
2652 "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
2653 "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
2654 "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
2655 "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
2656 "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
2657 "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
2658 "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
2659 "FrameSet", "IFRAME": "IFrame"
2660 };
2661 if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
2662 if (window[klass]) return window[klass];
2663 klass = 'HTML' + tagName + 'Element';
2664 if (window[klass]) return window[klass];
2665 klass = 'HTML' + tagName.capitalize() + 'Element';
2666 if (window[klass]) return window[klass];
2667
2668 window[klass] = { };
2669 window[klass].prototype = document.createElement(tagName).__proto__;
2670 return window[klass];
1707 }
2671 }
2672
2673 if (F.ElementExtensions) {
2674 copy(Element.Methods, HTMLElement.prototype);
2675 copy(Element.Methods.Simulated, HTMLElement.prototype, true);
2676 }
2677
2678 if (F.SpecificElementExtensions) {
2679 for (var tag in Element.Methods.ByTag) {
2680 var klass = findDOMClass(tag);
2681 if (Object.isUndefined(klass)) continue;
2682 copy(T[tag], klass.prototype);
2683 }
2684 }
2685
2686 Object.extend(Element, Element.Methods);
2687 delete Element.ByTag;
2688
2689 if (Element.extend.refresh) Element.extend.refresh();
2690 Element.cache = { };
2691 };
2692
2693 document.viewport = {
2694 getDimensions: function() {
2695 var dimensions = { };
2696 var B = Prototype.Browser;
2697 $w('width height').each(function(d) {
2698 var D = d.capitalize();
2699 dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
2700 (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
1708 });
2701 });
2702 return dimensions;
2703 },
1709
2704
1710 /*--------------------------------------------------------------------------*/
2705 getWidth: function() {
2706 return this.getDimensions().width;
2707 },
1711
2708
1712 Element.ClassNames = Class.create();
2709 getHeight: function() {
1713 Element.ClassNames.prototype = {
2710 return this.getDimensions().height;
1714 initialize: function(element) {
1715 this.element = $(element);
1716 },
2711 },
1717
2712
1718 _each: function(iterator) {
2713 getScrollOffsets: function() {
1719 this.element.className.split(/\s+/).select(function(name) {
2714 return Element._returnOffset(
1720 return name.length > 0;
2715 window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
1721 })._each(iterator);
2716 window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
2717 }
2718 };
2719 /* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
2720 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
2721 * license. Please see http://www.yui-ext.com/ for more information. */
2722
2723 var Selector = Class.create({
2724 initialize: function(expression) {
2725 this.expression = expression.strip();
2726 this.compileMatcher();
1722 },
2727 },
1723
2728
1724 set: function(className) {
2729 shouldUseXPath: function() {
1725 this.element.className = className;
2730 if (!Prototype.BrowserFeatures.XPath) return false;
2731
2732 var e = this.expression;
2733
2734 // Safari 3 chokes on :*-of-type and :empty
2735 if (Prototype.Browser.WebKit &&
2736 (e.include("-of-type") || e.include(":empty")))
2737 return false;
2738
2739 // XPath can't do namespaced attributes, nor can it read
2740 // the "checked" property from DOM nodes
2741 if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
2742 return false;
2743
2744 return true;
1726 },
2745 },
1727
2746
1728 add: function(classNameToAdd) {
2747 compileMatcher: function() {
1729 if (this.include(classNameToAdd)) return;
2748 if (this.shouldUseXPath())
1730 this.set($A(this).concat(classNameToAdd).join(' '));
2749 return this.compileXPathMatcher();
2750
2751 var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
2752 c = Selector.criteria, le, p, m;
2753
2754 if (Selector._cache[e]) {
2755 this.matcher = Selector._cache[e];
2756 return;
2757 }
2758
2759 this.matcher = ["this.matcher = function(root) {",
2760 "var r = root, h = Selector.handlers, c = false, n;"];
2761
2762 while (e && le != e && (/\S/).test(e)) {
2763 le = e;
2764 for (var i in ps) {
2765 p = ps[i];
2766 if (m = e.match(p)) {
2767 this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
2768 new Template(c[i]).evaluate(m));
2769 e = e.replace(m[0], '');
2770 break;
2771 }
2772 }
2773 }
2774
2775 this.matcher.push("return h.unique(n);\n}");
2776 eval(this.matcher.join('\n'));
2777 Selector._cache[this.expression] = this.matcher;
1731 },
2778 },
1732
2779
1733 remove: function(classNameToRemove) {
2780 compileXPathMatcher: function() {
1734 if (!this.include(classNameToRemove)) return;
2781 var e = this.expression, ps = Selector.patterns,
1735 this.set($A(this).without(classNameToRemove).join(' '));
2782 x = Selector.xpath, le, m;
2783
2784 if (Selector._cache[e]) {
2785 this.xpath = Selector._cache[e]; return;
2786 }
2787
2788 this.matcher = ['.//*'];
2789 while (e && le != e && (/\S/).test(e)) {
2790 le = e;
2791 for (var i in ps) {
2792 if (m = e.match(ps[i])) {
2793 this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
2794 new Template(x[i]).evaluate(m));
2795 e = e.replace(m[0], '');
2796 break;
2797 }
2798 }
2799 }
2800
2801 this.xpath = this.matcher.join('');
2802 Selector._cache[this.expression] = this.xpath;
2803 },
2804
2805 findElements: function(root) {
2806 root = root || document;
2807 if (this.xpath) return document._getElementsByXPath(this.xpath, root);
2808 return this.matcher(root);
2809 },
2810
2811 match: function(element) {
2812 this.tokens = [];
2813
2814 var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
2815 var le, p, m;
2816
2817 while (e && le !== e && (/\S/).test(e)) {
2818 le = e;
2819 for (var i in ps) {
2820 p = ps[i];
2821 if (m = e.match(p)) {
2822 // use the Selector.assertions methods unless the selector
2823 // is too complex.
2824 if (as[i]) {
2825 this.tokens.push([i, Object.clone(m)]);
2826 e = e.replace(m[0], '');
2827 } else {
2828 // reluctantly do a document-wide search
2829 // and look for a match in the array
2830 return this.findElements(document).include(element);
2831 }
2832 }
2833 }
2834 }
2835
2836 var match = true, name, matches;
2837 for (var i = 0, token; token = this.tokens[i]; i++) {
2838 name = token[0], matches = token[1];
2839 if (!Selector.assertions[name](element, matches)) {
2840 match = false; break;
2841 }
2842 }
2843
2844 return match;
1736 },
2845 },
1737
2846
1738 toString: function() {
2847 toString: function() {
1739 return $A(this).join(' ');
2848 return this.expression;
2849 },
2850
2851 inspect: function() {
2852 return "#<Selector:" + this.expression.inspect() + ">";
1740 }
2853 }
1741 };
2854 });
1742
2855
1743 Object.extend(Element.ClassNames.prototype, Enumerable);
2856 Object.extend(Selector, {
1744 var Selector = Class.create();
2857 _cache: { },
1745 Selector.prototype = {
2858
1746 initialize: function(expression) {
2859 xpath: {
1747 this.params = {classNames: []};
2860 descendant: "//*",
1748 this.expression = expression.toString().strip();
2861 child: "/*",
1749 this.parseExpression();
2862 adjacent: "/following-sibling::*[1]",
1750 this.compileMatcher();
2863 laterSibling: '/following-sibling::*',
2864 tagName: function(m) {
2865 if (m[1] == '*') return '';
2866 return "[local-name()='" + m[1].toLowerCase() +
2867 "' or local-name()='" + m[1].toUpperCase() + "']";
2868 },
2869 className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
2870 id: "[@id='#{1}']",
2871 attrPresence: function(m) {
2872 m[1] = m[1].toLowerCase();
2873 return new Template("[@#{1}]").evaluate(m);
2874 },
2875 attr: function(m) {
2876 m[1] = m[1].toLowerCase();
2877 m[3] = m[5] || m[6];
2878 return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
2879 },
2880 pseudo: function(m) {
2881 var h = Selector.xpath.pseudos[m[1]];
2882 if (!h) return '';
2883 if (Object.isFunction(h)) return h(m);
2884 return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
2885 },
2886 operators: {
2887 '=': "[@#{1}='#{3}']",
2888 '!=': "[@#{1}!='#{3}']",
2889 '^=': "[starts-with(@#{1}, '#{3}')]",
2890 '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
2891 '*=': "[contains(@#{1}, '#{3}')]",
2892 '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
2893 '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
2894 },
2895 pseudos: {
2896 'first-child': '[not(preceding-sibling::*)]',
2897 'last-child': '[not(following-sibling::*)]',
2898 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
2899 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
2900 'checked': "[@checked]",
2901 'disabled': "[@disabled]",
2902 'enabled': "[not(@disabled)]",
2903 'not': function(m) {
2904 var e = m[6], p = Selector.patterns,
2905 x = Selector.xpath, le, v;
2906
2907 var exclusion = [];
2908 while (e && le != e && (/\S/).test(e)) {
2909 le = e;
2910 for (var i in p) {
2911 if (m = e.match(p[i])) {
2912 v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
2913 exclusion.push("(" + v.substring(1, v.length - 1) + ")");
2914 e = e.replace(m[0], '');
2915 break;
2916 }
2917 }
2918 }
2919 return "[not(" + exclusion.join(" and ") + ")]";
2920 },
2921 'nth-child': function(m) {
2922 return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
2923 },
2924 'nth-last-child': function(m) {
2925 return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
2926 },
2927 'nth-of-type': function(m) {
2928 return Selector.xpath.pseudos.nth("position() ", m);
2929 },
2930 'nth-last-of-type': function(m) {
2931 return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
2932 },
2933 'first-of-type': function(m) {
2934 m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
2935 },
2936 'last-of-type': function(m) {
2937 m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
2938 },
2939 'only-of-type': function(m) {
2940 var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
2941 },
2942 nth: function(fragment, m) {
2943 var mm, formula = m[6], predicate;
2944 if (formula == 'even') formula = '2n+0';
2945 if (formula == 'odd') formula = '2n+1';
2946 if (mm = formula.match(/^(\d+)$/)) // digit only
2947 return '[' + fragment + "= " + mm[1] + ']';
2948 if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2949 if (mm[1] == "-") mm[1] = -1;
2950 var a = mm[1] ? Number(mm[1]) : 1;
2951 var b = mm[2] ? Number(mm[2]) : 0;
2952 predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
2953 "((#{fragment} - #{b}) div #{a} >= 0)]";
2954 return new Template(predicate).evaluate({
2955 fragment: fragment, a: a, b: b });
2956 }
2957 }
2958 }
2959 },
2960
2961 criteria: {
2962 tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
2963 className: 'n = h.className(n, r, "#{1}", c); c = false;',
2964 id: 'n = h.id(n, r, "#{1}", c); c = false;',
2965 attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
2966 attr: function(m) {
2967 m[3] = (m[5] || m[6]);
2968 return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
2969 },
2970 pseudo: function(m) {
2971 if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
2972 return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
2973 },
2974 descendant: 'c = "descendant";',
2975 child: 'c = "child";',
2976 adjacent: 'c = "adjacent";',
2977 laterSibling: 'c = "laterSibling";'
2978 },
2979
2980 patterns: {
2981 // combinators must be listed first
2982 // (and descendant needs to be last combinator)
2983 laterSibling: /^\s*~\s*/,
2984 child: /^\s*>\s*/,
2985 adjacent: /^\s*\+\s*/,
2986 descendant: /^\s/,
2987
2988 // selectors follow
2989 tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
2990 id: /^#([\w\-\*]+)(\b|$)/,
2991 className: /^\.([\w\-\*]+)(\b|$)/,
2992 pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
2993 attrPresence: /^\[([\w]+)\]/,
2994 attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
2995 },
2996
2997 // for Selector.match and Element#match
2998 assertions: {
2999 tagName: function(element, matches) {
3000 return matches[1].toUpperCase() == element.tagName.toUpperCase();
3001 },
3002
3003 className: function(element, matches) {
3004 return Element.hasClassName(element, matches[1]);
3005 },
3006
3007 id: function(element, matches) {
3008 return element.id === matches[1];
3009 },
3010
3011 attrPresence: function(element, matches) {
3012 return Element.hasAttribute(element, matches[1]);
3013 },
3014
3015 attr: function(element, matches) {
3016 var nodeValue = Element.readAttribute(element, matches[1]);
3017 return Selector.operators[matches[2]](nodeValue, matches[3]);
3018 }
3019 },
3020
3021 handlers: {
3022 // UTILITY FUNCTIONS
3023 // joins two collections
3024 concat: function(a, b) {
3025 for (var i = 0, node; node = b[i]; i++)
3026 a.push(node);
3027 return a;
3028 },
3029
3030 // marks an array of nodes for counting
3031 mark: function(nodes) {
3032 for (var i = 0, node; node = nodes[i]; i++)
3033 node._counted = true;
3034 return nodes;
3035 },
3036
3037 unmark: function(nodes) {
3038 for (var i = 0, node; node = nodes[i]; i++)
3039 node._counted = undefined;
3040 return nodes;
3041 },
3042
3043 // mark each child node with its position (for nth calls)
3044 // "ofType" flag indicates whether we're indexing for nth-of-type
3045 // rather than nth-child
3046 index: function(parentNode, reverse, ofType) {
3047 parentNode._counted = true;
3048 if (reverse) {
3049 for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
3050 var node = nodes[i];
3051 if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
3052 }
3053 } else {
3054 for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
3055 if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
3056 }
3057 },
3058
3059 // filters out duplicates and extends all nodes
3060 unique: function(nodes) {
3061 if (nodes.length == 0) return nodes;
3062 var results = [], n;
3063 for (var i = 0, l = nodes.length; i < l; i++)
3064 if (!(n = nodes[i])._counted) {
3065 n._counted = true;
3066 results.push(Element.extend(n));
3067 }
3068 return Selector.handlers.unmark(results);
3069 },
3070
3071 // COMBINATOR FUNCTIONS
3072 descendant: function(nodes) {
3073 var h = Selector.handlers;
3074 for (var i = 0, results = [], node; node = nodes[i]; i++)
3075 h.concat(results, node.getElementsByTagName('*'));
3076 return results;
3077 },
3078
3079 child: function(nodes) {
3080 var h = Selector.handlers;
3081 for (var i = 0, results = [], node; node = nodes[i]; i++) {
3082 for (var j = 0, child; child = node.childNodes[j]; j++)
3083 if (child.nodeType == 1 && child.tagName != '!') results.push(child);
3084 }
3085 return results;
3086 },
3087
3088 adjacent: function(nodes) {
3089 for (var i = 0, results = [], node; node = nodes[i]; i++) {
3090 var next = this.nextElementSibling(node);
3091 if (next) results.push(next);
3092 }
3093 return results;
3094 },
3095
3096 laterSibling: function(nodes) {
3097 var h = Selector.handlers;
3098 for (var i = 0, results = [], node; node = nodes[i]; i++)
3099 h.concat(results, Element.nextSiblings(node));
3100 return results;
1751 },
3101 },
1752
3102
1753 parseExpression: function() {
3103 nextElementSibling: function(node) {
1754 function abort(message) { throw 'Parse error in selector: ' + message; }
3104 while (node = node.nextSibling)
3105 if (node.nodeType == 1) return node;
3106 return null;
3107 },
1755
3108
1756 if (this.expression == '') abort('empty expression');
3109 previousElementSibling: function(node) {
3110 while (node = node.previousSibling)
3111 if (node.nodeType == 1) return node;
3112 return null;
3113 },
1757
3114
1758 var params = this.params, expr = this.expression, match, modifier, clause, rest;
3115 // TOKEN FUNCTIONS
1759 while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
3116 tagName: function(nodes, root, tagName, combinator) {
1760 params.attributes = params.attributes || [];
3117 tagName = tagName.toUpperCase();
1761 params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
3118 var results = [], h = Selector.handlers;
1762 expr = match[1];
3119 if (nodes) {
3120 if (combinator) {
3121 // fastlane for ordinary descendant combinators
3122 if (combinator == "descendant") {
3123 for (var i = 0, node; node = nodes[i]; i++)
3124 h.concat(results, node.getElementsByTagName(tagName));
3125 return results;
3126 } else nodes = this[combinator](nodes);
3127 if (tagName == "*") return nodes;
3128 }
3129 for (var i = 0, node; node = nodes[i]; i++)
3130 if (node.tagName.toUpperCase() == tagName) results.push(node);
3131 return results;
3132 } else return root.getElementsByTagName(tagName);
3133 },
3134
3135 id: function(nodes, root, id, combinator) {
3136 var targetNode = $(id), h = Selector.handlers;
3137 if (!targetNode) return [];
3138 if (!nodes && root == document) return [targetNode];
3139 if (nodes) {
3140 if (combinator) {
3141 if (combinator == 'child') {
3142 for (var i = 0, node; node = nodes[i]; i++)
3143 if (targetNode.parentNode == node) return [targetNode];
3144 } else if (combinator == 'descendant') {
3145 for (var i = 0, node; node = nodes[i]; i++)
3146 if (Element.descendantOf(targetNode, node)) return [targetNode];
3147 } else if (combinator == 'adjacent') {
3148 for (var i = 0, node; node = nodes[i]; i++)
3149 if (Selector.handlers.previousElementSibling(targetNode) == node)
3150 return [targetNode];
3151 } else nodes = h[combinator](nodes);
3152 }
3153 for (var i = 0, node; node = nodes[i]; i++)
3154 if (node == targetNode) return [targetNode];
3155 return [];
1763 }
3156 }
3157 return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
3158 },
1764
3159
1765 if (expr == '*') return this.params.wildcard = true;
3160 className: function(nodes, root, className, combinator) {
3161 if (nodes && combinator) nodes = this[combinator](nodes);
3162 return Selector.handlers.byClassName(nodes, root, className);
3163 },
1766
3164
1767 while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
3165 byClassName: function(nodes, root, className) {
1768 modifier = match[1], clause = match[2], rest = match[3];
3166 if (!nodes) nodes = Selector.handlers.descendant([root]);
1769 switch (modifier) {
3167 var needle = ' ' + className + ' ';
1770 case '#': params.id = clause; break;
3168 for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
1771 case '.': params.classNames.push(clause); break;
3169 nodeClassName = node.className;
1772 case '':
3170 if (nodeClassName.length == 0) continue;
1773 case undefined: params.tagName = clause.toUpperCase(); break;
3171 if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
1774 default: abort(expr.inspect());
3172 results.push(node);
1775 }
1776 expr = rest;
1777 }
3173 }
3174 return results;
3175 },
1778
3176
1779 if (expr.length > 0) abort(expr.inspect());
3177 attrPresence: function(nodes, root, attr) {
3178 if (!nodes) nodes = root.getElementsByTagName("*");
3179 var results = [];
3180 for (var i = 0, node; node = nodes[i]; i++)
3181 if (Element.hasAttribute(node, attr)) results.push(node);
3182 return results;
1780 },
3183 },
1781
3184
1782 buildMatchExpression: function() {
3185 attr: function(nodes, root, attr, value, operator) {
1783 var params = this.params, conditions = [], clause;
3186 if (!nodes) nodes = root.getElementsByTagName("*");
3187 var handler = Selector.operators[operator], results = [];
3188 for (var i = 0, node; node = nodes[i]; i++) {
3189 var nodeValue = Element.readAttribute(node, attr);
3190 if (nodeValue === null) continue;
3191 if (handler(nodeValue, value)) results.push(node);
3192 }
3193 return results;
3194 },
1784
3195
1785 if (params.wildcard)
3196 pseudo: function(nodes, name, value, root, combinator) {
1786 conditions.push('true');
3197 if (nodes && combinator) nodes = this[combinator](nodes);
1787 if (clause = params.id)
3198 if (!nodes) nodes = root.getElementsByTagName("*");
1788 conditions.push('element.readAttribute("id") == ' + clause.inspect());
3199 return Selector.pseudos[name](nodes, value, root);
1789 if (clause = params.tagName)
1790 conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
1791 if ((clause = params.classNames).length > 0)
1792 for (var i = 0, length = clause.length; i < length; i++)
1793 conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
1794 if (clause = params.attributes) {
1795 clause.each(function(attribute) {
1796 var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
1797 var splitValueBy = function(delimiter) {
1798 return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
1799 }
3200 }
3201 },
1800
3202
1801 switch (attribute.operator) {
3203 pseudos: {
1802 case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break;
3204 'first-child': function(nodes, value, root) {
1803 case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
3205 for (var i = 0, results = [], node; node = nodes[i]; i++) {
1804 case '|=': conditions.push(
3206 if (Selector.handlers.previousElementSibling(node)) continue;
1805 splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
3207 results.push(node);
1806 ); break;
1807 case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
1808 case '':
1809 case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
1810 default: throw 'Unknown operator ' + attribute.operator + ' in selector';
1811 }
3208 }
1812 });
3209 return results;
3210 },
3211 'last-child': function(nodes, value, root) {
3212 for (var i = 0, results = [], node; node = nodes[i]; i++) {
3213 if (Selector.handlers.nextElementSibling(node)) continue;
3214 results.push(node);
1813 }
3215 }
1814
3216 return results;
1815 return conditions.join(' && ');
3217 },
3218 'only-child': function(nodes, value, root) {
3219 var h = Selector.handlers;
3220 for (var i = 0, results = [], node; node = nodes[i]; i++)
3221 if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
3222 results.push(node);
3223 return results;
3224 },
3225 'nth-child': function(nodes, formula, root) {
3226 return Selector.pseudos.nth(nodes, formula, root);
3227 },
3228 'nth-last-child': function(nodes, formula, root) {
3229 return Selector.pseudos.nth(nodes, formula, root, true);
3230 },
3231 'nth-of-type': function(nodes, formula, root) {
3232 return Selector.pseudos.nth(nodes, formula, root, false, true);
3233 },
3234 'nth-last-of-type': function(nodes, formula, root) {
3235 return Selector.pseudos.nth(nodes, formula, root, true, true);
3236 },
3237 'first-of-type': function(nodes, formula, root) {
3238 return Selector.pseudos.nth(nodes, "1", root, false, true);
3239 },
3240 'last-of-type': function(nodes, formula, root) {
3241 return Selector.pseudos.nth(nodes, "1", root, true, true);
3242 },
3243 'only-of-type': function(nodes, formula, root) {
3244 var p = Selector.pseudos;
3245 return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
1816 },
3246 },
1817
3247
1818 compileMatcher: function() {
3248 // handles the an+b logic
1819 this.match = new Function('element', 'if (!element.tagName) return false; \
3249 getIndices: function(a, b, total) {
1820 element = $(element); \
3250 if (a == 0) return b > 0 ? [b] : [];
1821 return ' + this.buildMatchExpression());
3251 return $R(1, total).inject([], function(memo, i) {
3252 if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
3253 return memo;
3254 });
1822 },
3255 },
1823
3256
1824 findElements: function(scope) {
3257 // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
1825 var element;
3258 nth: function(nodes, formula, root, reverse, ofType) {
3259 if (nodes.length == 0) return [];
3260 if (formula == 'even') formula = '2n+0';
3261 if (formula == 'odd') formula = '2n+1';
3262 var h = Selector.handlers, results = [], indexed = [], m;
3263 h.mark(nodes);
3264 for (var i = 0, node; node = nodes[i]; i++) {
3265 if (!node.parentNode._counted) {
3266 h.index(node.parentNode, reverse, ofType);
3267 indexed.push(node.parentNode);
3268 }
3269 }
3270 if (formula.match(/^\d+$/)) { // just a number
3271 formula = Number(formula);
3272 for (var i = 0, node; node = nodes[i]; i++)
3273 if (node.nodeIndex == formula) results.push(node);
3274 } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
3275 if (m[1] == "-") m[1] = -1;
3276 var a = m[1] ? Number(m[1]) : 1;
3277 var b = m[2] ? Number(m[2]) : 0;
3278 var indices = Selector.pseudos.getIndices(a, b, nodes.length);
3279 for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
3280 for (var j = 0; j < l; j++)
3281 if (node.nodeIndex == indices[j]) results.push(node);
3282 }
3283 }
3284 h.unmark(nodes);
3285 h.unmark(indexed);
3286 return results;
3287 },
1826
3288
1827 if (element = $(this.params.id))
3289 'empty': function(nodes, value, root) {
1828 if (this.match(element))
3290 for (var i = 0, results = [], node; node = nodes[i]; i++) {
1829 if (!scope || Element.childOf(element, scope))
3291 // IE treats comments as element nodes
1830 return [element];
3292 if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
3293 results.push(node);
3294 }
3295 return results;
3296 },
1831
3297
1832 scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
3298 'not': function(nodes, selector, root) {
3299 var h = Selector.handlers, selectorType, m;
3300 var exclusions = new Selector(selector).findElements(root);
3301 h.mark(exclusions);
3302 for (var i = 0, results = [], node; node = nodes[i]; i++)
3303 if (!node._counted) results.push(node);
3304 h.unmark(exclusions);
3305 return results;
3306 },
1833
3307
1834 var results = [];
3308 'enabled': function(nodes, value, root) {
1835 for (var i = 0, length = scope.length; i < length; i++)
3309 for (var i = 0, results = [], node; node = nodes[i]; i++)
1836 if (this.match(element = scope[i]))
3310 if (!node.disabled) results.push(node);
1837 results.push(Element.extend(element));
3311 return results;
3312 },
1838
3313
3314 'disabled': function(nodes, value, root) {
3315 for (var i = 0, results = [], node; node = nodes[i]; i++)
3316 if (node.disabled) results.push(node);
1839 return results;
3317 return results;
1840 },
3318 },
1841
3319
1842 toString: function() {
3320 'checked': function(nodes, value, root) {
1843 return this.expression;
3321 for (var i = 0, results = [], node; node = nodes[i]; i++)
1844 }
3322 if (node.checked) results.push(node);
3323 return results;
1845 }
3324 }
3325 },
3326
3327 operators: {
3328 '=': function(nv, v) { return nv == v; },
3329 '!=': function(nv, v) { return nv != v; },
3330 '^=': function(nv, v) { return nv.startsWith(v); },
3331 '$=': function(nv, v) { return nv.endsWith(v); },
3332 '*=': function(nv, v) { return nv.include(v); },
3333 '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
3334 '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
3335 },
1846
3336
1847 Object.extend(Selector, {
1848 matchElements: function(elements, expression) {
3337 matchElements: function(elements, expression) {
1849 var selector = new Selector(expression);
3338 var matches = new Selector(expression).findElements(), h = Selector.handlers;
1850 return elements.select(selector.match.bind(selector)).map(Element.extend);
3339 h.mark(matches);
3340 for (var i = 0, results = [], element; element = elements[i]; i++)
3341 if (element._counted) results.push(element);
3342 h.unmark(matches);
3343 return results;
1851 },
3344 },
1852
3345
1853 findElement: function(elements, expression, index) {
3346 findElement: function(elements, expression, index) {
1854 if (typeof expression == 'number') index = expression, expression = false;
3347 if (Object.isNumber(expression)) {
3348 index = expression; expression = false;
3349 }
1855 return Selector.matchElements(elements, expression || '*')[index || 0];
3350 return Selector.matchElements(elements, expression || '*')[index || 0];
1856 },
3351 },
1857
3352
1858 findChildElements: function(element, expressions) {
3353 findChildElements: function(element, expressions) {
1859 return expressions.map(function(expression) {
3354 var exprs = expressions.join(',');
1860 return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
3355 expressions = [];
1861 var selector = new Selector(expr);
3356 exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
1862 return results.inject([], function(elements, result) {
3357 expressions.push(m[1].strip());
1863 return elements.concat(selector.findElements(result || element));
1864 });
1865 });
3358 });
1866 }).flatten();
3359 var results = [], h = Selector.handlers;
3360 for (var i = 0, l = expressions.length, selector; i < l; i++) {
3361 selector = new Selector(expressions[i].strip());
3362 h.concat(results, selector.findElements(element));
3363 }
3364 return (l > 1) ? h.unique(results) : results;
1867 }
3365 }
1868 });
3366 });
1869
3367
3368 if (Prototype.Browser.IE) {
3369 // IE returns comment nodes on getElementsByTagName("*").
3370 // Filter them out.
3371 Selector.handlers.concat = function(a, b) {
3372 for (var i = 0, node; node = b[i]; i++)
3373 if (node.tagName !== "!") a.push(node);
3374 return a;
3375 };
3376 }
3377
1870 function $$() {
3378 function $$() {
1871 return Selector.findChildElements(document, $A(arguments));
3379 return Selector.findChildElements(document, $A(arguments));
1872 }
3380 }
@@ -1876,13 +3384,19 var Form = {
1876 return form;
3384 return form;
1877 },
3385 },
1878
3386
1879 serializeElements: function(elements, getHash) {
3387 serializeElements: function(elements, options) {
3388 if (typeof options != 'object') options = { hash: !!options };
3389 else if (Object.isUndefined(options.hash)) options.hash = true;
3390 var key, value, submitted = false, submit = options.submit;
3391
1880 var data = elements.inject({}, function(result, element) {
3392 var data = elements.inject({ }, function(result, element) {
1881 if (!element.disabled && element.name) {
3393 if (!element.disabled && element.name) {
1882 var key = element.name, value = $(element).getValue();
3394 key = element.name; value = $(element).getValue();
1883 if (value != undefined) {
3395 if (value != null && (element.type != 'submit' || (!submitted &&
1884 if (result[key]) {
3396 submit !== false && (!submit || key == submit) && (submitted = true)))) {
1885 if (result[key].constructor != Array) result[key] = [result[key]];
3397 if (key in result) {
3398 // a key is already present; construct an array of values
3399 if (!Object.isArray(result[key])) result[key] = [result[key]];
1886 result[key].push(value);
3400 result[key].push(value);
1887 }
3401 }
1888 else result[key] = value;
3402 else result[key] = value;
@@ -1891,13 +3405,13 var Form = {
1891 return result;
3405 return result;
1892 });
3406 });
1893
3407
1894 return getHash ? data : Hash.toQueryString(data);
3408 return options.hash ? data : Object.toQueryString(data);
1895 }
3409 }
1896 };
3410 };
1897
3411
1898 Form.Methods = {
3412 Form.Methods = {
1899 serialize: function(form, getHash) {
3413 serialize: function(form, options) {
1900 return Form.serializeElements(Form.getElements(form), getHash);
3414 return Form.serializeElements(Form.getElements(form), options);
1901 },
3415 },
1902
3416
1903 getElements: function(form) {
3417 getElements: function(form) {
@@ -1928,25 +3442,26 Form.Methods = {
1928
3442
1929 disable: function(form) {
3443 disable: function(form) {
1930 form = $(form);
3444 form = $(form);
1931 form.getElements().each(function(element) {
3445 Form.getElements(form).invoke('disable');
1932 element.blur();
1933 element.disabled = 'true';
1934 });
1935 return form;
3446 return form;
1936 },
3447 },
1937
3448
1938 enable: function(form) {
3449 enable: function(form) {
1939 form = $(form);
3450 form = $(form);
1940 form.getElements().each(function(element) {
3451 Form.getElements(form).invoke('enable');
1941 element.disabled = '';
1942 });
1943 return form;
3452 return form;
1944 },
3453 },
1945
3454
1946 findFirstElement: function(form) {
3455 findFirstElement: function(form) {
1947 return $(form).getElements().find(function(element) {
3456 var elements = $(form).getElements().findAll(function(element) {
1948 return element.type != 'hidden' && !element.disabled &&
3457 return 'hidden' != element.type && !element.disabled;
1949 ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
3458 });
3459 var firstByIndex = elements.findAll(function(element) {
3460 return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
3461 }).sortBy(function(element) { return element.tabIndex }).first();
3462
3463 return firstByIndex ? firstByIndex : elements.find(function(element) {
3464 return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
1950 });
3465 });
1951 },
3466 },
1952
3467
@@ -1954,10 +3469,26 Form.Methods = {
1954 form = $(form);
3469 form = $(form);
1955 form.findFirstElement().activate();
3470 form.findFirstElement().activate();
1956 return form;
3471 return form;
1957 }
3472 },
3473
3474 request: function(form, options) {
3475 form = $(form), options = Object.clone(options || { });
3476
3477 var params = options.parameters, action = form.readAttribute('action') || '';
3478 if (action.blank()) action = window.location.href;
3479 options.parameters = form.serialize(true);
3480
3481 if (params) {
3482 if (Object.isString(params)) params = params.toQueryParams();
3483 Object.extend(options.parameters, params);
1958 }
3484 }
1959
3485
1960 Object.extend(Form, Form.Methods);
3486 if (form.hasAttribute('method') && !options.method)
3487 options.method = form.method;
3488
3489 return new Ajax.Request(action, options);
3490 }
3491 };
1961
3492
1962 /*--------------------------------------------------------------------------*/
3493 /*--------------------------------------------------------------------------*/
1963
3494
@@ -1971,7 +3502,7 Form.Element = {
1971 $(element).select();
3502 $(element).select();
1972 return element;
3503 return element;
1973 }
3504 }
1974 }
3505 };
1975
3506
1976 Form.Element.Methods = {
3507 Form.Element.Methods = {
1977 serialize: function(element) {
3508 serialize: function(element) {
@@ -1981,7 +3512,7 Form.Element.Methods = {
1981 if (value != undefined) {
3512 if (value != undefined) {
1982 var pair = {};
3513 var pair = { };
1983 pair[element.name] = value;
3514 pair[element.name] = value;
1984 return Hash.toQueryString(pair);
3515 return Object.toQueryString(pair);
1985 }
3516 }
1986 }
3517 }
1987 return '';
3518 return '';
@@ -1993,6 +3524,13 Form.Element.Methods = {
1993 return Form.Element.Serializers[method](element);
3524 return Form.Element.Serializers[method](element);
1994 },
3525 },
1995
3526
3527 setValue: function(element, value) {
3528 element = $(element);
3529 var method = element.tagName.toLowerCase();
3530 Form.Element.Serializers[method](element, value);
3531 return element;
3532 },
3533
1996 clear: function(element) {
3534 clear: function(element) {
1997 $(element).value = '';
3535 $(element).value = '';
1998 return element;
3536 return element;
@@ -2004,55 +3542,75 Form.Element.Methods = {
2004
3542
2005 activate: function(element) {
3543 activate: function(element) {
2006 element = $(element);
3544 element = $(element);
3545 try {
2007 element.focus();
3546 element.focus();
2008 if (element.select && ( element.tagName.toLowerCase() != 'input' ||
3547 if (element.select && (element.tagName.toLowerCase() != 'input' ||
2009 !['button', 'reset', 'submit'].include(element.type) ) )
3548 !['button', 'reset', 'submit'].include(element.type)))
2010 element.select();
3549 element.select();
3550 } catch (e) { }
2011 return element;
3551 return element;
2012 },
3552 },
2013
3553
2014 disable: function(element) {
3554 disable: function(element) {
2015 element = $(element);
3555 element = $(element);
3556 element.blur();
2016 element.disabled = true;
3557 element.disabled = true;
2017 return element;
3558 return element;
2018 },
3559 },
2019
3560
2020 enable: function(element) {
3561 enable: function(element) {
2021 element = $(element);
3562 element = $(element);
2022 element.blur();
2023 element.disabled = false;
3563 element.disabled = false;
2024 return element;
3564 return element;
2025 }
3565 }
2026 }
3566 };
3567
3568 /*--------------------------------------------------------------------------*/
2027
3569
2028 Object.extend(Form.Element, Form.Element.Methods);
2029 var Field = Form.Element;
3570 var Field = Form.Element;
2030 var $F = Form.Element.getValue;
3571 var $F = Form.Element.Methods.getValue;
2031
3572
2032 /*--------------------------------------------------------------------------*/
3573 /*--------------------------------------------------------------------------*/
2033
3574
2034 Form.Element.Serializers = {
3575 Form.Element.Serializers = {
2035 input: function(element) {
3576 input: function(element, value) {
2036 switch (element.type.toLowerCase()) {
3577 switch (element.type.toLowerCase()) {
2037 case 'checkbox':
3578 case 'checkbox':
2038 case 'radio':
3579 case 'radio':
2039 return Form.Element.Serializers.inputSelector(element);
3580 return Form.Element.Serializers.inputSelector(element, value);
2040 default:
3581 default:
2041 return Form.Element.Serializers.textarea(element);
3582 return Form.Element.Serializers.textarea(element, value);
2042 }
3583 }
2043 },
3584 },
2044
3585
2045 inputSelector: function(element) {
3586 inputSelector: function(element, value) {
2046 return element.checked ? element.value : null;
3587 if (Object.isUndefined(value)) return element.checked ? element.value : null;
3588 else element.checked = !!value;
2047 },
3589 },
2048
3590
2049 textarea: function(element) {
3591 textarea: function(element, value) {
2050 return element.value;
3592 if (Object.isUndefined(value)) return element.value;
3593 else element.value = value;
2051 },
3594 },
2052
3595
2053 select: function(element) {
3596 select: function(element, index) {
3597 if (Object.isUndefined(index))
2054 return this[element.type == 'select-one' ?
3598 return this[element.type == 'select-one' ?
2055 'selectOne' : 'selectMany'](element);
3599 'selectOne' : 'selectMany'](element);
3600 else {
3601 var opt, value, single = !Object.isArray(index);
3602 for (var i = 0, length = element.length; i < length; i++) {
3603 opt = element.options[i];
3604 value = this.optionValue(opt);
3605 if (single) {
3606 if (value == index) {
3607 opt.selected = true;
3608 return;
3609 }
3610 }
3611 else opt.selected = index.include(value);
3612 }
3613 }
2056 },
3614 },
2057
3615
2058 selectOne: function(element) {
3616 selectOne: function(element) {
@@ -2075,219 +3633,438 Form.Element.Serializers = {
2075 // extend element because hasAttribute may not be native
3633 // extend element because hasAttribute may not be native
2076 return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
3634 return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
2077 }
3635 }
3636 };
3637
3638 /*--------------------------------------------------------------------------*/
3639
3640 Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
3641 initialize: function($super, element, frequency, callback) {
3642 $super(callback, frequency);
3643 this.element = $(element);
3644 this.lastValue = this.getValue();
3645 },
3646
3647 execute: function() {
3648 var value = this.getValue();
3649 if (Object.isString(this.lastValue) && Object.isString(value) ?
3650 this.lastValue != value : String(this.lastValue) != String(value)) {
3651 this.callback(this.element, value);
3652 this.lastValue = value;
3653 }
3654 }
3655 });
3656
3657 Form.Element.Observer = Class.create(Abstract.TimedObserver, {
3658 getValue: function() {
3659 return Form.Element.getValue(this.element);
3660 }
3661 });
3662
3663 Form.Observer = Class.create(Abstract.TimedObserver, {
3664 getValue: function() {
3665 return Form.serialize(this.element);
2078 }
3666 }
3667 });
2079
3668
2080 /*--------------------------------------------------------------------------*/
3669 /*--------------------------------------------------------------------------*/
2081
3670
2082 Abstract.TimedObserver = function() {}
3671 Abstract.EventObserver = Class.create({
2083 Abstract.TimedObserver.prototype = {
3672 initialize: function(element, callback) {
2084 initialize: function(element, frequency, callback) {
2085 this.frequency = frequency;
2086 this.element = $(element);
3673 this.element = $(element);
2087 this.callback = callback;
3674 this.callback = callback;
2088
3675
2089 this.lastValue = this.getValue();
3676 this.lastValue = this.getValue();
2090 this.registerCallback();
3677 if (this.element.tagName.toLowerCase() == 'form')
2091 },
3678 this.registerFormCallbacks();
2092
3679 else
2093 registerCallback: function() {
3680 this.registerCallback(this.element);
2094 setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
2095 },
3681 },
2096
3682
2097 onTimerEvent: function() {
3683 onElementEvent: function() {
2098 var value = this.getValue();
3684 var value = this.getValue();
2099 var changed = ('string' == typeof this.lastValue && 'string' == typeof value
3685 if (this.lastValue != value) {
2100 ? this.lastValue != value : String(this.lastValue) != String(value));
2101 if (changed) {
2102 this.callback(this.element, value);
3686 this.callback(this.element, value);
2103 this.lastValue = value;
3687 this.lastValue = value;
2104 }
3688 }
3689 },
3690
3691 registerFormCallbacks: function() {
3692 Form.getElements(this.element).each(this.registerCallback, this);
3693 },
3694
3695 registerCallback: function(element) {
3696 if (element.type) {
3697 switch (element.type.toLowerCase()) {
3698 case 'checkbox':
3699 case 'radio':
3700 Event.observe(element, 'click', this.onElementEvent.bind(this));
3701 break;
3702 default:
3703 Event.observe(element, 'change', this.onElementEvent.bind(this));
3704 break;
3705 }
2105 }
3706 }
2106 }
3707 }
3708 });
2107
3709
2108 Form.Element.Observer = Class.create();
3710 Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
2109 Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2110 getValue: function() {
3711 getValue: function() {
2111 return Form.Element.getValue(this.element);
3712 return Form.Element.getValue(this.element);
2112 }
3713 }
2113 });
3714 });
2114
3715
2115 Form.Observer = Class.create();
3716 Form.EventObserver = Class.create(Abstract.EventObserver, {
2116 Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
3717 getValue: function() {
2117 getValue: function() {
3718 return Form.serialize(this.element);
2118 return Form.serialize(this.element);
3719 }
2119 }
3720 });
3721 if (!window.Event) var Event = { };
3722
3723 Object.extend(Event, {
3724 KEY_BACKSPACE: 8,
3725 KEY_TAB: 9,
3726 KEY_RETURN: 13,
3727 KEY_ESC: 27,
3728 KEY_LEFT: 37,
3729 KEY_UP: 38,
3730 KEY_RIGHT: 39,
3731 KEY_DOWN: 40,
3732 KEY_DELETE: 46,
3733 KEY_HOME: 36,
3734 KEY_END: 35,
3735 KEY_PAGEUP: 33,
3736 KEY_PAGEDOWN: 34,
3737 KEY_INSERT: 45,
3738
3739 cache: { },
3740
3741 relatedTarget: function(event) {
3742 var element;
3743 switch(event.type) {
3744 case 'mouseover': element = event.fromElement; break;
3745 case 'mouseout': element = event.toElement; break;
3746 default: return null;
3747 }
3748 return Element.extend(element);
3749 }
3750 });
3751
3752 Event.Methods = (function() {
3753 var isButton;
3754
3755 if (Prototype.Browser.IE) {
3756 var buttonMap = { 0: 1, 1: 4, 2: 2 };
3757 isButton = function(event, code) {
3758 return event.button == buttonMap[code];
3759 };
3760
3761 } else if (Prototype.Browser.WebKit) {
3762 isButton = function(event, code) {
3763 switch (code) {
3764 case 0: return event.which == 1 && !event.metaKey;
3765 case 1: return event.which == 1 && event.metaKey;
3766 default: return false;
3767 }
3768 };
3769
3770 } else {
3771 isButton = function(event, code) {
3772 return event.which ? (event.which === code + 1) : (event.button === code);
3773 };
3774 }
3775
3776 return {
3777 isLeftClick: function(event) { return isButton(event, 0) },
3778 isMiddleClick: function(event) { return isButton(event, 1) },
3779 isRightClick: function(event) { return isButton(event, 2) },
3780
3781 element: function(event) {
3782 var node = Event.extend(event).target;
3783 return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
3784 },
3785
3786 findElement: function(event, expression) {
3787 var element = Event.element(event);
3788 if (!expression) return element;
3789 var elements = [element].concat(element.ancestors());
3790 return Selector.findElement(elements, expression, 0);
3791 },
3792
3793 pointer: function(event) {
3794 return {
3795 x: event.pageX || (event.clientX +
3796 (document.documentElement.scrollLeft || document.body.scrollLeft)),
3797 y: event.pageY || (event.clientY +
3798 (document.documentElement.scrollTop || document.body.scrollTop))
3799 };
3800 },
3801
3802 pointerX: function(event) { return Event.pointer(event).x },
3803 pointerY: function(event) { return Event.pointer(event).y },
3804
3805 stop: function(event) {
3806 Event.extend(event);
3807 event.preventDefault();
3808 event.stopPropagation();
3809 event.stopped = true;
3810 }
3811 };
3812 })();
3813
3814 Event.extend = (function() {
3815 var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
3816 m[name] = Event.Methods[name].methodize();
3817 return m;
3818 });
3819
3820 if (Prototype.Browser.IE) {
3821 Object.extend(methods, {
3822 stopPropagation: function() { this.cancelBubble = true },
3823 preventDefault: function() { this.returnValue = false },
3824 inspect: function() { return "[object Event]" }
3825 });
3826
3827 return function(event) {
3828 if (!event) return false;
3829 if (event._extendedByPrototype) return event;
3830
3831 event._extendedByPrototype = Prototype.emptyFunction;
3832 var pointer = Event.pointer(event);
3833 Object.extend(event, {
3834 target: event.srcElement,
3835 relatedTarget: Event.relatedTarget(event),
3836 pageX: pointer.x,
3837 pageY: pointer.y
3838 });
3839 return Object.extend(event, methods);
3840 };
3841
3842 } else {
3843 Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
3844 Object.extend(Event.prototype, methods);
3845 return Prototype.K;
3846 }
3847 })();
3848
3849 Object.extend(Event, (function() {
3850 var cache = Event.cache;
3851
3852 function getEventID(element) {
3853 if (element._eventID) return element._eventID;
3854 arguments.callee.id = arguments.callee.id || 1;
3855 return element._eventID = ++arguments.callee.id;
3856 }
3857
3858 function getDOMEventName(eventName) {
3859 if (eventName && eventName.include(':')) return "dataavailable";
3860 return eventName;
3861 }
3862
3863 function getCacheForID(id) {
3864 return cache[id] = cache[id] || { };
3865 }
3866
3867 function getWrappersForEventName(id, eventName) {
3868 var c = getCacheForID(id);
3869 return c[eventName] = c[eventName] || [];
3870 }
3871
3872 function createWrapper(element, eventName, handler) {
3873 var id = getEventID(element);
3874 var c = getWrappersForEventName(id, eventName);
3875 if (c.pluck("handler").include(handler)) return false;
3876
3877 var wrapper = function(event) {
3878 if (!Event || !Event.extend ||
3879 (event.eventName && event.eventName != eventName))
3880 return false;
3881
3882 Event.extend(event);
3883 handler.call(element, event)
3884 };
3885
3886 wrapper.handler = handler;
3887 c.push(wrapper);
3888 return wrapper;
3889 }
3890
3891 function findWrapper(id, eventName, handler) {
3892 var c = getWrappersForEventName(id, eventName);
3893 return c.find(function(wrapper) { return wrapper.handler == handler });
3894 }
3895
3896 function destroyWrapper(id, eventName, handler) {
3897 var c = getCacheForID(id);
3898 if (!c[eventName]) return false;
3899 c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
3900 }
3901
3902 function destroyCache() {
3903 for (var id in cache)
3904 for (var eventName in cache[id])
3905 cache[id][eventName] = null;
3906 }
3907
3908 if (window.attachEvent) {
3909 window.attachEvent("onunload", destroyCache);
3910 }
3911
3912 return {
3913 observe: function(element, eventName, handler) {
3914 element = $(element);
3915 var name = getDOMEventName(eventName);
3916
3917 var wrapper = createWrapper(element, eventName, handler);
3918 if (!wrapper) return element;
3919
3920 if (element.addEventListener) {
3921 element.addEventListener(name, wrapper, false);
3922 } else {
3923 element.attachEvent("on" + name, wrapper);
3924 }
3925
3926 return element;
3927 },
3928
3929 stopObserving: function(element, eventName, handler) {
3930 element = $(element);
3931 var id = getEventID(element), name = getDOMEventName(eventName);
3932
3933 if (!handler && eventName) {
3934 getWrappersForEventName(id, eventName).each(function(wrapper) {
3935 element.stopObserving(eventName, wrapper.handler);
3936 });
3937 return element;
3938
3939 } else if (!eventName) {
3940 Object.keys(getCacheForID(id)).each(function(eventName) {
3941 element.stopObserving(eventName);
2120 });
3942 });
3943 return element;
3944 }
2121
3945
2122 /*--------------------------------------------------------------------------*/
3946 var wrapper = findWrapper(id, eventName, handler);
3947 if (!wrapper) return element;
2123
3948
2124 Abstract.EventObserver = function() {}
3949 if (element.removeEventListener) {
2125 Abstract.EventObserver.prototype = {
3950 element.removeEventListener(name, wrapper, false);
2126 initialize: function(element, callback) {
3951 } else {
2127 this.element = $(element);
3952 element.detachEvent("on" + name, wrapper);
2128 this.callback = callback;
3953 }
2129
3954
2130 this.lastValue = this.getValue();
3955 destroyWrapper(id, eventName, handler);
2131 if (this.element.tagName.toLowerCase() == 'form')
2132 this.registerFormCallbacks();
2133 else
2134 this.registerCallback(this.element);
2135 },
2136
3956
2137 onElementEvent: function() {
3957 return element;
2138 var value = this.getValue();
2139 if (this.lastValue != value) {
2140 this.callback(this.element, value);
2141 this.lastValue = value;
2142 }
2143 },
3958 },
2144
3959
2145 registerFormCallbacks: function() {
3960 fire: function(element, eventName, memo) {
2146 Form.getElements(this.element).each(this.registerCallback.bind(this));
3961 element = $(element);
2147 },
3962 if (element == document && document.createEvent && !element.dispatchEvent)
3963 element = document.documentElement;
2148
3964
2149 registerCallback: function(element) {
3965 if (document.createEvent) {
2150 if (element.type) {
3966 var event = document.createEvent("HTMLEvents");
2151 switch (element.type.toLowerCase()) {
3967 event.initEvent("dataavailable", true, true);
2152 case 'checkbox':
3968 } else {
2153 case 'radio':
3969 var event = document.createEventObject();
2154 Event.observe(element, 'click', this.onElementEvent.bind(this));
3970 event.eventType = "ondataavailable";
2155 break;
2156 default:
2157 Event.observe(element, 'change', this.onElementEvent.bind(this));
2158 break;
2159 }
2160 }
2161 }
2162 }
3971 }
2163
3972
2164 Form.Element.EventObserver = Class.create();
3973 event.eventName = eventName;
2165 Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
3974 event.memo = memo || { };
2166 getValue: function() {
2167 return Form.Element.getValue(this.element);
2168 }
2169 });
2170
3975
2171 Form.EventObserver = Class.create();
3976 if (document.createEvent) {
2172 Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
3977 element.dispatchEvent(event);
2173 getValue: function() {
3978 } else {
2174 return Form.serialize(this.element);
3979 element.fireEvent(event.eventType, event);
2175 }
3980 }
2176 });
3981
2177 if (!window.Event) {
3982 return Event.extend(event);
2178 var Event = new Object();
2179 }
3983 }
3984 };
3985 })());
2180
3986
2181 Object.extend(Event, {
3987 Object.extend(Event, Event.Methods);
2182 KEY_BACKSPACE: 8,
2183 KEY_TAB: 9,
2184 KEY_RETURN: 13,
2185 KEY_ESC: 27,
2186 KEY_LEFT: 37,
2187 KEY_UP: 38,
2188 KEY_RIGHT: 39,
2189 KEY_DOWN: 40,
2190 KEY_DELETE: 46,
2191 KEY_HOME: 36,
2192 KEY_END: 35,
2193 KEY_PAGEUP: 33,
2194 KEY_PAGEDOWN: 34,
2195
3988
2196 element: function(event) {
3989 Element.addMethods({
2197 return event.target || event.srcElement;
3990 fire: Event.fire,
2198 },
3991 observe: Event.observe,
3992 stopObserving: Event.stopObserving
3993 });
2199
3994
2200 isLeftClick: function(event) {
3995 Object.extend(document, {
2201 return (((event.which) && (event.which == 1)) ||
3996 fire: Element.Methods.fire.methodize(),
2202 ((event.button) && (event.button == 1)));
3997 observe: Element.Methods.observe.methodize(),
2203 },
3998 stopObserving: Element.Methods.stopObserving.methodize()
3999 });
2204
4000
2205 pointerX: function(event) {
4001 (function() {
2206 return event.pageX || (event.clientX +
4002 /* Support for the DOMContentLoaded event is based on work by Dan Webb,
2207 (document.documentElement.scrollLeft || document.body.scrollLeft));
4003 Matthias Miller, Dean Edwards and John Resig. */
2208 },
2209
4004
2210 pointerY: function(event) {
4005 var timer, fired = false;
2211 return event.pageY || (event.clientY +
2212 (document.documentElement.scrollTop || document.body.scrollTop));
2213 },
2214
4006
2215 stop: function(event) {
4007 function fireContentLoadedEvent() {
2216 if (event.preventDefault) {
4008 if (fired) return;
2217 event.preventDefault();
4009 if (timer) window.clearInterval(timer);
2218 event.stopPropagation();
4010 document.fire("dom:loaded");
2219 } else {
4011 fired = true;
2220 event.returnValue = false;
2221 event.cancelBubble = true;
2222 }
4012 }
2223 },
2224
4013
2225 // find the first node with the given tagName, starting from the
4014 if (document.addEventListener) {
2226 // node the event was triggered on; traverses the DOM upwards
4015 if (Prototype.Browser.WebKit) {
2227 findElement: function(event, tagName) {
4016 timer = window.setInterval(function() {
2228 var element = Event.element(event);
4017 if (/loaded|complete/.test(document.readyState))
2229 while (element.parentNode && (!element.tagName ||
4018 fireContentLoadedEvent();
2230 (element.tagName.toUpperCase() != tagName.toUpperCase())))
4019 }, 0);
2231 element = element.parentNode;
2232 return element;
2233 },
2234
4020
2235 observers: false,
4021 Event.observe(window, "load", fireContentLoadedEvent);
2236
4022
2237 _observeAndCache: function(element, name, observer, useCapture) {
4023 } else {
2238 if (!this.observers) this.observers = [];
4024 document.addEventListener("DOMContentLoaded",
2239 if (element.addEventListener) {
4025 fireContentLoadedEvent, false);
2240 this.observers.push([element, name, observer, useCapture]);
2241 element.addEventListener(name, observer, useCapture);
2242 } else if (element.attachEvent) {
2243 this.observers.push([element, name, observer, useCapture]);
2244 element.attachEvent('on' + name, observer);
2245 }
4026 }
2246 },
2247
4027
2248 unloadCache: function() {
4028 } else {
2249 if (!Event.observers) return;
4029 document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
2250 for (var i = 0, length = Event.observers.length; i < length; i++) {
4030 $("__onDOMContentLoaded").onreadystatechange = function() {
2251 Event.stopObserving.apply(this, Event.observers[i]);
4031 if (this.readyState == "complete") {
2252 Event.observers[i][0] = null;
4032 this.onreadystatechange = null;
4033 fireContentLoadedEvent();
2253 }
4034 }
2254 Event.observers = false;
4035 };
2255 },
4036 }
4037 })();
4038 /*------------------------------- DEPRECATED -------------------------------*/
2256
4039
2257 observe: function(element, name, observer, useCapture) {
4040 Hash.toQueryString = Object.toQueryString;
2258 element = $(element);
2259 useCapture = useCapture || false;
2260
4041
2261 if (name == 'keypress' &&
4042 var Toggle = { display: Element.toggle };
2262 (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
2263 || element.attachEvent))
2264 name = 'keydown';
2265
4043
2266 Event._observeAndCache(element, name, observer, useCapture);
4044 Element.Methods.childOf = Element.Methods.descendantOf;
4045
4046 var Insertion = {
4047 Before: function(element, content) {
4048 return Element.insert(element, {before:content});
2267 },
4049 },
2268
4050
2269 stopObserving: function(element, name, observer, useCapture) {
4051 Top: function(element, content) {
2270 element = $(element);
4052 return Element.insert(element, {top:content});
2271 useCapture = useCapture || false;
4053 },
2272
4054
2273 if (name == 'keypress' &&
4055 Bottom: function(element, content) {
2274 (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
4056 return Element.insert(element, {bottom:content});
2275 || element.detachEvent))
4057 },
2276 name = 'keydown';
2277
4058
2278 if (element.removeEventListener) {
4059 After: function(element, content) {
2279 element.removeEventListener(name, observer, useCapture);
4060 return Element.insert(element, {after:content});
2280 } else if (element.detachEvent) {
2281 try {
2282 element.detachEvent('on' + name, observer);
2283 } catch (e) {}
2284 }
2285 }
4061 }
2286 });
4062 };
2287
4063
2288 /* prevent memory leaks in IE */
4064 var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
2289 if (navigator.appVersion.match(/\bMSIE\b/))
4065
2290 Event.observe(window, 'unload', Event.unloadCache, false);
4066 // This should be moved to script.aculo.us; notice the deprecated methods
4067 // further below, that map to the newer Element methods.
2291 var Position = {
4068 var Position = {
2292 // set to true if needed, warning: firefox performance problems
4069 // set to true if needed, warning: firefox performance problems
2293 // NOT neeeded for page scrolling, only if draggable contained in
4070 // NOT neeeded for page scrolling, only if draggable contained in
@@ -2307,59 +4084,13 var Position = {
2307 || 0;
4084 || 0;
2308 },
4085 },
2309
4086
2310 realOffset: function(element) {
2311 var valueT = 0, valueL = 0;
2312 do {
2313 valueT += element.scrollTop || 0;
2314 valueL += element.scrollLeft || 0;
2315 element = element.parentNode;
2316 } while (element);
2317 return [valueL, valueT];
2318 },
2319
2320 cumulativeOffset: function(element) {
2321 var valueT = 0, valueL = 0;
2322 do {
2323 valueT += element.offsetTop || 0;
2324 valueL += element.offsetLeft || 0;
2325 element = element.offsetParent;
2326 } while (element);
2327 return [valueL, valueT];
2328 },
2329
2330 positionedOffset: function(element) {
2331 var valueT = 0, valueL = 0;
2332 do {
2333 valueT += element.offsetTop || 0;
2334 valueL += element.offsetLeft || 0;
2335 element = element.offsetParent;
2336 if (element) {
2337 if(element.tagName=='BODY') break;
2338 var p = Element.getStyle(element, 'position');
2339 if (p == 'relative' || p == 'absolute') break;
2340 }
2341 } while (element);
2342 return [valueL, valueT];
2343 },
2344
2345 offsetParent: function(element) {
2346 if (element.offsetParent) return element.offsetParent;
2347 if (element == document.body) return element;
2348
2349 while ((element = element.parentNode) && element != document.body)
2350 if (Element.getStyle(element, 'position') != 'static')
2351 return element;
2352
2353 return document.body;
2354 },
2355
2356 // caches x/y coordinate pair to use with overlap
4087 // caches x/y coordinate pair to use with overlap
2357 within: function(element, x, y) {
4088 within: function(element, x, y) {
2358 if (this.includeScrollOffsets)
4089 if (this.includeScrollOffsets)
2359 return this.withinIncludingScrolloffsets(element, x, y);
4090 return this.withinIncludingScrolloffsets(element, x, y);
2360 this.xcomp = x;
4091 this.xcomp = x;
2361 this.ycomp = y;
4092 this.ycomp = y;
2362 this.offset = this.cumulativeOffset(element);
4093 this.offset = Element.cumulativeOffset(element);
2363
4094
2364 return (y >= this.offset[1] &&
4095 return (y >= this.offset[1] &&
2365 y < this.offset[1] + element.offsetHeight &&
4096 y < this.offset[1] + element.offsetHeight &&
@@ -2368,11 +4099,11 var Position = {
2368 },
4099 },
2369
4100
2370 withinIncludingScrolloffsets: function(element, x, y) {
4101 withinIncludingScrolloffsets: function(element, x, y) {
2371 var offsetcache = this.realOffset(element);
4102 var offsetcache = Element.cumulativeScrollOffset(element);
2372
4103
2373 this.xcomp = x + offsetcache[0] - this.deltaX;
4104 this.xcomp = x + offsetcache[0] - this.deltaX;
2374 this.ycomp = y + offsetcache[1] - this.deltaY;
4105 this.ycomp = y + offsetcache[1] - this.deltaY;
2375 this.offset = this.cumulativeOffset(element);
4106 this.offset = Element.cumulativeOffset(element);
2376
4107
2377 return (this.ycomp >= this.offset[1] &&
4108 return (this.ycomp >= this.offset[1] &&
2378 this.ycomp < this.offset[1] + element.offsetHeight &&
4109 this.ycomp < this.offset[1] + element.offsetHeight &&
@@ -2391,125 +4122,104 var Position = {
2391 element.offsetWidth;
4122 element.offsetWidth;
2392 },
4123 },
2393
4124
2394 page: function(forElement) {
4125 // Deprecation layer -- use newer Element methods now (1.5.2).
2395 var valueT = 0, valueL = 0;
2396
2397 var element = forElement;
2398 do {
2399 valueT += element.offsetTop || 0;
2400 valueL += element.offsetLeft || 0;
2401
4126
2402 // Safari fix
4127 cumulativeOffset: Element.Methods.cumulativeOffset,
2403 if (element.offsetParent==document.body)
2404 if (Element.getStyle(element,'position')=='absolute') break;
2405
4128
2406 } while (element = element.offsetParent);
4129 positionedOffset: Element.Methods.positionedOffset,
2407
4130
2408 element = forElement;
4131 absolutize: function(element) {
2409 do {
4132 Position.prepare();
2410 if (!window.opera || element.tagName=='BODY') {
4133 return Element.absolutize(element);
2411 valueT -= element.scrollTop || 0;
4134 },
2412 valueL -= element.scrollLeft || 0;
2413 }
2414 } while (element = element.parentNode);
2415
4135
2416 return [valueL, valueT];
4136 relativize: function(element) {
4137 Position.prepare();
4138 return Element.relativize(element);
2417 },
4139 },
2418
4140
2419 clone: function(source, target) {
4141 realOffset: Element.Methods.cumulativeScrollOffset,
2420 var options = Object.extend({
2421 setLeft: true,
2422 setTop: true,
2423 setWidth: true,
2424 setHeight: true,
2425 offsetTop: 0,
2426 offsetLeft: 0
2427 }, arguments[2] || {})
2428
4142
2429 // find page position of source
4143 offsetParent: Element.Methods.getOffsetParent,
2430 source = $(source);
2431 var p = Position.page(source);
2432
4144
2433 // find coordinate system to use
4145 page: Element.Methods.viewportOffset,
2434 target = $(target);
4146
2435 var delta = [0, 0];
4147 clone: function(source, target, options) {
2436 var parent = null;
4148 options = options || { };
2437 // delta [0,0] will do fine with position: fixed elements,
4149 return Element.clonePosition(target, source, options);
2438 // position:absolute needs offsetParent deltas
2439 if (Element.getStyle(target,'position') == 'absolute') {
2440 parent = Position.offsetParent(target);
2441 delta = Position.page(parent);
2442 }
4150 }
4151 };
2443
4152
2444 // correct by body offsets (fixes Safari)
4153 /*--------------------------------------------------------------------------*/
2445 if (parent == document.body) {
4154
2446 delta[0] -= document.body.offsetLeft;
4155 if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
2447 delta[1] -= document.body.offsetTop;
4156 function iter(name) {
4157 return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
4158 }
4159
4160 instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
4161 function(element, className) {
4162 className = className.toString().strip();
4163 var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
4164 return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
4165 } : function(element, className) {
4166 className = className.toString().strip();
4167 var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
4168 if (!classNames && !className) return elements;
4169
4170 var nodes = $(element).getElementsByTagName('*');
4171 className = ' ' + className + ' ';
4172
4173 for (var i = 0, child, cn; child = nodes[i]; i++) {
4174 if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
4175 (classNames && classNames.all(function(name) {
4176 return !name.toString().blank() && cn.include(' ' + name + ' ');
4177 }))))
4178 elements.push(Element.extend(child));
2448 }
4179 }
4180 return elements;
4181 };
2449
4182
2450 // set position
4183 return function(className, parentElement) {
2451 if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
4184 return $(parentElement || document.body).getElementsByClassName(className);
2452 if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
4185 };
2453 if(options.setWidth) target.style.width = source.offsetWidth + 'px';
4186 }(Element.Methods);
2454 if(options.setHeight) target.style.height = source.offsetHeight + 'px';
2455 },
2456
4187
2457 absolutize: function(element) {
4188 /*--------------------------------------------------------------------------*/
2458 element = $(element);
2459 if (element.style.position == 'absolute') return;
2460 Position.prepare();
2461
4189
2462 var offsets = Position.positionedOffset(element);
4190 Element.ClassNames = Class.create();
2463 var top = offsets[1];
4191 Element.ClassNames.prototype = {
2464 var left = offsets[0];
4192 initialize: function(element) {
2465 var width = element.clientWidth;
4193 this.element = $(element);
2466 var height = element.clientHeight;
4194 },
2467
4195
2468 element._originalLeft = left - parseFloat(element.style.left || 0);
4196 _each: function(iterator) {
2469 element._originalTop = top - parseFloat(element.style.top || 0);
4197 this.element.className.split(/\s+/).select(function(name) {
2470 element._originalWidth = element.style.width;
4198 return name.length > 0;
2471 element._originalHeight = element.style.height;
4199 })._each(iterator);
4200 },
2472
4201
2473 element.style.position = 'absolute';
4202 set: function(className) {
2474 element.style.top = top + 'px';
4203 this.element.className = className;
2475 element.style.left = left + 'px';
2476 element.style.width = width + 'px';
2477 element.style.height = height + 'px';
2478 },
4204 },
2479
4205
2480 relativize: function(element) {
4206 add: function(classNameToAdd) {
2481 element = $(element);
4207 if (this.include(classNameToAdd)) return;
2482 if (element.style.position == 'relative') return;
4208 this.set($A(this).concat(classNameToAdd).join(' '));
2483 Position.prepare();
4209 },
2484
4210
2485 element.style.position = 'relative';
4211 remove: function(classNameToRemove) {
2486 var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
4212 if (!this.include(classNameToRemove)) return;
2487 var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
4213 this.set($A(this).without(classNameToRemove).join(' '));
4214 },
2488
4215
2489 element.style.top = top + 'px';
4216 toString: function() {
2490 element.style.left = left + 'px';
4217 return $A(this).join(' ');
2491 element.style.height = element._originalHeight;
2492 element.style.width = element._originalWidth;
2493 }
2494 }
4218 }
4219 };
2495
4220
2496 // Safari returns margins on body which is incorrect if the child is absolutely
4221 Object.extend(Element.ClassNames.prototype, Enumerable);
2497 // positioned. For performance reasons, redefine Position.cumulativeOffset for
2498 // KHTML/WebKit only.
2499 if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
2500 Position.cumulativeOffset = function(element) {
2501 var valueT = 0, valueL = 0;
2502 do {
2503 valueT += element.offsetTop || 0;
2504 valueL += element.offsetLeft || 0;
2505 if (element.offsetParent == document.body)
2506 if (Element.getStyle(element, 'position') == 'absolute') break;
2507
2508 element = element.offsetParent;
2509 } while (element);
2510
4222
2511 return [valueL, valueT];
4223 /*--------------------------------------------------------------------------*/
2512 }
2513 }
2514
4224
2515 Element.addMethods(); No newline at end of file
4225 Element.addMethods();
General Comments 0
You need to be logged in to leave comments. Login now