##// END OF EJS Templates
updated prototype (1.50) and script.aculo.us javascripts...
Jean-Philippe Lang -
r238:2e72f2eca8f4
parent child
Show More
@@ -1,12 +1,13
1 // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 // (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3 // (c) 2005 Jon Tirsen (http://www.tirsen.com)
1 // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 // (c) 2005, 2006 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3 // (c) 2005, 2006 Jon Tirsen (http://www.tirsen.com)
4 4 // Contributors:
5 5 // Richard Livsey
6 6 // Rahul Bhargava
7 7 // Rob Wills
8 8 //
9 // See scriptaculous.js for full license.
9 // script.aculo.us is freely distributable under the terms of an MIT-style license.
10 // For details, see the script.aculo.us web site: http://script.aculo.us/
10 11
11 12 // Autocompleter.Base handles all the autocompletion functionality
12 13 // that's independent of the data source for autocompletion. This
@@ -33,6 +34,9
33 34 // useful when one of the tokens is \n (a newline), as it
34 35 // allows smart autocompletion after linebreaks.
35 36
37 if(typeof Effect == 'undefined')
38 throw("controls.js requires including script.aculo.us' effects.js library");
39
36 40 var Autocompleter = {}
37 41 Autocompleter.Base = function() {};
38 42 Autocompleter.Base.prototype = {
@@ -45,7 +49,7 Autocompleter.Base.prototype = {
45 49 this.index = 0;
46 50 this.entryCount = 0;
47 51
48 if (this.setOptions)
52 if(this.setOptions)
49 53 this.setOptions(options);
50 54 else
51 55 this.options = options || {};
@@ -55,17 +59,20 Autocompleter.Base.prototype = {
55 59 this.options.frequency = this.options.frequency || 0.4;
56 60 this.options.minChars = this.options.minChars || 1;
57 61 this.options.onShow = this.options.onShow ||
58 function(element, update){
59 if(!update.style.position || update.style.position=='absolute') {
60 update.style.position = 'absolute';
61 Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
62 }
63 Effect.Appear(update,{duration:0.15});
64 };
62 function(element, update){
63 if(!update.style.position || update.style.position=='absolute') {
64 update.style.position = 'absolute';
65 Position.clone(element, update, {
66 setHeight: false,
67 offsetTop: element.offsetHeight
68 });
69 }
70 Effect.Appear(update,{duration:0.15});
71 };
65 72 this.options.onHide = this.options.onHide ||
66 function(element, update){ new Effect.Fade(update,{duration:0.15}) };
73 function(element, update){ new Effect.Fade(update,{duration:0.15}) };
67 74
68 if (typeof(this.options.tokens) == 'string')
75 if(typeof(this.options.tokens) == 'string')
69 76 this.options.tokens = new Array(this.options.tokens);
70 77
71 78 this.observer = null;
@@ -94,7 +101,7 Autocompleter.Base.prototype = {
94 101 },
95 102
96 103 fixIEOverlapping: function() {
97 Position.clone(this.update, this.iefix);
104 Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
98 105 this.iefix.style.zIndex = 1;
99 106 this.update.style.zIndex = 2;
100 107 Element.show(this.iefix);
@@ -141,8 +148,8 Autocompleter.Base.prototype = {
141 148 return;
142 149 }
143 150 else
144 if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN)
145 return;
151 if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
152 (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
146 153
147 154 this.changed = true;
148 155 this.hasFocus = true;
@@ -152,6 +159,12 Autocompleter.Base.prototype = {
152 159 setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
153 160 },
154 161
162 activate: function() {
163 this.changed = false;
164 this.hasFocus = true;
165 this.getUpdatedChoices();
166 },
167
155 168 onHover: function(event) {
156 169 var element = Event.findElement(event, 'LI');
157 170 if(this.index != element.autocompleteIndex)
@@ -196,11 +209,13 Autocompleter.Base.prototype = {
196 209 markPrevious: function() {
197 210 if(this.index > 0) this.index--
198 211 else this.index = this.entryCount-1;
212 this.getEntry(this.index).scrollIntoView(true);
199 213 },
200 214
201 215 markNext: function() {
202 216 if(this.index < this.entryCount-1) this.index++
203 217 else this.index = 0;
218 this.getEntry(this.index).scrollIntoView(false);
204 219 },
205 220
206 221 getEntry: function(index) {
@@ -221,8 +236,13 Autocompleter.Base.prototype = {
221 236 this.options.updateElement(selectedElement);
222 237 return;
223 238 }
224
225 var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
239 var value = '';
240 if (this.options.select) {
241 var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
242 if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
243 } else
244 value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
245
226 246 var lastTokenPos = this.findLastToken();
227 247 if (lastTokenPos != -1) {
228 248 var newValue = this.element.value.substr(0, lastTokenPos + 1);
@@ -243,11 +263,11 Autocompleter.Base.prototype = {
243 263 if(!this.changed && this.hasFocus) {
244 264 this.update.innerHTML = choices;
245 265 Element.cleanWhitespace(this.update);
246 Element.cleanWhitespace(this.update.firstChild);
266 Element.cleanWhitespace(this.update.down());
247 267
248 if(this.update.firstChild && this.update.firstChild.childNodes) {
268 if(this.update.firstChild && this.update.down().childNodes) {
249 269 this.entryCount =
250 this.update.firstChild.childNodes.length;
270 this.update.down().childNodes.length;
251 271 for (var i = 0; i < this.entryCount; i++) {
252 272 var entry = this.getEntry(i);
253 273 entry.autocompleteIndex = i;
@@ -258,9 +278,14 Autocompleter.Base.prototype = {
258 278 }
259 279
260 280 this.stopIndicator();
261
262 281 this.index = 0;
263 this.render();
282
283 if(this.entryCount==1 && this.options.autoSelect) {
284 this.selectEntry();
285 this.hide();
286 } else {
287 this.render();
288 }
264 289 }
265 290 },
266 291
@@ -305,7 +330,7 Autocompleter.Base.prototype = {
305 330 Ajax.Autocompleter = Class.create();
306 331 Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
307 332 initialize: function(element, update, url, options) {
308 this.baseInitialize(element, update, options);
333 this.baseInitialize(element, update, options);
309 334 this.options.asynchronous = true;
310 335 this.options.onComplete = this.onComplete.bind(this);
311 336 this.options.defaultParams = this.options.parameters || null;
@@ -448,7 +473,10 Ajax.InPlaceEditor.prototype = {
448 473 this.element = $(element);
449 474
450 475 this.options = Object.extend({
476 paramName: "value",
477 okButton: true,
451 478 okText: "ok",
479 cancelLink: true,
452 480 cancelText: "cancel",
453 481 savingText: "Saving...",
454 482 clickToEditText: "Click to edit",
@@ -470,8 +498,10 Ajax.InPlaceEditor.prototype = {
470 498 formClassName: 'inplaceeditor-form',
471 499 highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
472 500 highlightendcolor: "#FFFFFF",
473 externalControl: null,
474 ajaxOptions: {}
501 externalControl: null,
502 submitOnBlur: false,
503 ajaxOptions: {},
504 evalScripts: false
475 505 }, options || {});
476 506
477 507 if(!this.options.formId && this.element.id) {
@@ -516,7 +546,7 Ajax.InPlaceEditor.prototype = {
516 546 Element.hide(this.element);
517 547 this.createForm();
518 548 this.element.parentNode.insertBefore(this.form, this.element);
519 Field.scrollFreeActivate(this.editField);
549 if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
520 550 // stop the event to avoid a page refresh in Safari
521 551 if (evt) {
522 552 Event.stop(evt);
@@ -536,16 +566,22 Ajax.InPlaceEditor.prototype = {
536 566 this.form.appendChild(br);
537 567 }
538 568
539 okButton = document.createElement("input");
540 okButton.type = "submit";
541 okButton.value = this.options.okText;
542 this.form.appendChild(okButton);
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 }
543 576
544 cancelLink = document.createElement("a");
545 cancelLink.href = "#";
546 cancelLink.appendChild(document.createTextNode(this.options.cancelText));
547 cancelLink.onclick = this.onclickCancel.bind(this);
548 this.form.appendChild(cancelLink);
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 }
549 585 },
550 586 hasHTMLLineBreaks: function(string) {
551 587 if (!this.options.handleLineBreaks) return false;
@@ -561,24 +597,34 Ajax.InPlaceEditor.prototype = {
561 597 } else {
562 598 text = this.getText();
563 599 }
600
601 var obj = this;
564 602
565 603 if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
566 604 this.options.textarea = false;
567 605 var textField = document.createElement("input");
606 textField.obj = this;
568 607 textField.type = "text";
569 textField.name = "value";
608 textField.name = this.options.paramName;
570 609 textField.value = text;
571 610 textField.style.backgroundColor = this.options.highlightcolor;
611 textField.className = 'editor_field';
572 612 var size = this.options.size || this.options.cols || 0;
573 613 if (size != 0) textField.size = size;
614 if (this.options.submitOnBlur)
615 textField.onblur = this.onSubmit.bind(this);
574 616 this.editField = textField;
575 617 } else {
576 618 this.options.textarea = true;
577 619 var textArea = document.createElement("textarea");
578 textArea.name = "value";
620 textArea.obj = this;
621 textArea.name = this.options.paramName;
579 622 textArea.value = this.convertHTMLLineBreaks(text);
580 623 textArea.rows = this.options.rows;
581 624 textArea.cols = this.options.cols || 40;
625 textArea.className = 'editor_field';
626 if (this.options.submitOnBlur)
627 textArea.onblur = this.onSubmit.bind(this);
582 628 this.editField = textArea;
583 629 }
584 630
@@ -605,6 +651,7 Ajax.InPlaceEditor.prototype = {
605 651 Element.removeClassName(this.form, this.options.loadingClassName);
606 652 this.editField.disabled = false;
607 653 this.editField.value = transport.responseText.stripTags();
654 Field.scrollFreeActivate(this.editField);
608 655 },
609 656 onclickCancel: function() {
610 657 this.onComplete();
@@ -629,19 +676,26 Ajax.InPlaceEditor.prototype = {
629 676 // to be displayed indefinitely
630 677 this.onLoading();
631 678
632 new Ajax.Updater(
633 {
634 success: this.element,
635 // don't update on failure (this could be an option)
636 failure: null
637 },
638 this.url,
639 Object.extend({
640 parameters: this.options.callback(form, value),
641 onComplete: this.onComplete.bind(this),
642 onFailure: this.onFailure.bind(this)
643 }, this.options.ajaxOptions)
644 );
679 if (this.options.evalScripts) {
680 new Ajax.Request(
681 this.url, Object.extend({
682 parameters: this.options.callback(form, value),
683 onComplete: this.onComplete.bind(this),
684 onFailure: this.onFailure.bind(this),
685 asynchronous:true,
686 evalScripts:true
687 }, this.options.ajaxOptions));
688 } else {
689 new Ajax.Updater(
690 { success: this.element,
691 // don't update on failure (this could be an option)
692 failure: null },
693 this.url, Object.extend({
694 parameters: this.options.callback(form, value),
695 onComplete: this.onComplete.bind(this),
696 onFailure: this.onFailure.bind(this)
697 }, this.options.ajaxOptions));
698 }
645 699 // stop the event to avoid a page refresh in Safari
646 700 if (arguments.length > 1) {
647 701 Event.stop(arguments[0]);
@@ -723,6 +777,35 Ajax.InPlaceEditor.prototype = {
723 777 }
724 778 };
725 779
780 Ajax.InPlaceCollectionEditor = Class.create();
781 Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
782 Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
783 createEditField: function() {
784 if (!this.cached_selectTag) {
785 var selectTag = document.createElement("select");
786 var collection = this.options.collection || [];
787 var optionTag;
788 collection.each(function(e,i) {
789 optionTag = document.createElement("option");
790 optionTag.value = (e instanceof Array) ? e[0] : e;
791 if((typeof this.options.value == 'undefined') &&
792 ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
793 if(this.options.value==optionTag.value) optionTag.selected = true;
794 optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
795 selectTag.appendChild(optionTag);
796 }.bind(this));
797 this.cached_selectTag = selectTag;
798 }
799
800 this.editField = this.cached_selectTag;
801 if(this.options.loadTextURL) this.loadExternalText();
802 this.form.appendChild(this.editField);
803 this.options.callback = function(form, value) {
804 return "value=" + encodeURIComponent(value);
805 }
806 }
807 });
808
726 809 // Delayed observer, like Form.Element.Observer,
727 810 // but waits for delay after last key input
728 811 // Ideal for live-search fields
@@ -747,4 +830,4 Form.Element.DelayedObserver.prototype = {
747 830 this.timer = null;
748 831 this.callback(this.element, $F(this.element));
749 832 }
750 }; No newline at end of file
833 };
This diff has been collapsed as it changes many lines, (550 lines changed) Show them Hide them
@@ -1,8 +1,11
1 // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
1 // Copyright (c) 2005, 2006 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 3 //
3 // See scriptaculous.js for full 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/
4 6
5 /*--------------------------------------------------------------------------*/
7 if(typeof Effect == 'undefined')
8 throw("dragdrop.js requires including script.aculo.us' effects.js library");
6 9
7 10 var Droppables = {
8 11 drops: [],
@@ -15,7 +18,8 var Droppables = {
15 18 element = $(element);
16 19 var options = Object.extend({
17 20 greedy: true,
18 hoverclass: null
21 hoverclass: null,
22 tree: false
19 23 }, arguments[1] || {});
20 24
21 25 // cache containers
@@ -37,12 +41,27 var Droppables = {
37 41
38 42 this.drops.push(options);
39 43 },
44
45 findDeepestChild: function(drops) {
46 deepest = drops[0];
47
48 for (i = 1; i < drops.length; ++i)
49 if (Element.isParent(drops[i].element, deepest.element))
50 deepest = drops[i];
51
52 return deepest;
53 },
40 54
41 55 isContained: function(element, drop) {
42 var parentNode = element.parentNode;
43 return drop._containers.detect(function(c) { return parentNode == c });
56 var containmentNode;
57 if(drop.tree) {
58 containmentNode = element.treeNode;
59 } else {
60 containmentNode = element.parentNode;
61 }
62 return drop._containers.detect(function(c) { return containmentNode == c });
44 63 },
45
64
46 65 isAffected: function(point, element, drop) {
47 66 return (
48 67 (drop.element!=element) &&
@@ -68,18 +87,22 var Droppables = {
68 87
69 88 show: function(point, element) {
70 89 if(!this.drops.length) return;
90 var affected = [];
71 91
72 92 if(this.last_active) this.deactivate(this.last_active);
73 93 this.drops.each( function(drop) {
74 if(Droppables.isAffected(point, element, drop)) {
75 if(drop.onHover)
76 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
77 if(drop.greedy) {
78 Droppables.activate(drop);
79 throw $break;
80 }
81 }
94 if(Droppables.isAffected(point, element, drop))
95 affected.push(drop);
82 96 });
97
98 if(affected.length>0) {
99 drop = Droppables.findDeepestChild(affected);
100 Position.within(drop.element, point[0], point[1]);
101 if(drop.onHover)
102 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
103
104 Droppables.activate(drop);
105 }
83 106 },
84 107
85 108 fire: function(event, element) {
@@ -124,11 +147,19 var Draggables = {
124 147 },
125 148
126 149 activate: function(draggable) {
127 window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
128 this.activeDraggable = draggable;
150 if(draggable.options.delay) {
151 this._timeout = setTimeout(function() {
152 Draggables._timeout = null;
153 window.focus();
154 Draggables.activeDraggable = draggable;
155 }.bind(this), draggable.options.delay);
156 } else {
157 window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
158 this.activeDraggable = draggable;
159 }
129 160 },
130 161
131 deactivate: function(draggbale) {
162 deactivate: function() {
132 163 this.activeDraggable = null;
133 164 },
134 165
@@ -139,13 +170,19 var Draggables = {
139 170 // the same coordinates, prevent needless redrawing (moz bug?)
140 171 if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
141 172 this._lastPointer = pointer;
173
142 174 this.activeDraggable.updateDrag(event, pointer);
143 175 },
144 176
145 177 endDrag: function(event) {
178 if(this._timeout) {
179 clearTimeout(this._timeout);
180 this._timeout = null;
181 }
146 182 if(!this.activeDraggable) return;
147 183 this._lastPointer = null;
148 184 this.activeDraggable.endDrag(event);
185 this.activeDraggable = null;
149 186 },
150 187
151 188 keyPress: function(event) {
@@ -168,6 +205,7 var Draggables = {
168 205 this.observers.each( function(o) {
169 206 if(o[eventName]) o[eventName](eventName, draggable, event);
170 207 });
208 if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
171 209 },
172 210
173 211 _cacheObserverCallbacks: function() {
@@ -182,31 +220,59 var Draggables = {
182 220 /*--------------------------------------------------------------------------*/
183 221
184 222 var Draggable = Class.create();
223 Draggable._dragging = {};
224
185 225 Draggable.prototype = {
186 226 initialize: function(element) {
187 var options = Object.extend({
227 var defaults = {
188 228 handle: false,
189 starteffect: function(element) {
190 new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7});
191 },
192 229 reverteffect: function(element, top_offset, left_offset) {
193 230 var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
194 element._revert = new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur});
231 new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
232 queue: {scope:'_draggable', position:'end'}
233 });
195 234 },
196 endeffect: function(element) {
197 new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
235 endeffect: function(element) {
236 var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
237 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
238 queue: {scope:'_draggable', position:'end'},
239 afterFinish: function(){
240 Draggable._dragging[element] = false
241 }
242 });
198 243 },
199 244 zindex: 1000,
200 245 revert: false,
201 snap: false // false, or xy or [x,y] or function(x,y){ return [x,y] }
202 }, arguments[1] || {});
246 scroll: false,
247 scrollSensitivity: 20,
248 scrollSpeed: 15,
249 snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
250 delay: 0
251 };
252
253 if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
254 Object.extend(defaults, {
255 starteffect: function(element) {
256 element._opacity = Element.getOpacity(element);
257 Draggable._dragging[element] = true;
258 new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
259 }
260 });
261
262 var options = Object.extend(defaults, arguments[1] || {});
203 263
204 264 this.element = $(element);
205 265
206 266 if(options.handle && (typeof options.handle == 'string'))
207 this.handle = Element.childrenWithClassName(this.element, options.handle)[0];
267 this.handle = this.element.down('.'+options.handle, 0);
268
208 269 if(!this.handle) this.handle = $(options.handle);
209 270 if(!this.handle) this.handle = this.element;
271
272 if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
273 options.scroll = $(options.scroll);
274 this._isScrollChild = Element.childOf(this.element, options.scroll);
275 }
210 276
211 277 Element.makePositioned(this.element); // fix IE
212 278
@@ -227,25 +293,23 Draggable.prototype = {
227 293
228 294 currentDelta: function() {
229 295 return([
230 parseInt(this.element.style.left || '0'),
231 parseInt(this.element.style.top || '0')]);
296 parseInt(Element.getStyle(this.element,'left') || '0'),
297 parseInt(Element.getStyle(this.element,'top') || '0')]);
232 298 },
233 299
234 300 initDrag: function(event) {
301 if(typeof Draggable._dragging[this.element] != 'undefined' &&
302 Draggable._dragging[this.element]) return;
235 303 if(Event.isLeftClick(event)) {
236 304 // abort on form elements, fixes a Firefox issue
237 305 var src = Event.element(event);
238 306 if(src.tagName && (
239 307 src.tagName=='INPUT' ||
240 308 src.tagName=='SELECT' ||
309 src.tagName=='OPTION' ||
241 310 src.tagName=='BUTTON' ||
242 311 src.tagName=='TEXTAREA')) return;
243 312
244 if(this.element._revert) {
245 this.element._revert.cancel();
246 this.element._revert = null;
247 }
248
249 313 var pointer = [Event.pointerX(event), Event.pointerY(event)];
250 314 var pos = Position.cumulativeOffset(this.element);
251 315 this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
@@ -269,7 +333,19 Draggable.prototype = {
269 333 this.element.parentNode.insertBefore(this._clone, this.element);
270 334 }
271 335
336 if(this.options.scroll) {
337 if (this.options.scroll == window) {
338 var where = this._getWindowScroll(this.options.scroll);
339 this.originalScrollLeft = where.left;
340 this.originalScrollTop = where.top;
341 } else {
342 this.originalScrollLeft = this.options.scroll.scrollLeft;
343 this.originalScrollTop = this.options.scroll.scrollTop;
344 }
345 }
346
272 347 Draggables.notify('onStart', this, event);
348
273 349 if(this.options.starteffect) this.options.starteffect(this.element);
274 350 },
275 351
@@ -278,11 +354,34 Draggable.prototype = {
278 354 Position.prepare();
279 355 Droppables.show(pointer, this.element);
280 356 Draggables.notify('onDrag', this, event);
357
281 358 this.draw(pointer);
282 359 if(this.options.change) this.options.change(this);
283 360
361 if(this.options.scroll) {
362 this.stopScrolling();
363
364 var p;
365 if (this.options.scroll == window) {
366 with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
367 } else {
368 p = Position.page(this.options.scroll);
369 p[0] += this.options.scroll.scrollLeft + Position.deltaX;
370 p[1] += this.options.scroll.scrollTop + Position.deltaY;
371 p.push(p[0]+this.options.scroll.offsetWidth);
372 p.push(p[1]+this.options.scroll.offsetHeight);
373 }
374 var speed = [0,0];
375 if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
376 if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
377 if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
378 if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
379 this.startScrolling(speed);
380 }
381
284 382 // fix AppleWebKit rendering
285 383 if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
384
286 385 Event.stop(event);
287 386 },
288 387
@@ -314,33 +413,46 Draggable.prototype = {
314 413
315 414 if(this.options.endeffect)
316 415 this.options.endeffect(this.element);
317
416
318 417 Draggables.deactivate(this);
319 418 Droppables.reset();
320 419 },
321 420
322 421 keyPress: function(event) {
323 if(!event.keyCode==Event.KEY_ESC) return;
422 if(event.keyCode!=Event.KEY_ESC) return;
324 423 this.finishDrag(event, false);
325 424 Event.stop(event);
326 425 },
327 426
328 427 endDrag: function(event) {
329 428 if(!this.dragging) return;
429 this.stopScrolling();
330 430 this.finishDrag(event, true);
331 431 Event.stop(event);
332 432 },
333 433
334 434 draw: function(point) {
335 435 var pos = Position.cumulativeOffset(this.element);
436 if(this.options.ghosting) {
437 var r = Position.realOffset(this.element);
438 pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
439 }
440
336 441 var d = this.currentDelta();
337 442 pos[0] -= d[0]; pos[1] -= d[1];
338 443
339 var p = [0,1].map(function(i){ return (point[i]-pos[i]-this.offset[i]) }.bind(this));
444 if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
445 pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
446 pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
447 }
448
449 var p = [0,1].map(function(i){
450 return (point[i]-pos[i]-this.offset[i])
451 }.bind(this));
340 452
341 453 if(this.options.snap) {
342 454 if(typeof this.options.snap == 'function') {
343 p = this.options.snap(p[0],p[1]);
455 p = this.options.snap(p[0],p[1],this);
344 456 } else {
345 457 if(this.options.snap instanceof Array) {
346 458 p = p.map( function(v, i) {
@@ -356,7 +468,80 Draggable.prototype = {
356 468 style.left = p[0] + "px";
357 469 if((!this.options.constraint) || (this.options.constraint=='vertical'))
358 470 style.top = p[1] + "px";
471
359 472 if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
473 },
474
475 stopScrolling: function() {
476 if(this.scrollInterval) {
477 clearInterval(this.scrollInterval);
478 this.scrollInterval = null;
479 Draggables._lastScrollPointer = null;
480 }
481 },
482
483 startScrolling: function(speed) {
484 if(!(speed[0] || speed[1])) return;
485 this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
486 this.lastScrolled = new Date();
487 this.scrollInterval = setInterval(this.scroll.bind(this), 10);
488 },
489
490 scroll: function() {
491 var current = new Date();
492 var delta = current - this.lastScrolled;
493 this.lastScrolled = current;
494 if(this.options.scroll == window) {
495 with (this._getWindowScroll(this.options.scroll)) {
496 if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
497 var d = delta / 1000;
498 this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
499 }
500 }
501 } else {
502 this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
503 this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
504 }
505
506 Position.prepare();
507 Droppables.show(Draggables._lastPointer, this.element);
508 Draggables.notify('onDrag', this);
509 if (this._isScrollChild) {
510 Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
511 Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
512 Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
513 if (Draggables._lastScrollPointer[0] < 0)
514 Draggables._lastScrollPointer[0] = 0;
515 if (Draggables._lastScrollPointer[1] < 0)
516 Draggables._lastScrollPointer[1] = 0;
517 this.draw(Draggables._lastScrollPointer);
518 }
519
520 if(this.options.change) this.options.change(this);
521 },
522
523 _getWindowScroll: function(w) {
524 var T, L, W, H;
525 with (w.document) {
526 if (w.document.documentElement && documentElement.scrollTop) {
527 T = documentElement.scrollTop;
528 L = documentElement.scrollLeft;
529 } else if (w.document.body) {
530 T = body.scrollTop;
531 L = body.scrollLeft;
532 }
533 if (w.innerWidth) {
534 W = w.innerWidth;
535 H = w.innerHeight;
536 } else if (w.document.documentElement && documentElement.clientWidth) {
537 W = documentElement.clientWidth;
538 H = documentElement.clientHeight;
539 } else {
540 W = body.offsetWidth;
541 H = body.offsetHeight
542 }
543 }
544 return { top: T, left: L, width: W, height: H };
360 545 }
361 546 }
362 547
@@ -382,38 +567,55 SortableObserver.prototype = {
382 567 }
383 568
384 569 var Sortable = {
385 sortables: new Array(),
570 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
386 571
387 options: function(element){
388 element = $(element);
389 return this.sortables.detect(function(s) { return s.element == element });
572 sortables: {},
573
574 _findRootElement: function(element) {
575 while (element.tagName != "BODY") {
576 if(element.id && Sortable.sortables[element.id]) return element;
577 element = element.parentNode;
578 }
579 },
580
581 options: function(element) {
582 element = Sortable._findRootElement($(element));
583 if(!element) return;
584 return Sortable.sortables[element.id];
390 585 },
391 586
392 587 destroy: function(element){
393 element = $(element);
394 this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
588 var s = Sortable.options(element);
589
590 if(s) {
395 591 Draggables.removeObserver(s.element);
396 592 s.droppables.each(function(d){ Droppables.remove(d) });
397 593 s.draggables.invoke('destroy');
398 });
399 this.sortables = this.sortables.reject(function(s) { return s.element == element });
594
595 delete Sortable.sortables[s.element.id];
596 }
400 597 },
401
598
402 599 create: function(element) {
403 600 element = $(element);
404 601 var options = Object.extend({
405 602 element: element,
406 603 tag: 'li', // assumes li children, override with tag: 'tagname'
407 604 dropOnEmpty: false,
408 tree: false, // fixme: unimplemented
605 tree: false,
606 treeTag: 'ul',
409 607 overlap: 'vertical', // one of 'vertical', 'horizontal'
410 608 constraint: 'vertical', // one of 'vertical', 'horizontal', false
411 609 containment: element, // also takes array of elements (or id's); or false
412 610 handle: false, // or a CSS class
413 611 only: false,
612 delay: 0,
414 613 hoverclass: null,
415 614 ghosting: false,
416 format: null,
615 scroll: false,
616 scrollSensitivity: 20,
617 scrollSpeed: 15,
618 format: this.SERIALIZE_RULE,
417 619 onChange: Prototype.emptyFunction,
418 620 onUpdate: Prototype.emptyFunction
419 621 }, arguments[1] || {});
@@ -424,6 +626,10 var Sortable = {
424 626 // build options for the draggables
425 627 var options_for_draggable = {
426 628 revert: true,
629 scroll: options.scroll,
630 scrollSpeed: options.scrollSpeed,
631 scrollSensitivity: options.scrollSensitivity,
632 delay: options.delay,
427 633 ghosting: options.ghosting,
428 634 constraint: options.constraint,
429 635 handle: options.handle };
@@ -449,9 +655,16 var Sortable = {
449 655 var options_for_droppable = {
450 656 overlap: options.overlap,
451 657 containment: options.containment,
658 tree: options.tree,
452 659 hoverclass: options.hoverclass,
453 onHover: Sortable.onHover,
454 greedy: !options.dropOnEmpty
660 onHover: Sortable.onHover
661 }
662
663 var options_for_tree = {
664 onHover: Sortable.onEmptyHover,
665 overlap: options.overlap,
666 containment: options.containment,
667 hoverclass: options.hoverclass
455 668 }
456 669
457 670 // fix for gecko engine
@@ -460,27 +673,33 var Sortable = {
460 673 options.draggables = [];
461 674 options.droppables = [];
462 675
463 // make it so
464
465 676 // drop on empty handling
466 if(options.dropOnEmpty) {
467 Droppables.add(element,
468 {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
677 if(options.dropOnEmpty || options.tree) {
678 Droppables.add(element, options_for_tree);
469 679 options.droppables.push(element);
470 680 }
471 681
472 682 (this.findElements(element, options) || []).each( function(e) {
473 683 // handles are per-draggable
474 684 var handle = options.handle ?
475 Element.childrenWithClassName(e, options.handle)[0] : e;
685 $(e).down('.'+options.handle,0) : e;
476 686 options.draggables.push(
477 687 new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
478 688 Droppables.add(e, options_for_droppable);
689 if(options.tree) e.treeNode = element;
479 690 options.droppables.push(e);
480 691 });
692
693 if(options.tree) {
694 (Sortable.findTreeElements(element, options) || []).each( function(e) {
695 Droppables.add(e, options_for_tree);
696 e.treeNode = element;
697 options.droppables.push(e);
698 });
699 }
481 700
482 701 // keep reference
483 this.sortables.push(options);
702 this.sortables[element.id] = options;
484 703
485 704 // for onupdate
486 705 Draggables.addObserver(new SortableObserver(element, options.onUpdate));
@@ -489,23 +708,21 var Sortable = {
489 708
490 709 // return all suitable-for-sortable elements in a guaranteed order
491 710 findElements: function(element, options) {
492 if(!element.hasChildNodes()) return null;
493 var elements = [];
494 $A(element.childNodes).each( function(e) {
495 if(e.tagName && e.tagName.toUpperCase()==options.tag.toUpperCase() &&
496 (!options.only || (Element.hasClassName(e, options.only))))
497 elements.push(e);
498 if(options.tree) {
499 var grandchildren = this.findElements(e, options);
500 if(grandchildren) elements.push(grandchildren);
501 }
502 });
503
504 return (elements.length>0 ? elements.flatten() : null);
711 return Element.findChildren(
712 element, options.only, options.tree ? true : false, options.tag);
713 },
714
715 findTreeElements: function(element, options) {
716 return Element.findChildren(
717 element, options.only, options.tree ? true : false, options.treeTag);
505 718 },
506 719
507 720 onHover: function(element, dropon, overlap) {
508 if(overlap>0.5) {
721 if(Element.isParent(dropon, element)) return;
722
723 if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
724 return;
725 } else if(overlap>0.5) {
509 726 Sortable.mark(dropon, 'before');
510 727 if(dropon.previousSibling != element) {
511 728 var oldParentNode = element.parentNode;
@@ -528,18 +745,42 var Sortable = {
528 745 }
529 746 }
530 747 },
531
532 onEmptyHover: function(element, dropon) {
533 if(element.parentNode!=dropon) {
534 var oldParentNode = element.parentNode;
535 dropon.appendChild(element);
748
749 onEmptyHover: function(element, dropon, overlap) {
750 var oldParentNode = element.parentNode;
751 var droponOptions = Sortable.options(dropon);
752
753 if(!Element.isParent(dropon, element)) {
754 var index;
755
756 var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
757 var child = null;
758
759 if(children) {
760 var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
761
762 for (index = 0; index < children.length; index += 1) {
763 if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
764 offset -= Element.offsetSize (children[index], droponOptions.overlap);
765 } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
766 child = index + 1 < children.length ? children[index + 1] : null;
767 break;
768 } else {
769 child = children[index];
770 break;
771 }
772 }
773 }
774
775 dropon.insertBefore(element, child);
776
536 777 Sortable.options(oldParentNode).onChange(element);
537 Sortable.options(dropon).onChange(element);
778 droponOptions.onChange(element);
538 779 }
539 780 },
540 781
541 782 unmark: function() {
542 if(Sortable._marker) Element.hide(Sortable._marker);
783 if(Sortable._marker) Sortable._marker.hide();
543 784 },
544 785
545 786 mark: function(dropon, position) {
@@ -548,37 +789,154 var Sortable = {
548 789 if(sortable && !sortable.ghosting) return;
549 790
550 791 if(!Sortable._marker) {
551 Sortable._marker = $('dropmarker') || document.createElement('DIV');
552 Element.hide(Sortable._marker);
553 Element.addClassName(Sortable._marker, 'dropmarker');
554 Sortable._marker.style.position = 'absolute';
792 Sortable._marker =
793 ($('dropmarker') || Element.extend(document.createElement('DIV'))).
794 hide().addClassName('dropmarker').setStyle({position:'absolute'});
555 795 document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
556 796 }
557 797 var offsets = Position.cumulativeOffset(dropon);
558 Sortable._marker.style.left = offsets[0] + 'px';
559 Sortable._marker.style.top = offsets[1] + 'px';
798 Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
560 799
561 800 if(position=='after')
562 801 if(sortable.overlap == 'horizontal')
563 Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
802 Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
564 803 else
565 Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
804 Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
566 805
567 Element.show(Sortable._marker);
806 Sortable._marker.show();
568 807 },
808
809 _tree: function(element, options, parent) {
810 var children = Sortable.findElements(element, options) || [];
811
812 for (var i = 0; i < children.length; ++i) {
813 var match = children[i].id.match(options.format);
569 814
570 serialize: function(element) {
815 if (!match) continue;
816
817 var child = {
818 id: encodeURIComponent(match ? match[1] : null),
819 element: element,
820 parent: parent,
821 children: [],
822 position: parent.children.length,
823 container: $(children[i]).down(options.treeTag)
824 }
825
826 /* Get the element containing the children and recurse over it */
827 if (child.container)
828 this._tree(child.container, options, child)
829
830 parent.children.push (child);
831 }
832
833 return parent;
834 },
835
836 tree: function(element) {
571 837 element = $(element);
572 838 var sortableOptions = this.options(element);
573 839 var options = Object.extend({
574 tag: sortableOptions.tag,
840 tag: sortableOptions.tag,
841 treeTag: sortableOptions.treeTag,
575 842 only: sortableOptions.only,
576 843 name: element.id,
577 format: sortableOptions.format || /^[^_]*_(.*)$/
844 format: sortableOptions.format
578 845 }, arguments[1] || {});
846
847 var root = {
848 id: null,
849 parent: null,
850 children: [],
851 container: element,
852 position: 0
853 }
854
855 return Sortable._tree(element, options, root);
856 },
857
858 /* Construct a [i] index for a particular node */
859 _constructIndex: function(node) {
860 var index = '';
861 do {
862 if (node.id) index = '[' + node.position + ']' + index;
863 } while ((node = node.parent) != null);
864 return index;
865 },
866
867 sequence: function(element) {
868 element = $(element);
869 var options = Object.extend(this.options(element), arguments[1] || {});
870
579 871 return $(this.findElements(element, options) || []).map( function(item) {
580 return (encodeURIComponent(options.name) + "[]=" +
581 encodeURIComponent(item.id.match(options.format) ? item.id.match(options.format)[1] : ''));
582 }).join("&");
872 return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
873 });
874 },
875
876 setSequence: function(element, new_sequence) {
877 element = $(element);
878 var options = Object.extend(this.options(element), arguments[2] || {});
879
880 var nodeMap = {};
881 this.findElements(element, options).each( function(n) {
882 if (n.id.match(options.format))
883 nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
884 n.parentNode.removeChild(n);
885 });
886
887 new_sequence.each(function(ident) {
888 var n = nodeMap[ident];
889 if (n) {
890 n[1].appendChild(n[0]);
891 delete nodeMap[ident];
892 }
893 });
894 },
895
896 serialize: function(element) {
897 element = $(element);
898 var options = Object.extend(Sortable.options(element), arguments[1] || {});
899 var name = encodeURIComponent(
900 (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
901
902 if (options.tree) {
903 return Sortable.tree(element, arguments[1]).children.map( function (item) {
904 return [name + Sortable._constructIndex(item) + "[id]=" +
905 encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
906 }).flatten().join('&');
907 } else {
908 return Sortable.sequence(element, arguments[1]).map( function(item) {
909 return name + "[]=" + encodeURIComponent(item);
910 }).join('&');
911 }
583 912 }
584 } No newline at end of file
913 }
914
915 // Returns true if child is contained within element
916 Element.isParent = function(child, element) {
917 if (!child.parentNode || child == element) return false;
918 if (child.parentNode == element) return true;
919 return Element.isParent(child.parentNode, element);
920 }
921
922 Element.findChildren = function(element, only, recursive, tagName) {
923 if(!element.hasChildNodes()) return null;
924 tagName = tagName.toUpperCase();
925 if(only) only = [only].flatten();
926 var elements = [];
927 $A(element.childNodes).each( function(e) {
928 if(e.tagName && e.tagName.toUpperCase()==tagName &&
929 (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
930 elements.push(e);
931 if(recursive) {
932 var grandchildren = Element.findChildren(e, only, recursive, tagName);
933 if(grandchildren) elements.push(grandchildren);
934 }
935 });
936
937 return (elements.length>0 ? elements.flatten() : []);
938 }
939
940 Element.offsetSize = function (element, type) {
941 return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
942 }
This diff has been collapsed as it changes many lines, (800 lines changed) Show them Hide them
@@ -1,17 +1,16
1 // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
1 // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 2 // Contributors:
3 3 // Justin Palmer (http://encytemedia.com/)
4 4 // Mark Pilgrim (http://diveintomark.org/)
5 5 // Martin Bialasinki
6 6 //
7 // See scriptaculous.js for full license.
7 // script.aculo.us is freely distributable under the terms of an MIT-style license.
8 // For details, see the script.aculo.us web site: http://script.aculo.us/
8 9
9 /* ------------- element ext -------------- */
10
11 10 // converts rgb() and #xxx to #xxxxxx format,
12 11 // returns self (or first argument) if not convertable
13 12 String.prototype.parseColor = function() {
14 var color = '#';
13 var color = '#';
15 14 if(this.slice(0,4) == 'rgb(') {
16 15 var cols = this.slice(4,this.length-1).split(',');
17 16 var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
@@ -22,40 +21,38 String.prototype.parseColor = function() {
22 21 }
23 22 }
24 23 return(color.length==7 ? color : (arguments[0] || this));
25 }
24 }
26 25
27 Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
28 var children = $(element).childNodes;
29 var text = '';
30 var classtest = new RegExp('^([^ ]+ )*' + ignoreclass+ '( [^ ]+)*$','i');
31
32 for (var i = 0; i < children.length; i++) {
33 if(children[i].nodeType==3) {
34 text+=children[i].nodeValue;
35 } else {
36 if((!children[i].className.match(classtest)) && children[i].hasChildNodes())
37 text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass);
38 }
39 }
40
41 return text;
26 /*--------------------------------------------------------------------------*/
27
28 Element.collectTextNodes = function(element) {
29 return $A($(element).childNodes).collect( function(node) {
30 return (node.nodeType==3 ? node.nodeValue :
31 (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
32 }).flatten().join('');
42 33 }
43 34
44 Element.setStyle = function(element, style) {
45 element = $(element);
46 for(k in style) element.style[k.camelize()] = style[k];
35 Element.collectTextNodesIgnoreClass = function(element, className) {
36 return $A($(element).childNodes).collect( function(node) {
37 return (node.nodeType==3 ? node.nodeValue :
38 ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
39 Element.collectTextNodesIgnoreClass(node, className) : ''));
40 }).flatten().join('');
47 41 }
48 42
49 Element.setContentZoom = function(element, percent) {
50 Element.setStyle(element, {fontSize: (percent/100) + 'em'});
51 if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
43 Element.setContentZoom = function(element, percent) {
44 element = $(element);
45 element.setStyle({fontSize: (percent/100) + 'em'});
46 if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
47 return element;
52 48 }
53 49
54 Element.getOpacity = function(element){
50 Element.getOpacity = function(element){
51 element = $(element);
55 52 var opacity;
56 if (opacity = Element.getStyle(element, 'opacity'))
53 if (opacity = element.getStyle('opacity'))
57 54 return parseFloat(opacity);
58 if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
55 if (opacity = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
59 56 if(opacity[1]) return parseFloat(opacity[1]) / 100;
60 57 return 1.0;
61 58 }
@@ -63,29 +60,36 Element.getOpacity = function(element){
63 60 Element.setOpacity = function(element, value){
64 61 element= $(element);
65 62 if (value == 1){
66 Element.setStyle(element, { opacity:
63 element.setStyle({ opacity:
67 64 (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
68 0.999999 : null });
69 if(/MSIE/.test(navigator.userAgent))
70 Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
65 0.999999 : 1.0 });
66 if(/MSIE/.test(navigator.userAgent) && !window.opera)
67 element.setStyle({filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
71 68 } else {
72 69 if(value < 0.00001) value = 0;
73 Element.setStyle(element, {opacity: value});
74 if(/MSIE/.test(navigator.userAgent))
75 Element.setStyle(element,
76 { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
77 'alpha(opacity='+value*100+')' });
78 }
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;
79 77 }
80 78
81 79 Element.getInlineOpacity = function(element){
82 80 return $(element).style.opacity || '';
83 81 }
84 82
85 Element.childrenWithClassName = function(element, className) {
86 return $A($(element).getElementsByTagName('*')).select(
87 function(c) { return Element.hasClassName(c, className) });
88 }
83 Element.forceRerendering = function(element) {
84 try {
85 element = $(element);
86 var n = document.createTextNode(' ');
87 element.appendChild(n);
88 element.removeChild(n);
89 } catch(e) { }
90 };
91
92 /*--------------------------------------------------------------------------*/
89 93
90 94 Array.prototype.call = function() {
91 95 var args = arguments;
@@ -95,9 +99,17 Array.prototype.call = function() {
95 99 /*--------------------------------------------------------------------------*/
96 100
97 101 var Effect = {
102 _elementDoesNotExistError: {
103 name: 'ElementDoesNotExistError',
104 message: 'The specified DOM element does not exist, but is required for this effect to operate'
105 },
98 106 tagifyText: function(element) {
107 if(typeof Builder == 'undefined')
108 throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
109
99 110 var tagifyStyle = 'position:relative';
100 if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
111 if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
112
101 113 element = $(element);
102 114 $A(element.childNodes).each( function(child) {
103 115 if(child.nodeType==3) {
@@ -129,6 +141,20 var Effect = {
129 141 $A(elements).each( function(element, index) {
130 142 new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
131 143 });
144 },
145 PAIRS: {
146 'slide': ['SlideDown','SlideUp'],
147 'blind': ['BlindDown','BlindUp'],
148 'appear': ['Appear','Fade']
149 },
150 toggle: function(element, effect) {
151 element = $(element);
152 effect = (effect || 'appear').toLowerCase();
153 var options = Object.extend({
154 queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
155 }, arguments[2] || {});
156 Effect[element.visible() ?
157 Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
132 158 }
133 159 };
134 160
@@ -136,46 +162,54 var Effect2 = Effect; // deprecated
136 162
137 163 /* ------------- transitions ------------- */
138 164
139 Effect.Transitions = {}
140
141 Effect.Transitions.linear = function(pos) {
142 return pos;
143 }
144 Effect.Transitions.sinoidal = function(pos) {
145 return (-Math.cos(pos*Math.PI)/2) + 0.5;
146 }
147 Effect.Transitions.reverse = function(pos) {
148 return 1-pos;
149 }
150 Effect.Transitions.flicker = function(pos) {
151 return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
152 }
153 Effect.Transitions.wobble = function(pos) {
154 return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
155 }
156 Effect.Transitions.pulse = function(pos) {
157 return (Math.floor(pos*10) % 2 == 0 ?
158 (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
159 }
160 Effect.Transitions.none = function(pos) {
161 return 0;
162 }
163 Effect.Transitions.full = function(pos) {
164 return 1;
165 }
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 };
166 194
167 195 /* ------------- core effects ------------- */
168 196
169 Effect.Queue = {
170 effects: [],
197 Effect.ScopedQueue = Class.create();
198 Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
199 initialize: function() {
200 this.effects = [];
201 this.interval = null;
202 },
171 203 _each: function(iterator) {
172 204 this.effects._each(iterator);
173 205 },
174 interval: null,
175 206 add: function(effect) {
176 207 var timestamp = new Date().getTime();
177 208
178 switch(effect.options.queue) {
209 var position = (typeof effect.options.queue == 'string') ?
210 effect.options.queue : effect.options.queue.position;
211
212 switch(position) {
179 213 case 'front':
180 214 // move unstarted effects after this effect
181 215 this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
@@ -183,6 +217,9 Effect.Queue = {
183 217 e.finishOn += effect.finishOn;
184 218 });
185 219 break;
220 case 'with-last':
221 timestamp = this.effects.pluck('startOn').max() || timestamp;
222 break;
186 223 case 'end':
187 224 // start effect after last queued effect has finished
188 225 timestamp = this.effects.pluck('finishOn').max() || timestamp;
@@ -191,7 +228,10 Effect.Queue = {
191 228
192 229 effect.startOn += timestamp;
193 230 effect.finishOn += timestamp;
194 this.effects.push(effect);
231
232 if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
233 this.effects.push(effect);
234
195 235 if(!this.interval)
196 236 this.interval = setInterval(this.loop.bind(this), 40);
197 237 },
@@ -206,32 +246,45 Effect.Queue = {
206 246 var timePos = new Date().getTime();
207 247 this.effects.invoke('loop', timePos);
208 248 }
249 });
250
251 Effect.Queues = {
252 instances: $H(),
253 get: function(queueName) {
254 if(typeof queueName != 'string') return queueName;
255
256 if(!this.instances[queueName])
257 this.instances[queueName] = new Effect.ScopedQueue();
258
259 return this.instances[queueName];
260 }
261 }
262 Effect.Queue = Effect.Queues.get('global');
263
264 Effect.DefaultOptions = {
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'
209 273 }
210 Object.extend(Effect.Queue, Enumerable);
211 274
212 275 Effect.Base = function() {};
213 276 Effect.Base.prototype = {
214 277 position: null,
215 setOptions: function(options) {
216 this.options = Object.extend({
217 transition: Effect.Transitions.sinoidal,
218 duration: 1.0, // seconds
219 fps: 25.0, // max. 25fps due to Effect.Queue implementation
220 sync: false, // true for combining
221 from: 0.0,
222 to: 1.0,
223 delay: 0.0,
224 queue: 'parallel'
225 }, options || {});
226 },
227 278 start: function(options) {
228 this.setOptions(options || {});
279 this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
229 280 this.currentFrame = 0;
230 281 this.state = 'idle';
231 282 this.startOn = this.options.delay*1000;
232 283 this.finishOn = this.startOn + (this.options.duration*1000);
233 284 this.event('beforeStart');
234 if(!this.options.sync) Effect.Queue.add(this);
285 if(!this.options.sync)
286 Effect.Queues.get(typeof this.options.queue == 'string' ?
287 'global' : this.options.queue.scope).add(this);
235 288 },
236 289 loop: function(timePos) {
237 290 if(timePos >= this.startOn) {
@@ -269,7 +322,9 Effect.Base.prototype = {
269 322 }
270 323 },
271 324 cancel: function() {
272 if(!this.options.sync) Effect.Queue.remove(this);
325 if(!this.options.sync)
326 Effect.Queues.get(typeof this.options.queue == 'string' ?
327 'global' : this.options.queue.scope).remove(this);
273 328 this.state = 'finished';
274 329 },
275 330 event: function(eventName) {
@@ -301,53 +356,81 Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
301 356 }
302 357 });
303 358
359 Effect.Event = Class.create();
360 Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
361 initialize: function() {
362 var options = Object.extend({
363 duration: 0
364 }, arguments[0] || {});
365 this.start(options);
366 },
367 update: Prototype.emptyFunction
368 });
369
304 370 Effect.Opacity = Class.create();
305 371 Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
306 372 initialize: function(element) {
307 373 this.element = $(element);
374 if(!this.element) throw(Effect._elementDoesNotExistError);
308 375 // make this work on IE on elements without 'layout'
309 if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
310 Element.setStyle(this.element, {zoom: 1});
376 if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
377 this.element.setStyle({zoom: 1});
311 378 var options = Object.extend({
312 from: Element.getOpacity(this.element) || 0.0,
379 from: this.element.getOpacity() || 0.0,
313 380 to: 1.0
314 381 }, arguments[1] || {});
315 382 this.start(options);
316 383 },
317 384 update: function(position) {
318 Element.setOpacity(this.element, position);
385 this.element.setOpacity(position);
319 386 }
320 387 });
321 388
322 Effect.MoveBy = Class.create();
323 Object.extend(Object.extend(Effect.MoveBy.prototype, Effect.Base.prototype), {
324 initialize: function(element, toTop, toLeft) {
325 this.element = $(element);
326 this.toTop = toTop;
327 this.toLeft = toLeft;
328 this.start(arguments[3]);
389 Effect.Move = Class.create();
390 Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
391 initialize: function(element) {
392 this.element = $(element);
393 if(!this.element) throw(Effect._elementDoesNotExistError);
394 var options = Object.extend({
395 x: 0,
396 y: 0,
397 mode: 'relative'
398 }, arguments[1] || {});
399 this.start(options);
329 400 },
330 401 setup: function() {
331 402 // Bug in Opera: Opera returns the "real" position of a static element or
332 403 // relative element that does not have top/left explicitly set.
333 404 // ==> Always set top and left for position relative elements in your stylesheets
334 405 // (to 0 if you do not need them)
335 Element.makePositioned(this.element);
336 this.originalTop = parseFloat(Element.getStyle(this.element,'top') || '0');
337 this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0');
406 this.element.makePositioned();
407 this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
408 this.originalTop = parseFloat(this.element.getStyle('top') || '0');
409 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;
412 this.options.y = this.options.y - this.originalTop;
413 }
338 414 },
339 415 update: function(position) {
340 Element.setStyle(this.element, {
341 top: this.toTop * position + this.originalTop + 'px',
342 left: this.toLeft * position + this.originalLeft + 'px'
416 this.element.setStyle({
417 left: Math.round(this.options.x * position + this.originalLeft) + 'px',
418 top: Math.round(this.options.y * position + this.originalTop) + 'px'
343 419 });
344 420 }
345 421 });
346 422
423 // for backwards compatibility
424 Effect.MoveBy = function(element, toTop, toLeft) {
425 return new Effect.Move(element,
426 Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
427 };
428
347 429 Effect.Scale = Class.create();
348 430 Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
349 431 initialize: function(element, percent) {
350 this.element = $(element)
432 this.element = $(element);
433 if(!this.element) throw(Effect._elementDoesNotExistError);
351 434 var options = Object.extend({
352 435 scaleX: true,
353 436 scaleY: true,
@@ -361,7 +444,7 Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
361 444 },
362 445 setup: function() {
363 446 this.restoreAfterFinish = this.options.restoreAfterFinish || false;
364 this.elementPositioning = Element.getStyle(this.element,'position');
447 this.elementPositioning = this.element.getStyle('position');
365 448
366 449 this.originalStyle = {};
367 450 ['top','left','width','height','fontSize'].each( function(k) {
@@ -371,8 +454,8 Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
371 454 this.originalTop = this.element.offsetTop;
372 455 this.originalLeft = this.element.offsetLeft;
373 456
374 var fontSize = Element.getStyle(this.element,'font-size') || '100%';
375 ['em','px','%'].each( function(fontSizeType) {
457 var fontSize = this.element.getStyle('font-size') || '100%';
458 ['em','px','%','pt'].each( function(fontSizeType) {
376 459 if(fontSize.indexOf(fontSizeType)>0) {
377 460 this.fontSize = parseFloat(fontSize);
378 461 this.fontSizeType = fontSizeType;
@@ -393,16 +476,16 Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
393 476 update: function(position) {
394 477 var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
395 478 if(this.options.scaleContent && this.fontSize)
396 Element.setStyle(this.element, {fontSize: this.fontSize * currentScale + this.fontSizeType });
479 this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
397 480 this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
398 481 },
399 482 finish: function(position) {
400 if (this.restoreAfterFinish) Element.setStyle(this.element, this.originalStyle);
483 if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
401 484 },
402 485 setDimensions: function(height, width) {
403 486 var d = {};
404 if(this.options.scaleX) d.width = width + 'px';
405 if(this.options.scaleY) d.height = height + 'px';
487 if(this.options.scaleX) d.width = Math.round(width) + 'px';
488 if(this.options.scaleY) d.height = Math.round(height) + 'px';
406 489 if(this.options.scaleFromCenter) {
407 490 var topd = (height - this.dims[0])/2;
408 491 var leftd = (width - this.dims[1])/2;
@@ -414,7 +497,7 Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
414 497 if(this.options.scaleX) d.left = -leftd + 'px';
415 498 }
416 499 }
417 Element.setStyle(this.element, d);
500 this.element.setStyle(d);
418 501 }
419 502 });
420 503
@@ -422,30 +505,31 Effect.Highlight = Class.create();
422 505 Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
423 506 initialize: function(element) {
424 507 this.element = $(element);
508 if(!this.element) throw(Effect._elementDoesNotExistError);
425 509 var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
426 510 this.start(options);
427 511 },
428 512 setup: function() {
429 513 // Prevent executing on elements not in the layout flow
430 if(Element.getStyle(this.element, 'display')=='none') { this.cancel(); return; }
514 if(this.element.getStyle('display')=='none') { this.cancel(); return; }
431 515 // Disable background image during the effect
432 516 this.oldStyle = {
433 backgroundImage: Element.getStyle(this.element, 'background-image') };
434 Element.setStyle(this.element, {backgroundImage: 'none'});
517 backgroundImage: this.element.getStyle('background-image') };
518 this.element.setStyle({backgroundImage: 'none'});
435 519 if(!this.options.endcolor)
436 this.options.endcolor = Element.getStyle(this.element, 'background-color').parseColor('#ffffff');
520 this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
437 521 if(!this.options.restorecolor)
438 this.options.restorecolor = Element.getStyle(this.element, 'background-color');
522 this.options.restorecolor = this.element.getStyle('background-color');
439 523 // init color calculations
440 524 this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
441 525 this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
442 526 },
443 527 update: function(position) {
444 Element.setStyle(this.element,{backgroundColor: $R(0,2).inject('#',function(m,v,i){
528 this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
445 529 return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
446 530 },
447 531 finish: function() {
448 Element.setStyle(this.element, Object.extend(this.oldStyle, {
532 this.element.setStyle(Object.extend(this.oldStyle, {
449 533 backgroundColor: this.options.restorecolor
450 534 }));
451 535 }
@@ -479,86 +563,93 Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
479 563 /* ------------- combination effects ------------- */
480 564
481 565 Effect.Fade = function(element) {
482 var oldOpacity = Element.getInlineOpacity(element);
566 element = $(element);
567 var oldOpacity = element.getInlineOpacity();
483 568 var options = Object.extend({
484 from: Element.getOpacity(element) || 1.0,
569 from: element.getOpacity() || 1.0,
485 570 to: 0.0,
486 afterFinishInternal: function(effect) { with(Element) {
571 afterFinishInternal: function(effect) {
487 572 if(effect.options.to!=0) return;
488 hide(effect.element);
489 setStyle(effect.element, {opacity: oldOpacity}); }}
490 }, arguments[1] || {});
573 effect.element.hide().setStyle({opacity: oldOpacity});
574 }}, arguments[1] || {});
491 575 return new Effect.Opacity(element,options);
492 576 }
493 577
494 578 Effect.Appear = function(element) {
579 element = $(element);
495 580 var options = Object.extend({
496 from: (Element.getStyle(element, 'display') == 'none' ? 0.0 : Element.getOpacity(element) || 0.0),
581 from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
497 582 to: 1.0,
498 beforeSetup: function(effect) { with(Element) {
499 setOpacity(effect.element, effect.options.from);
500 show(effect.element); }}
501 }, arguments[1] || {});
583 // force Safari to render floated elements properly
584 afterFinishInternal: function(effect) {
585 effect.element.forceRerendering();
586 },
587 beforeSetup: function(effect) {
588 effect.element.setOpacity(effect.options.from).show();
589 }}, arguments[1] || {});
502 590 return new Effect.Opacity(element,options);
503 591 }
504 592
505 593 Effect.Puff = function(element) {
506 594 element = $(element);
507 var oldStyle = { opacity: Element.getInlineOpacity(element), position: Element.getStyle(element, 'position') };
595 var oldStyle = {
596 opacity: element.getInlineOpacity(),
597 position: element.getStyle('position'),
598 top: element.style.top,
599 left: element.style.left,
600 width: element.style.width,
601 height: element.style.height
602 };
508 603 return new Effect.Parallel(
509 604 [ new Effect.Scale(element, 200,
510 605 { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
511 606 new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
512 607 Object.extend({ duration: 1.0,
513 beforeSetupInternal: function(effect) { with(Element) {
514 setStyle(effect.effects[0].element, {position: 'absolute'}); }},
515 afterFinishInternal: function(effect) { with(Element) {
516 hide(effect.effects[0].element);
517 setStyle(effect.effects[0].element, oldStyle); }}
608 beforeSetupInternal: function(effect) {
609 Position.absolutize(effect.effects[0].element)
610 },
611 afterFinishInternal: function(effect) {
612 effect.effects[0].element.hide().setStyle(oldStyle); }
518 613 }, arguments[1] || {})
519 614 );
520 615 }
521 616
522 617 Effect.BlindUp = function(element) {
523 618 element = $(element);
524 Element.makeClipping(element);
525 return new Effect.Scale(element, 0,
619 element.makeClipping();
620 return new Effect.Scale(element, 0,
526 621 Object.extend({ scaleContent: false,
527 622 scaleX: false,
528 623 restoreAfterFinish: true,
529 afterFinishInternal: function(effect) { with(Element) {
530 [hide, undoClipping].call(effect.element); }}
624 afterFinishInternal: function(effect) {
625 effect.element.hide().undoClipping();
626 }
531 627 }, arguments[1] || {})
532 628 );
533 629 }
534 630
535 631 Effect.BlindDown = function(element) {
536 632 element = $(element);
537 var oldHeight = Element.getStyle(element, 'height');
538 var elementDimensions = Element.getDimensions(element);
539 return new Effect.Scale(element, 100,
540 Object.extend({ scaleContent: false,
541 scaleX: false,
542 scaleFrom: 0,
543 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
544 restoreAfterFinish: true,
545 afterSetup: function(effect) { with(Element) {
546 makeClipping(effect.element);
547 setStyle(effect.element, {height: '0px'});
548 show(effect.element);
549 }},
550 afterFinishInternal: function(effect) { with(Element) {
551 undoClipping(effect.element);
552 setStyle(effect.element, {height: oldHeight});
553 }}
554 }, arguments[1] || {})
555 );
633 var elementDimensions = element.getDimensions();
634 return new Effect.Scale(element, 100, Object.extend({
635 scaleContent: false,
636 scaleX: false,
637 scaleFrom: 0,
638 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
639 restoreAfterFinish: true,
640 afterSetup: function(effect) {
641 effect.element.makeClipping().setStyle({height: '0px'}).show();
642 },
643 afterFinishInternal: function(effect) {
644 effect.element.undoClipping();
645 }
646 }, arguments[1] || {}));
556 647 }
557 648
558 649 Effect.SwitchOff = function(element) {
559 650 element = $(element);
560 var oldOpacity = Element.getInlineOpacity(element);
561 return new Effect.Appear(element, {
651 var oldOpacity = element.getInlineOpacity();
652 return new Effect.Appear(element, Object.extend({
562 653 duration: 0.4,
563 654 from: 0,
564 655 transition: Effect.Transitions.flicker,
@@ -566,127 +657,123 Effect.SwitchOff = function(element) {
566 657 new Effect.Scale(effect.element, 1, {
567 658 duration: 0.3, scaleFromCenter: true,
568 659 scaleX: false, scaleContent: false, restoreAfterFinish: true,
569 beforeSetup: function(effect) { with(Element) {
570 [makePositioned,makeClipping].call(effect.element);
571 }},
572 afterFinishInternal: function(effect) { with(Element) {
573 [hide,undoClipping,undoPositioned].call(effect.element);
574 setStyle(effect.element, {opacity: oldOpacity});
575 }}
660 beforeSetup: function(effect) {
661 effect.element.makePositioned().makeClipping();
662 },
663 afterFinishInternal: function(effect) {
664 effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
665 }
576 666 })
577 667 }
578 });
668 }, arguments[1] || {}));
579 669 }
580 670
581 671 Effect.DropOut = function(element) {
582 672 element = $(element);
583 673 var oldStyle = {
584 top: Element.getStyle(element, 'top'),
585 left: Element.getStyle(element, 'left'),
586 opacity: Element.getInlineOpacity(element) };
674 top: element.getStyle('top'),
675 left: element.getStyle('left'),
676 opacity: element.getInlineOpacity() };
587 677 return new Effect.Parallel(
588 [ new Effect.MoveBy(element, 100, 0, { sync: true }),
678 [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
589 679 new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
590 680 Object.extend(
591 681 { duration: 0.5,
592 beforeSetup: function(effect) { with(Element) {
593 makePositioned(effect.effects[0].element); }},
594 afterFinishInternal: function(effect) { with(Element) {
595 [hide, undoPositioned].call(effect.effects[0].element);
596 setStyle(effect.effects[0].element, oldStyle); }}
682 beforeSetup: function(effect) {
683 effect.effects[0].element.makePositioned();
684 },
685 afterFinishInternal: function(effect) {
686 effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
687 }
597 688 }, arguments[1] || {}));
598 689 }
599 690
600 691 Effect.Shake = function(element) {
601 692 element = $(element);
602 693 var oldStyle = {
603 top: Element.getStyle(element, 'top'),
604 left: Element.getStyle(element, 'left') };
605 return new Effect.MoveBy(element, 0, 20,
606 { duration: 0.05, afterFinishInternal: function(effect) {
607 new Effect.MoveBy(effect.element, 0, -40,
608 { duration: 0.1, afterFinishInternal: function(effect) {
609 new Effect.MoveBy(effect.element, 0, 40,
610 { duration: 0.1, afterFinishInternal: function(effect) {
611 new Effect.MoveBy(effect.element, 0, -40,
612 { duration: 0.1, afterFinishInternal: function(effect) {
613 new Effect.MoveBy(effect.element, 0, 40,
614 { duration: 0.1, afterFinishInternal: function(effect) {
615 new Effect.MoveBy(effect.element, 0, -20,
616 { duration: 0.05, afterFinishInternal: function(effect) { with(Element) {
617 undoPositioned(effect.element);
618 setStyle(effect.element, oldStyle);
619 }}}) }}) }}) }}) }}) }});
694 top: element.getStyle('top'),
695 left: element.getStyle('left') };
696 return new Effect.Move(element,
697 { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
698 new Effect.Move(effect.element,
699 { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
700 new Effect.Move(effect.element,
701 { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
702 new Effect.Move(effect.element,
703 { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
704 new Effect.Move(effect.element,
705 { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
706 new Effect.Move(effect.element,
707 { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
708 effect.element.undoPositioned().setStyle(oldStyle);
709 }}) }}) }}) }}) }}) }});
620 710 }
621 711
622 712 Effect.SlideDown = function(element) {
623 element = $(element);
624 Element.cleanWhitespace(element);
713 element = $(element).cleanWhitespace();
625 714 // SlideDown need to have the content of the element wrapped in a container element with fixed height!
626 var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
627 var elementDimensions = Element.getDimensions(element);
715 var oldInnerBottom = element.down().getStyle('bottom');
716 var elementDimensions = element.getDimensions();
628 717 return new Effect.Scale(element, 100, Object.extend({
629 718 scaleContent: false,
630 719 scaleX: false,
631 scaleFrom: 0,
720 scaleFrom: window.opera ? 0 : 1,
632 721 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
633 722 restoreAfterFinish: true,
634 afterSetup: function(effect) { with(Element) {
635 makePositioned(effect.element);
636 makePositioned(effect.element.firstChild);
637 if(window.opera) setStyle(effect.element, {top: ''});
638 makeClipping(effect.element);
639 setStyle(effect.element, {height: '0px'});
640 show(element); }},
641 afterUpdateInternal: function(effect) { with(Element) {
642 setStyle(effect.element.firstChild, {bottom:
643 (effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
644 afterFinishInternal: function(effect) { with(Element) {
645 undoClipping(effect.element);
646 undoPositioned(effect.element.firstChild);
647 undoPositioned(effect.element);
648 setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
723 afterSetup: function(effect) {
724 effect.element.makePositioned();
725 effect.element.down().makePositioned();
726 if(window.opera) effect.element.setStyle({top: ''});
727 effect.element.makeClipping().setStyle({height: '0px'}).show();
728 },
729 afterUpdateInternal: function(effect) {
730 effect.element.down().setStyle({bottom:
731 (effect.dims[0] - effect.element.clientHeight) + 'px' });
732 },
733 afterFinishInternal: function(effect) {
734 effect.element.undoClipping().undoPositioned();
735 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
649 736 }, arguments[1] || {})
650 737 );
651 738 }
652
739
653 740 Effect.SlideUp = function(element) {
654 element = $(element);
655 Element.cleanWhitespace(element);
656 var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
657 return new Effect.Scale(element, 0,
741 element = $(element).cleanWhitespace();
742 var oldInnerBottom = element.down().getStyle('bottom');
743 return new Effect.Scale(element, window.opera ? 0 : 1,
658 744 Object.extend({ scaleContent: false,
659 745 scaleX: false,
660 746 scaleMode: 'box',
661 747 scaleFrom: 100,
662 748 restoreAfterFinish: true,
663 beforeStartInternal: function(effect) { with(Element) {
664 makePositioned(effect.element);
665 makePositioned(effect.element.firstChild);
666 if(window.opera) setStyle(effect.element, {top: ''});
667 makeClipping(effect.element);
668 show(element); }},
669 afterUpdateInternal: function(effect) { with(Element) {
670 setStyle(effect.element.firstChild, {bottom:
671 (effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
672 afterFinishInternal: function(effect) { with(Element) {
673 [hide, undoClipping].call(effect.element);
674 undoPositioned(effect.element.firstChild);
675 undoPositioned(effect.element);
676 setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
749 beforeStartInternal: function(effect) {
750 effect.element.makePositioned();
751 effect.element.down().makePositioned();
752 if(window.opera) effect.element.setStyle({top: ''});
753 effect.element.makeClipping().show();
754 },
755 afterUpdateInternal: function(effect) {
756 effect.element.down().setStyle({bottom:
757 (effect.dims[0] - effect.element.clientHeight) + 'px' });
758 },
759 afterFinishInternal: function(effect) {
760 effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
761 effect.element.down().undoPositioned();
762 }
677 763 }, arguments[1] || {})
678 764 );
679 765 }
680 766
681 767 // Bug in opera makes the TD containing this element expand for a instance after finish
682 768 Effect.Squish = function(element) {
683 return new Effect.Scale(element, window.opera ? 1 : 0,
684 { restoreAfterFinish: true,
685 beforeSetup: function(effect) { with(Element) {
686 makeClipping(effect.element); }},
687 afterFinishInternal: function(effect) { with(Element) {
688 hide(effect.element);
689 undoClipping(effect.element); }}
769 return new Effect.Scale(element, window.opera ? 1 : 0, {
770 restoreAfterFinish: true,
771 beforeSetup: function(effect) {
772 effect.element.makeClipping();
773 },
774 afterFinishInternal: function(effect) {
775 effect.element.hide().undoClipping();
776 }
690 777 });
691 778 }
692 779
@@ -694,7 +781,7 Effect.Grow = function(element) {
694 781 element = $(element);
695 782 var options = Object.extend({
696 783 direction: 'center',
697 moveTransistion: Effect.Transitions.sinoidal,
784 moveTransition: Effect.Transitions.sinoidal,
698 785 scaleTransition: Effect.Transitions.sinoidal,
699 786 opacityTransition: Effect.Transitions.full
700 787 }, arguments[1] || {});
@@ -703,9 +790,9 Effect.Grow = function(element) {
703 790 left: element.style.left,
704 791 height: element.style.height,
705 792 width: element.style.width,
706 opacity: Element.getInlineOpacity(element) };
793 opacity: element.getInlineOpacity() };
707 794
708 var dims = Element.getDimensions(element);
795 var dims = element.getDimensions();
709 796 var initialMoveX, initialMoveY;
710 797 var moveX, moveY;
711 798
@@ -737,27 +824,27 Effect.Grow = function(element) {
737 824 break;
738 825 }
739 826
740 return new Effect.MoveBy(element, initialMoveY, initialMoveX, {
827 return new Effect.Move(element, {
828 x: initialMoveX,
829 y: initialMoveY,
741 830 duration: 0.01,
742 beforeSetup: function(effect) { with(Element) {
743 hide(effect.element);
744 makeClipping(effect.element);
745 makePositioned(effect.element);
746 }},
831 beforeSetup: function(effect) {
832 effect.element.hide().makeClipping().makePositioned();
833 },
747 834 afterFinishInternal: function(effect) {
748 835 new Effect.Parallel(
749 836 [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
750 new Effect.MoveBy(effect.element, moveY, moveX, { sync: true, transition: options.moveTransition }),
837 new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
751 838 new Effect.Scale(effect.element, 100, {
752 839 scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
753 840 sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
754 841 ], Object.extend({
755 beforeSetup: function(effect) { with(Element) {
756 setStyle(effect.effects[0].element, {height: '0px'});
757 show(effect.effects[0].element); }},
758 afterFinishInternal: function(effect) { with(Element) {
759 [undoClipping, undoPositioned].call(effect.effects[0].element);
760 setStyle(effect.effects[0].element, oldStyle); }}
842 beforeSetup: function(effect) {
843 effect.effects[0].element.setStyle({height: '0px'}).show();
844 },
845 afterFinishInternal: function(effect) {
846 effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
847 }
761 848 }, options)
762 849 )
763 850 }
@@ -768,7 +855,7 Effect.Shrink = function(element) {
768 855 element = $(element);
769 856 var options = Object.extend({
770 857 direction: 'center',
771 moveTransistion: Effect.Transitions.sinoidal,
858 moveTransition: Effect.Transitions.sinoidal,
772 859 scaleTransition: Effect.Transitions.sinoidal,
773 860 opacityTransition: Effect.Transitions.none
774 861 }, arguments[1] || {});
@@ -777,9 +864,9 Effect.Shrink = function(element) {
777 864 left: element.style.left,
778 865 height: element.style.height,
779 866 width: element.style.width,
780 opacity: Element.getInlineOpacity(element) };
867 opacity: element.getInlineOpacity() };
781 868
782 var dims = Element.getDimensions(element);
869 var dims = element.getDimensions();
783 870 var moveX, moveY;
784 871
785 872 switch (options.direction) {
@@ -807,13 +894,13 Effect.Shrink = function(element) {
807 894 return new Effect.Parallel(
808 895 [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
809 896 new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
810 new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: options.moveTransition })
897 new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
811 898 ], Object.extend({
812 beforeStartInternal: function(effect) { with(Element) {
813 [makePositioned, makeClipping].call(effect.effects[0].element) }},
814 afterFinishInternal: function(effect) { with(Element) {
815 [hide, undoClipping, undoPositioned].call(effect.effects[0].element);
816 setStyle(effect.effects[0].element, oldStyle); }}
899 beforeStartInternal: function(effect) {
900 effect.effects[0].element.makePositioned().makeClipping();
901 },
902 afterFinishInternal: function(effect) {
903 effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
817 904 }, options)
818 905 );
819 906 }
@@ -821,13 +908,13 Effect.Shrink = function(element) {
821 908 Effect.Pulsate = function(element) {
822 909 element = $(element);
823 910 var options = arguments[1] || {};
824 var oldOpacity = Element.getInlineOpacity(element);
911 var oldOpacity = element.getInlineOpacity();
825 912 var transition = options.transition || Effect.Transitions.sinoidal;
826 var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
913 var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
827 914 reverser.bind(transition);
828 915 return new Effect.Opacity(element,
829 Object.extend(Object.extend({ duration: 3.0, from: 0,
830 afterFinishInternal: function(effect) { Element.setStyle(effect.element, {opacity: oldOpacity}); }
916 Object.extend(Object.extend({ duration: 2.0, from: 0,
917 afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
831 918 }, options), {transition: reverser}));
832 919 }
833 920
@@ -838,7 +925,7 Effect.Fold = function(element) {
838 925 left: element.style.left,
839 926 width: element.style.width,
840 927 height: element.style.height };
841 Element.makeClipping(element);
928 element.makeClipping();
842 929 return new Effect.Scale(element, 5, Object.extend({
843 930 scaleContent: false,
844 931 scaleX: false,
@@ -846,9 +933,156 Effect.Fold = function(element) {
846 933 new Effect.Scale(element, 1, {
847 934 scaleContent: false,
848 935 scaleY: false,
849 afterFinishInternal: function(effect) { with(Element) {
850 [hide, undoClipping].call(effect.element);
851 setStyle(effect.element, oldStyle);
852 }} });
936 afterFinishInternal: function(effect) {
937 effect.element.hide().undoClipping().setStyle(oldStyle);
938 } });
853 939 }}, arguments[1] || {}));
854 }
940 };
941
942 Effect.Morph = Class.create();
943 Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
944 initialize: function(element) {
945 this.element = $(element);
946 if(!this.element) throw(Effect._elementDoesNotExistError);
947 var options = Object.extend({
948 style: ''
949 }, arguments[1] || {});
950 this.start(options);
951 },
952 setup: function(){
953 function parseColor(color){
954 if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
955 color = color.parseColor();
956 return $R(0,2).map(function(i){
957 return parseInt( color.slice(i*2+1,i*2+3), 16 )
958 });
959 }
960 this.transforms = this.options.style.parseStyle().map(function(property){
961 var originalValue = this.element.getStyle(property[0]);
962 return $H({
963 style: property[0],
964 originalValue: property[1].unit=='color' ?
965 parseColor(originalValue) : parseFloat(originalValue || 0),
966 targetValue: property[1].unit=='color' ?
967 parseColor(property[1].value) : property[1].value,
968 unit: property[1].unit
969 });
970 }.bind(this)).reject(function(transform){
971 return (
972 (transform.originalValue == transform.targetValue) ||
973 (
974 transform.unit != 'color' &&
975 (isNaN(transform.originalValue) || isNaN(transform.targetValue))
976 )
977 )
978 });
979 },
980 update: function(position) {
981 var style = $H(), value = null;
982 this.transforms.each(function(transform){
983 value = transform.unit=='color' ?
984 $R(0,2).inject('#',function(m,v,i){
985 return m+(Math.round(transform.originalValue[i]+
986 (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) :
987 transform.originalValue + Math.round(
988 ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
989 style[transform.style] = value;
990 });
991 this.element.setStyle(style);
992 }
993 });
994
995 Effect.Transform = Class.create();
996 Object.extend(Effect.Transform.prototype, {
997 initialize: function(tracks){
998 this.tracks = [];
999 this.options = arguments[1] || {};
1000 this.addTracks(tracks);
1001 },
1002 addTracks: function(tracks){
1003 tracks.each(function(track){
1004 var data = $H(track).values().first();
1005 this.tracks.push($H({
1006 ids: $H(track).keys().first(),
1007 effect: Effect.Morph,
1008 options: { style: data }
1009 }));
1010 }.bind(this));
1011 return this;
1012 },
1013 play: function(){
1014 return new Effect.Parallel(
1015 this.tracks.map(function(track){
1016 var elements = [$(track.ids) || $$(track.ids)].flatten();
1017 return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
1018 }).flatten(),
1019 this.options
1020 );
1021 }
1022 });
1023
1024 Element.CSS_PROPERTIES = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage',
1025 'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle',
1026 'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth',
1027 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor',
1028 'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content',
1029 'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction',
1030 'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch',
1031 'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight',
1032 'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight',
1033 'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity',
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 Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1043
1044 String.prototype.parseStyle = function(){
1045 var element = Element.extend(document.createElement('div'));
1046 element.innerHTML = '<div style="' + this + '"></div>';
1047 var style = element.down().style, styleRules = $H();
1048
1049 Element.CSS_PROPERTIES.each(function(property){
1050 if(style[property]) styleRules[property] = style[property];
1051 });
1052
1053 var result = $H();
1054
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
1065 result[property.underscore().dasherize()] = $H({ value:value, unit:unit });
1066 }.bind(this));
1067
1068 return result;
1069 };
1070
1071 Element.morph = function(element, style) {
1072 new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
1073 return element;
1074 };
1075
1076 ['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
1077 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each(
1078 function(f) { Element.Methods[f] = Element[f]; }
1079 );
1080
1081 Element.Methods.visualEffect = function(element, effect, options) {
1082 s = effect.gsub(/_/, '-').camelize();
1083 effect_class = s.charAt(0).toUpperCase() + s.substring(1);
1084 new Effect[effect_class](element, options);
1085 return $(element);
1086 };
1087
1088 Element.addMethods(); No newline at end of file
This diff has been collapsed as it changes many lines, (558 lines changed) Show them Hide them
@@ -1,5 +1,5
1 /* Prototype JavaScript framework, version 1.5.0_rc1
2 * (c) 2005 Sam Stephenson <sam@conio.net>
1 /* Prototype JavaScript framework, version 1.5.0
2 * (c) 2005-2007 Sam Stephenson
3 3 *
4 4 * Prototype is freely distributable under the terms of an MIT-style license.
5 5 * For details, see the Prototype web site: http://prototype.conio.net/
@@ -7,7 +7,7
7 7 /*--------------------------------------------------------------------------*/
8 8
9 9 var Prototype = {
10 Version: '1.5.0_rc1',
10 Version: '1.5.0',
11 11 BrowserFeatures: {
12 12 XPath: !!document.evaluate
13 13 },
@@ -100,7 +100,7 var Try = {
100 100 these: function() {
101 101 var returnValue;
102 102
103 for (var i = 0; i < arguments.length; i++) {
103 for (var i = 0, length = arguments.length; i < length; i++) {
104 104 var lambda = arguments[i];
105 105 try {
106 106 returnValue = lambda();
@@ -145,6 +145,10 PeriodicalExecuter.prototype = {
145 145 }
146 146 }
147 147 }
148 String.interpret = function(value){
149 return value == null ? '' : String(value);
150 }
151
148 152 Object.extend(String.prototype, {
149 153 gsub: function(pattern, replacement) {
150 154 var result = '', source = this, match;
@@ -153,7 +157,7 Object.extend(String.prototype, {
153 157 while (source.length > 0) {
154 158 if (match = source.match(pattern)) {
155 159 result += source.slice(0, match.index);
156 result += (replacement(match) || '').toString();
160 result += String.interpret(replacement(match));
157 161 source = source.slice(match.index + match[0].length);
158 162 } else {
159 163 result += source, source = '';
@@ -218,18 +222,28 Object.extend(String.prototype, {
218 222 unescapeHTML: function() {
219 223 var div = document.createElement('div');
220 224 div.innerHTML = this.stripTags();
221 return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
225 return div.childNodes[0] ? (div.childNodes.length > 1 ?
226 $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
227 div.childNodes[0].nodeValue) : '';
222 228 },
223 229
224 toQueryParams: function() {
225 var match = this.strip().match(/[^?]*$/)[0];
230 toQueryParams: function(separator) {
231 var match = this.strip().match(/([^?#]*)(#.*)?$/);
226 232 if (!match) return {};
227 var pairs = match.split('&');
228 return pairs.inject({}, function(params, pairString) {
229 var pair = pairString.split('=');
230 var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
231 params[decodeURIComponent(pair[0])] = value;
232 return params;
233
234 return match[1].split(separator || '&').inject({}, function(hash, pair) {
235 if ((pair = pair.split('='))[0]) {
236 var name = decodeURIComponent(pair[0]);
237 var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
238
239 if (hash[name] !== undefined) {
240 if (hash[name].constructor != Array)
241 hash[name] = [hash[name]];
242 if (value) hash[name].push(value);
243 }
244 else hash[name] = value;
245 }
246 return hash;
233 247 });
234 248 },
235 249
@@ -237,20 +251,35 Object.extend(String.prototype, {
237 251 return this.split('');
238 252 },
239 253
254 succ: function() {
255 return this.slice(0, this.length - 1) +
256 String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
257 },
258
240 259 camelize: function() {
241 var oStringList = this.split('-');
242 if (oStringList.length == 1) return oStringList[0];
260 var parts = this.split('-'), len = parts.length;
261 if (len == 1) return parts[0];
243 262
244 var camelizedString = this.indexOf('-') == 0
245 ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
246 : oStringList[0];
263 var camelized = this.charAt(0) == '-'
264 ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
265 : parts[0];
247 266
248 for (var i = 1, length = oStringList.length; i < length; i++) {
249 var s = oStringList[i];
250 camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
251 }
267 for (var i = 1; i < len; i++)
268 camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
269
270 return camelized;
271 },
272
273 capitalize: function(){
274 return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
275 },
252 276
253 return camelizedString;
277 underscore: function() {
278 return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
279 },
280
281 dasherize: function() {
282 return this.gsub(/_/,'-');
254 283 },
255 284
256 285 inspect: function(useDoubleQuotes) {
@@ -282,7 +311,7 Template.prototype = {
282 311 return this.template.gsub(this.pattern, function(match) {
283 312 var before = match[1];
284 313 if (before == '\\') return match[2];
285 return before + (object[match[3]] || '').toString();
314 return before + String.interpret(object[match[3]]);
286 315 });
287 316 }
288 317 }
@@ -311,7 +340,7 var Enumerable = {
311 340 var index = -number, slices = [], array = this.toArray();
312 341 while ((index += number) < array.length)
313 342 slices.push(array.slice(index, index+number));
314 return slices.collect(iterator || Prototype.K);
343 return slices.map(iterator);
315 344 },
316 345
317 346 all: function(iterator) {
@@ -335,7 +364,7 var Enumerable = {
335 364 collect: function(iterator) {
336 365 var results = [];
337 366 this.each(function(value, index) {
338 results.push(iterator(value, index));
367 results.push((iterator || Prototype.K)(value, index));
339 368 });
340 369 return results;
341 370 },
@@ -382,12 +411,11 var Enumerable = {
382 411 },
383 412
384 413 inGroupsOf: function(number, fillWith) {
385 fillWith = fillWith || null;
386 var results = this.eachSlice(number);
387 if (results.length > 0) (number - results.last().length).times(function() {
388 results.last().push(fillWith)
414 fillWith = fillWith === undefined ? null : fillWith;
415 return this.eachSlice(number, function(slice) {
416 while(slice.length < number) slice.push(fillWith);
417 return slice;
389 418 });
390 return results;
391 419 },
392 420
393 421 inject: function(memo, iterator) {
@@ -399,7 +427,7 var Enumerable = {
399 427
400 428 invoke: function(method) {
401 429 var args = $A(arguments).slice(1);
402 return this.collect(function(value) {
430 return this.map(function(value) {
403 431 return value[method].apply(value, args);
404 432 });
405 433 },
@@ -451,7 +479,7 var Enumerable = {
451 479 },
452 480
453 481 sortBy: function(iterator) {
454 return this.collect(function(value, index) {
482 return this.map(function(value, index) {
455 483 return {value: value, criteria: iterator(value, index)};
456 484 }).sort(function(left, right) {
457 485 var a = left.criteria, b = right.criteria;
@@ -460,7 +488,7 var Enumerable = {
460 488 },
461 489
462 490 toArray: function() {
463 return this.collect(Prototype.K);
491 return this.map();
464 492 },
465 493
466 494 zip: function() {
@@ -474,6 +502,10 var Enumerable = {
474 502 });
475 503 },
476 504
505 size: function() {
506 return this.toArray().length;
507 },
508
477 509 inspect: function() {
478 510 return '#<Enumerable:' + this.toArray().inspect() + '>';
479 511 }
@@ -524,7 +556,7 Object.extend(Array.prototype, {
524 556
525 557 compact: function() {
526 558 return this.select(function(value) {
527 return value != undefined || value != null;
559 return value != null;
528 560 });
529 561 },
530 562
@@ -566,17 +598,74 Object.extend(Array.prototype, {
566 598 return [].concat(this);
567 599 },
568 600
601 size: function() {
602 return this.length;
603 },
604
569 605 inspect: function() {
570 606 return '[' + this.map(Object.inspect).join(', ') + ']';
571 607 }
572 608 });
573 609
574 610 Array.prototype.toArray = Array.prototype.clone;
575 var Hash = {
611
612 function $w(string){
613 string = string.strip();
614 return string ? string.split(/\s+/) : [];
615 }
616
617 if(window.opera){
618 Array.prototype.concat = function(){
619 var array = [];
620 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++) {
622 if(arguments[i].constructor == Array) {
623 for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
624 array.push(arguments[i][j]);
625 } else {
626 array.push(arguments[i]);
627 }
628 }
629 return array;
630 }
631 }
632 var Hash = function(obj) {
633 Object.extend(this, obj || {});
634 };
635
636 Object.extend(Hash, {
637 toQueryString: function(obj) {
638 var parts = [];
639
640 this.prototype._each.call(obj, function(pair) {
641 if (!pair.key) return;
642
643 if (pair.value && pair.value.constructor == Array) {
644 var values = pair.value.compact();
645 if (values.length < 2) pair.value = values.reduce();
646 else {
647 key = encodeURIComponent(pair.key);
648 values.each(function(value) {
649 value = value != undefined ? encodeURIComponent(value) : '';
650 parts.push(key + '=' + encodeURIComponent(value));
651 });
652 return;
653 }
654 }
655 if (pair.value == undefined) pair[1] = '';
656 parts.push(pair.map(encodeURIComponent).join('='));
657 });
658
659 return parts.join('&');
660 }
661 });
662
663 Object.extend(Hash.prototype, Enumerable);
664 Object.extend(Hash.prototype, {
576 665 _each: function(iterator) {
577 666 for (var key in this) {
578 667 var value = this[key];
579 if (typeof value == 'function') continue;
668 if (value && value == Hash.prototype[key]) continue;
580 669
581 670 var pair = [key, value];
582 671 pair.key = key;
@@ -600,12 +689,24 var Hash = {
600 689 });
601 690 },
602 691
692 remove: function() {
693 var result;
694 for(var i = 0, length = arguments.length; i < length; i++) {
695 var value = this[arguments[i]];
696 if (value !== undefined){
697 if (result === undefined) result = value;
698 else {
699 if (result.constructor != Array) result = [result];
700 result.push(value)
701 }
702 }
703 delete this[arguments[i]];
704 }
705 return result;
706 },
707
603 708 toQueryString: function() {
604 return this.map(function(pair) {
605 if (!pair.value && pair.value !== 0) pair[1] = '';
606 if (!pair.key) return;
607 return pair.map(encodeURIComponent).join('=');
608 }).join('&');
709 return Hash.toQueryString(this);
609 710 },
610 711
611 712 inspect: function() {
@@ -613,14 +714,12 var Hash = {
613 714 return pair.map(Object.inspect).join(': ');
614 715 }).join(', ') + '}>';
615 716 }
616 }
717 });
617 718
618 719 function $H(object) {
619 var hash = Object.extend({}, object || {});
620 Object.extend(hash, Enumerable);
621 Object.extend(hash, Hash);
622 return hash;
623 }
720 if (object && object.constructor == Hash) return object;
721 return new Hash(object);
722 };
624 723 ObjectRange = Class.create();
625 724 Object.extend(ObjectRange.prototype, Enumerable);
626 725 Object.extend(ObjectRange.prototype, {
@@ -714,8 +813,8 Ajax.Base.prototype = {
714 813 Object.extend(this.options, options || {});
715 814
716 815 this.options.method = this.options.method.toLowerCase();
717 this.options.parameters = $H(typeof this.options.parameters == 'string' ?
718 this.options.parameters.toQueryParams() : this.options.parameters);
816 if (typeof this.options.parameters == 'string')
817 this.options.parameters = this.options.parameters.toQueryParams();
719 818 }
720 819 }
721 820
@@ -724,6 +823,8 Ajax.Request.Events =
724 823 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
725 824
726 825 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
826 _complete: false,
827
727 828 initialize: function(url, options) {
728 829 this.transport = Ajax.getTransport();
729 830 this.setOptions(options);
@@ -731,28 +832,28 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
731 832 },
732 833
733 834 request: function(url) {
835 this.url = url;
836 this.method = this.options.method;
734 837 var params = this.options.parameters;
735 if (params.any()) params['_'] = '';
736 838
737 if (!['get', 'post'].include(this.options.method)) {
839 if (!['get', 'post'].include(this.method)) {
738 840 // simulate other verbs over post
739 params['_method'] = this.options.method;
740 this.options.method = 'post';
841 params['_method'] = this.method;
842 this.method = 'post';
741 843 }
742 844
743 this.url = url;
845 params = Hash.toQueryString(params);
846 if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
744 847
745 848 // when GET, append parameters to URL
746 if (this.options.method == 'get' && params.any())
747 this.url += (this.url.indexOf('?') >= 0 ? '&' : '?') +
748 params.toQueryString();
849 if (this.method == 'get' && params)
850 this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
749 851
750 852 try {
751 853 Ajax.Responders.dispatch('onCreate', this, this.transport);
752 854
753 this.transport.open(this.options.method.toUpperCase(), this.url,
754 this.options.asynchronous, this.options.username,
755 this.options.password);
855 this.transport.open(this.method.toUpperCase(), this.url,
856 this.options.asynchronous);
756 857
757 858 if (this.options.asynchronous)
758 859 setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
@@ -760,14 +861,14 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
760 861 this.transport.onreadystatechange = this.onStateChange.bind(this);
761 862 this.setRequestHeaders();
762 863
763 var body = this.options.method == 'post' ?
764 (this.options.postBody || params.toQueryString()) : null;
864 var body = this.method == 'post' ? (this.options.postBody || params) : null;
765 865
766 866 this.transport.send(body);
767 867
768 868 /* Force Firefox to handle ready state 4 for synchronous requests */
769 869 if (!this.options.asynchronous && this.transport.overrideMimeType)
770 870 this.onStateChange();
871
771 872 }
772 873 catch (e) {
773 874 this.dispatchException(e);
@@ -776,7 +877,7 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
776 877
777 878 onStateChange: function() {
778 879 var readyState = this.transport.readyState;
779 if (readyState > 1)
880 if (readyState > 1 && !((readyState == 4) && this._complete))
780 881 this.respondToReadyState(this.transport.readyState);
781 882 },
782 883
@@ -787,7 +888,7 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
787 888 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
788 889 };
789 890
790 if (this.options.method == 'post') {
891 if (this.method == 'post') {
791 892 headers['Content-type'] = this.options.contentType +
792 893 (this.options.encoding ? '; charset=' + this.options.encoding : '');
793 894
@@ -805,7 +906,7 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
805 906 var extras = this.options.requestHeaders;
806 907
807 908 if (typeof extras.push == 'function')
808 for (var i = 0; i < extras.length; i += 2)
909 for (var i = 0, length = extras.length; i < length; i += 2)
809 910 headers[extras[i]] = extras[i+1];
810 911 else
811 912 $H(extras).each(function(pair) { headers[pair.key] = pair.value });
@@ -826,12 +927,17 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
826 927
827 928 if (state == 'Complete') {
828 929 try {
930 this._complete = true;
829 931 (this.options['on' + this.transport.status]
830 932 || this.options['on' + (this.success() ? 'Success' : 'Failure')]
831 933 || Prototype.emptyFunction)(transport, json);
832 934 } catch (e) {
833 935 this.dispatchException(e);
834 936 }
937
938 if ((this.getHeader('Content-type') || 'text/javascript').strip().
939 match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
940 this.evalResponse();
835 941 }
836 942
837 943 try {
@@ -842,10 +948,6 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
842 948 }
843 949
844 950 if (state == 'Complete') {
845 if ((this.getHeader('Content-type') || '').strip().
846 match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
847 this.evalResponse();
848
849 951 // avoid memory leak in MSIE: clean up
850 952 this.transport.onreadystatechange = Prototype.emptyFunction;
851 953 }
@@ -977,10 +1079,10 if (Prototype.BrowserFeatures.XPath) {
977 1079 var results = [];
978 1080 var query = document.evaluate(expression, $(parentElement) || document,
979 1081 null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
980 for (var i = 0, len = query.snapshotLength; i < len; i++)
1082 for (var i = 0, length = query.snapshotLength; i < length; i++)
981 1083 results.push(query.snapshotItem(i));
982 1084 return results;
983 }
1085 };
984 1086 }
985 1087
986 1088 document.getElementsByClassName = function(className, parentElement) {
@@ -997,7 +1099,7 document.getElementsByClassName = function(className, parentElement) {
997 1099 }
998 1100 return elements;
999 1101 }
1000 }
1102 };
1001 1103
1002 1104 /*--------------------------------------------------------------------------*/
1003 1105
@@ -1005,8 +1107,7 if (!window.Element)
1005 1107 var Element = new Object();
1006 1108
1007 1109 Element.extend = function(element) {
1008 if (!element) return;
1009 if (_nativeExtensions || element.nodeType == 3) return element;
1110 if (!element || _nativeExtensions || element.nodeType == 3) return element;
1010 1111
1011 1112 if (!element._extended && element.tagName && element != window) {
1012 1113 var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
@@ -1016,23 +1117,18 Element.extend = function(element) {
1016 1117 if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
1017 1118 Object.extend(methods, Form.Element.Methods);
1018 1119
1019 for (var property in methods) {
1020 var value = methods[property];
1021 if (typeof value == 'function')
1022 element[property] = cache.findOrStore(value);
1023 }
1120 Object.extend(methods, Element.Methods.Simulated);
1024 1121
1025 var methods = Object.clone(Element.Methods.Simulated), cache = Element.extend.cache;
1026 1122 for (var property in methods) {
1027 1123 var value = methods[property];
1028 if ('function' == typeof value && !(property in element))
1124 if (typeof value == 'function' && !(property in element))
1029 1125 element[property] = cache.findOrStore(value);
1030 1126 }
1031 1127 }
1032 1128
1033 1129 element._extended = true;
1034 1130 return element;
1035 }
1131 };
1036 1132
1037 1133 Element.extend.cache = {
1038 1134 findOrStore: function(value) {
@@ -1040,7 +1136,7 Element.extend.cache = {
1040 1136 return value.apply(null, [this].concat($A(arguments)));
1041 1137 }
1042 1138 }
1043 }
1139 };
1044 1140
1045 1141 Element.Methods = {
1046 1142 visible: function(element) {
@@ -1078,6 +1174,7 Element.Methods = {
1078 1174
1079 1175 replace: function(element, html) {
1080 1176 element = $(element);
1177 html = typeof html == 'undefined' ? '' : html.toString();
1081 1178 if (element.outerHTML) {
1082 1179 element.outerHTML = html.stripScripts();
1083 1180 } else {
@@ -1115,8 +1212,14 Element.Methods = {
1115 1212 },
1116 1213
1117 1214 descendants: function(element) {
1118 element = $(element);
1119 return $A(element.getElementsByTagName('*'));
1215 return $A($(element).getElementsByTagName('*'));
1216 },
1217
1218 immediateDescendants: function(element) {
1219 if (!(element = $(element).firstChild)) return [];
1220 while (element && element.nodeType != 1) element = element.nextSibling;
1221 if (element) return [element].concat($(element).nextSiblings());
1222 return [];
1120 1223 },
1121 1224
1122 1225 previousSiblings: function(element) {
@@ -1133,10 +1236,9 Element.Methods = {
1133 1236 },
1134 1237
1135 1238 match: function(element, selector) {
1136 element = $(element);
1137 1239 if (typeof selector == 'string')
1138 1240 selector = new Selector(selector);
1139 return selector.match(element);
1241 return selector.match($(element));
1140 1242 },
1141 1243
1142 1244 up: function(element, expression, index) {
@@ -1161,13 +1263,27 Element.Methods = {
1161 1263 },
1162 1264
1163 1265 getElementsByClassName: function(element, className) {
1164 element = $(element);
1165 1266 return document.getElementsByClassName(className, element);
1166 1267 },
1167 1268
1168 getHeight: function(element) {
1269 readAttribute: function(element, name) {
1169 1270 element = $(element);
1170 return element.offsetHeight;
1271 if (document.all && !window.opera) {
1272 var t = Element._attributeTranslations;
1273 if (t.values[name]) return t.values[name](element, name);
1274 if (t.names[name]) name = t.names[name];
1275 var attribute = element.attributes[name];
1276 if(attribute) return attribute.nodeValue;
1277 }
1278 return element.getAttribute(name);
1279 },
1280
1281 getHeight: function(element) {
1282 return $(element).getDimensions().height;
1283 },
1284
1285 getWidth: function(element) {
1286 return $(element).getDimensions().width;
1171 1287 },
1172 1288
1173 1289 classNames: function(element) {
@@ -1196,6 +1312,12 Element.Methods = {
1196 1312 return element;
1197 1313 },
1198 1314
1315 toggleClassName: function(element, className) {
1316 if (!(element = $(element))) return;
1317 Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
1318 return element;
1319 },
1320
1199 1321 observe: function() {
1200 1322 Event.observe.apply(Event, arguments);
1201 1323 return $A(arguments).first();
@@ -1223,7 +1345,7 Element.Methods = {
1223 1345 return $(element).innerHTML.match(/^\s*$/);
1224 1346 },
1225 1347
1226 childOf: function(element, ancestor) {
1348 descendantOf: function(element, ancestor) {
1227 1349 element = $(element), ancestor = $(ancestor);
1228 1350 while (element = element.parentNode)
1229 1351 if (element == ancestor) return true;
@@ -1232,40 +1354,69 Element.Methods = {
1232 1354
1233 1355 scrollTo: function(element) {
1234 1356 element = $(element);
1235 var x = element.x ? element.x : element.offsetLeft,
1236 y = element.y ? element.y : element.offsetTop;
1237 window.scrollTo(x, y);
1357 var pos = Position.cumulativeOffset(element);
1358 window.scrollTo(pos[0], pos[1]);
1238 1359 return element;
1239 1360 },
1240 1361
1241 1362 getStyle: function(element, style) {
1242 1363 element = $(element);
1243 var value = element.style[style.camelize()];
1364 if (['float','cssFloat'].include(style))
1365 style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
1366 style = style.camelize();
1367 var value = element.style[style];
1244 1368 if (!value) {
1245 1369 if (document.defaultView && document.defaultView.getComputedStyle) {
1246 1370 var css = document.defaultView.getComputedStyle(element, null);
1247 value = css ? css.getPropertyValue(style) : null;
1371 value = css ? css[style] : null;
1248 1372 } else if (element.currentStyle) {
1249 value = element.currentStyle[style.camelize()];
1373 value = element.currentStyle[style];
1250 1374 }
1251 1375 }
1252 1376
1377 if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
1378 value = element['offset'+style.capitalize()] + 'px';
1379
1253 1380 if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
1254 1381 if (Element.getStyle(element, 'position') == 'static') value = 'auto';
1255
1382 if(style == 'opacity') {
1383 if(value) return parseFloat(value);
1384 if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
1385 if(value[1]) return parseFloat(value[1]) / 100;
1386 return 1.0;
1387 }
1256 1388 return value == 'auto' ? null : value;
1257 1389 },
1258 1390
1259 1391 setStyle: function(element, style) {
1260 1392 element = $(element);
1261 for (var name in style)
1262 element.style[name.camelize()] = style[name];
1393 for (var name in style) {
1394 var value = style[name];
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 }
1263 1413 return element;
1264 1414 },
1265 1415
1266 1416 getDimensions: function(element) {
1267 1417 element = $(element);
1268 if (Element.getStyle(element, 'display') != 'none')
1418 var display = $(element).getStyle('display');
1419 if (display != 'none' && display != null) // Safari bug
1269 1420 return {width: element.offsetWidth, height: element.offsetHeight};
1270 1421
1271 1422 // All *Width and *Height properties give 0 on elements with display none,
@@ -1273,12 +1424,13 Element.Methods = {
1273 1424 var els = element.style;
1274 1425 var originalVisibility = els.visibility;
1275 1426 var originalPosition = els.position;
1427 var originalDisplay = els.display;
1276 1428 els.visibility = 'hidden';
1277 1429 els.position = 'absolute';
1278 els.display = '';
1430 els.display = 'block';
1279 1431 var originalWidth = element.clientWidth;
1280 1432 var originalHeight = element.clientHeight;
1281 els.display = 'none';
1433 els.display = originalDisplay;
1282 1434 els.position = originalPosition;
1283 1435 els.visibility = originalVisibility;
1284 1436 return {width: originalWidth, height: originalHeight};
@@ -1329,21 +1481,68 Element.Methods = {
1329 1481 element._overflow = null;
1330 1482 return element;
1331 1483 }
1332 }
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 },
1507
1508 _flag: function(element, attribute) {
1509 return $(element).hasAttribute(attribute) ? attribute : null;
1510 },
1511
1512 style: function(element) {
1513 return element.style.cssText.toLowerCase();
1514 },
1515
1516 title: function(element) {
1517 var node = element.getAttributeNode('title');
1518 return node.specified ? node.nodeValue : null;
1519 }
1520 };
1521
1522 Object.extend(Element._attributeTranslations.values, {
1523 href: Element._attributeTranslations.values._getAttr,
1524 src: Element._attributeTranslations.values._getAttr,
1525 disabled: Element._attributeTranslations.values._flag,
1526 checked: Element._attributeTranslations.values._flag,
1527 readonly: Element._attributeTranslations.values._flag,
1528 multiple: Element._attributeTranslations.values._flag
1529 });
1333 1530
1334 1531 Element.Methods.Simulated = {
1335 1532 hasAttribute: function(element, attribute) {
1533 var t = Element._attributeTranslations;
1534 attribute = t.names[attribute] || attribute;
1336 1535 return $(element).getAttributeNode(attribute).specified;
1337 1536 }
1338 }
1537 };
1339 1538
1340 1539 // IE is missing .innerHTML support for TABLE-related elements
1341 if(document.all){
1540 if (document.all && !window.opera){
1342 1541 Element.Methods.update = function(element, html) {
1343 1542 element = $(element);
1344 1543 html = typeof html == 'undefined' ? '' : html.toString();
1345 1544 var tagName = element.tagName.toUpperCase();
1346 if (['THEAD','TBODY','TR','TD'].indexOf(tagName) > -1) {
1545 if (['THEAD','TBODY','TR','TD'].include(tagName)) {
1347 1546 var div = document.createElement('div');
1348 1547 switch (tagName) {
1349 1548 case 'THEAD':
@@ -1372,7 +1571,7 if(document.all){
1372 1571 setTimeout(function() {html.evalScripts()}, 10);
1373 1572 return element;
1374 1573 }
1375 }
1574 };
1376 1575
1377 1576 Object.extend(Element, Element.Methods);
1378 1577
@@ -1428,8 +1627,8 Abstract.Insertion.prototype = {
1428 1627 try {
1429 1628 this.element.insertAdjacentHTML(this.adjacency, this.content);
1430 1629 } catch (e) {
1431 var tagName = this.element.tagName.toLowerCase();
1432 if (tagName == 'tbody' || tagName == 'tr') {
1630 var tagName = this.element.tagName.toUpperCase();
1631 if (['TBODY', 'TR'].include(tagName)) {
1433 1632 this.insertContent(this.contentFromAnonymousTable());
1434 1633 } else {
1435 1634 throw e;
@@ -1539,7 +1738,7 Element.ClassNames.prototype = {
1539 1738 toString: function() {
1540 1739 return $A(this).join(' ');
1541 1740 }
1542 }
1741 };
1543 1742
1544 1743 Object.extend(Element.ClassNames.prototype, Enumerable);
1545 1744 var Selector = Class.create();
@@ -1586,15 +1785,15 Selector.prototype = {
1586 1785 if (params.wildcard)
1587 1786 conditions.push('true');
1588 1787 if (clause = params.id)
1589 conditions.push('element.id == ' + clause.inspect());
1788 conditions.push('element.readAttribute("id") == ' + clause.inspect());
1590 1789 if (clause = params.tagName)
1591 1790 conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
1592 1791 if ((clause = params.classNames).length > 0)
1593 for (var i = 0; i < clause.length; i++)
1594 conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
1792 for (var i = 0, length = clause.length; i < length; i++)
1793 conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
1595 1794 if (clause = params.attributes) {
1596 1795 clause.each(function(attribute) {
1597 var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
1796 var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
1598 1797 var splitValueBy = function(delimiter) {
1599 1798 return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
1600 1799 }
@@ -1607,7 +1806,7 Selector.prototype = {
1607 1806 ); break;
1608 1807 case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
1609 1808 case '':
1610 case undefined: conditions.push(value + ' != null'); break;
1809 case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
1611 1810 default: throw 'Unknown operator ' + attribute.operator + ' in selector';
1612 1811 }
1613 1812 });
@@ -1618,6 +1817,7 Selector.prototype = {
1618 1817
1619 1818 compileMatcher: function() {
1620 1819 this.match = new Function('element', 'if (!element.tagName) return false; \
1820 element = $(element); \
1621 1821 return ' + this.buildMatchExpression());
1622 1822 },
1623 1823
@@ -1647,7 +1847,7 Selector.prototype = {
1647 1847 Object.extend(Selector, {
1648 1848 matchElements: function(elements, expression) {
1649 1849 var selector = new Selector(expression);
1650 return elements.select(selector.match.bind(selector)).collect(Element.extend);
1850 return elements.select(selector.match.bind(selector)).map(Element.extend);
1651 1851 },
1652 1852
1653 1853 findElement: function(elements, expression, index) {
@@ -1657,7 +1857,7 Object.extend(Selector, {
1657 1857
1658 1858 findChildElements: function(element, expressions) {
1659 1859 return expressions.map(function(expression) {
1660 return expression.strip().split(/\s+/).inject([null], function(results, expr) {
1860 return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
1661 1861 var selector = new Selector(expr);
1662 1862 return results.inject([], function(elements, result) {
1663 1863 return elements.concat(selector.findElements(result || element));
@@ -1676,18 +1876,28 var Form = {
1676 1876 return form;
1677 1877 },
1678 1878
1679 serializeElements: function(elements) {
1680 return elements.inject([], function(queryComponents, element) {
1681 var queryComponent = Form.Element.serialize(element);
1682 if (queryComponent) queryComponents.push(queryComponent);
1683 return queryComponents;
1684 }).join('&');
1879 serializeElements: function(elements, getHash) {
1880 var data = elements.inject({}, function(result, element) {
1881 if (!element.disabled && element.name) {
1882 var key = element.name, value = $(element).getValue();
1883 if (value != undefined) {
1884 if (result[key]) {
1885 if (result[key].constructor != Array) result[key] = [result[key]];
1886 result[key].push(value);
1887 }
1888 else result[key] = value;
1889 }
1890 }
1891 return result;
1892 });
1893
1894 return getHash ? data : Hash.toQueryString(data);
1685 1895 }
1686 1896 };
1687 1897
1688 1898 Form.Methods = {
1689 serialize: function(form) {
1690 return Form.serializeElements($(form).getElements());
1899 serialize: function(form, getHash) {
1900 return Form.serializeElements(Form.getElements(form), getHash);
1691 1901 },
1692 1902
1693 1903 getElements: function(form) {
@@ -1704,14 +1914,11 Form.Methods = {
1704 1914 form = $(form);
1705 1915 var inputs = form.getElementsByTagName('input');
1706 1916
1707 if (!typeName && !name)
1708 return inputs;
1917 if (!typeName && !name) return $A(inputs).map(Element.extend);
1709 1918
1710 var matchingInputs = new Array();
1711 for (var i = 0, length = inputs.length; i < length; i++) {
1919 for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
1712 1920 var input = inputs[i];
1713 if ((typeName && input.type != typeName) ||
1714 (name && input.name != name))
1921 if ((typeName && input.type != typeName) || (name && input.name != name))
1715 1922 continue;
1716 1923 matchingInputs.push(Element.extend(input));
1717 1924 }
@@ -1769,30 +1976,21 Form.Element = {
1769 1976 Form.Element.Methods = {
1770 1977 serialize: function(element) {
1771 1978 element = $(element);
1772 if (element.disabled) return '';
1773 var method = element.tagName.toLowerCase();
1774 var parameter = Form.Element.Serializers[method](element);
1775
1776 if (parameter) {
1777 var key = encodeURIComponent(parameter[0]);
1778 if (key.length == 0) return;
1779
1780 if (parameter[1].constructor != Array)
1781 parameter[1] = [parameter[1]];
1782
1783 return parameter[1].map(function(value) {
1784 return key + '=' + encodeURIComponent(value);
1785 }).join('&');
1979 if (!element.disabled && element.name) {
1980 var value = element.getValue();
1981 if (value != undefined) {
1982 var pair = {};
1983 pair[element.name] = value;
1984 return Hash.toQueryString(pair);
1985 }
1786 1986 }
1987 return '';
1787 1988 },
1788 1989
1789 1990 getValue: function(element) {
1790 1991 element = $(element);
1791 1992 var method = element.tagName.toLowerCase();
1792 var parameter = Form.Element.Serializers[method](element);
1793
1794 if (parameter)
1795 return parameter[1];
1993 return Form.Element.Serializers[method](element);
1796 1994 },
1797 1995
1798 1996 clear: function(element) {
@@ -1807,7 +2005,8 Form.Element.Methods = {
1807 2005 activate: function(element) {
1808 2006 element = $(element);
1809 2007 element.focus();
1810 if (element.select)
2008 if (element.select && ( element.tagName.toLowerCase() != 'input' ||
2009 !['button', 'reset', 'submit'].include(element.type) ) )
1811 2010 element.select();
1812 2011 return element;
1813 2012 },
@@ -1828,6 +2027,7 Form.Element.Methods = {
1828 2027
1829 2028 Object.extend(Form.Element, Form.Element.Methods);
1830 2029 var Field = Form.Element;
2030 var $F = Form.Element.getValue;
1831 2031
1832 2032 /*--------------------------------------------------------------------------*/
1833 2033
@@ -1840,51 +2040,45 Form.Element.Serializers = {
1840 2040 default:
1841 2041 return Form.Element.Serializers.textarea(element);
1842 2042 }
1843 return false;
1844 2043 },
1845 2044
1846 2045 inputSelector: function(element) {
1847 if (element.checked)
1848 return [element.name, element.value];
2046 return element.checked ? element.value : null;
1849 2047 },
1850 2048
1851 2049 textarea: function(element) {
1852 return [element.name, element.value];
2050 return element.value;
1853 2051 },
1854 2052
1855 2053 select: function(element) {
1856 return Form.Element.Serializers[element.type == 'select-one' ?
2054 return this[element.type == 'select-one' ?
1857 2055 'selectOne' : 'selectMany'](element);
1858 2056 },
1859 2057
1860 2058 selectOne: function(element) {
1861 var value = '', opt, index = element.selectedIndex;
1862 if (index >= 0) {
1863 opt = Element.extend(element.options[index]);
1864 // Uses the new potential extension if hasAttribute isn't native.
1865 value = opt.hasAttribute('value') ? opt.value : opt.text;
1866 }
1867 return [element.name, value];
2059 var index = element.selectedIndex;
2060 return index >= 0 ? this.optionValue(element.options[index]) : null;
1868 2061 },
1869 2062
1870 2063 selectMany: function(element) {
1871 var value = [];
1872 for (var i = 0; i < element.length; i++) {
1873 var opt = Element.extend(element.options[i]);
1874 if (opt.selected)
1875 // Uses the new potential extension if hasAttribute isn't native.
1876 value.push(opt.hasAttribute('value') ? opt.value : opt.text);
2064 var values, length = element.length;
2065 if (!length) return null;
2066
2067 for (var i = 0, values = []; i < length; i++) {
2068 var opt = element.options[i];
2069 if (opt.selected) values.push(this.optionValue(opt));
1877 2070 }
1878 return [element.name, value];
2071 return values;
2072 },
2073
2074 optionValue: function(opt) {
2075 // extend element because hasAttribute may not be native
2076 return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
1879 2077 }
1880 2078 }
1881 2079
1882 2080 /*--------------------------------------------------------------------------*/
1883 2081
1884 var $F = Form.Element.getValue;
1885
1886 /*--------------------------------------------------------------------------*/
1887
1888 2082 Abstract.TimedObserver = function() {}
1889 2083 Abstract.TimedObserver.prototype = {
1890 2084 initialize: function(element, frequency, callback) {
@@ -1902,7 +2096,9 Abstract.TimedObserver.prototype = {
1902 2096
1903 2097 onTimerEvent: function() {
1904 2098 var value = this.getValue();
1905 if (this.lastValue != value) {
2099 var changed = ('string' == typeof this.lastValue && 'string' == typeof value
2100 ? this.lastValue != value : String(this.lastValue) != String(value));
2101 if (changed) {
1906 2102 this.callback(this.element, value);
1907 2103 this.lastValue = value;
1908 2104 }
@@ -2275,10 +2471,10 var Position = {
2275 2471 element._originalHeight = element.style.height;
2276 2472
2277 2473 element.style.position = 'absolute';
2278 element.style.top = top + 'px';;
2279 element.style.left = left + 'px';;
2280 element.style.width = width + 'px';;
2281 element.style.height = height + 'px';;
2474 element.style.top = top + 'px';
2475 element.style.left = left + 'px';
2476 element.style.width = width + 'px';
2477 element.style.height = height + 'px';
2282 2478 },
2283 2479
2284 2480 relativize: function(element) {
General Comments 0
You need to be logged in to leave comments. Login now