summaryrefslogtreecommitdiff
path: root/docroot/lib
diff options
context:
space:
mode:
Diffstat (limited to 'docroot/lib')
-rwxr-xr-xdocroot/lib/firebug/errorIcon.pngbin0 -> 457 bytes
-rwxr-xr-xdocroot/lib/firebug/firebug.css209
-rwxr-xr-xdocroot/lib/firebug/firebug.html23
-rwxr-xr-xdocroot/lib/firebug/firebug.js672
-rwxr-xr-xdocroot/lib/firebug/firebugx.js10
-rwxr-xr-xdocroot/lib/firebug/infoIcon.pngbin0 -> 524 bytes
-rwxr-xr-xdocroot/lib/firebug/warningIcon.pngbin0 -> 516 bytes
-rwxr-xr-xdocroot/lib/prototype.js3277
-rwxr-xr-xdocroot/lib/scriptaculous/builder.js136
-rwxr-xr-xdocroot/lib/scriptaculous/controls.js875
-rwxr-xr-xdocroot/lib/scriptaculous/dragdrop.js970
-rwxr-xr-xdocroot/lib/scriptaculous/effects.js1094
-rwxr-xr-xdocroot/lib/scriptaculous/scriptaculous.js58
-rwxr-xr-xdocroot/lib/scriptaculous/slider.js277
-rwxr-xr-xdocroot/lib/scriptaculous/sound.js60
-rwxr-xr-xdocroot/lib/scriptaculous/unittest.js564
-rwxr-xr-xdocroot/lib/swfobject/swfobject.js233
17 files changed, 8458 insertions, 0 deletions
diff --git a/docroot/lib/firebug/errorIcon.png b/docroot/lib/firebug/errorIcon.png
new file mode 100755
index 0000000..2d75261
--- /dev/null
+++ b/docroot/lib/firebug/errorIcon.png
Binary files differ
diff --git a/docroot/lib/firebug/firebug.css b/docroot/lib/firebug/firebug.css
new file mode 100755
index 0000000..1f041c4
--- /dev/null
+++ b/docroot/lib/firebug/firebug.css
@@ -0,0 +1,209 @@
1
2html, body {
3 margin: 0;
4 background: #FFFFFF;
5 font-family: Lucida Grande, Tahoma, sans-serif;
6 font-size: 11px;
7 overflow: hidden;
8}
9
10a {
11 text-decoration: none;
12}
13
14a:hover {
15 text-decoration: underline;
16}
17
18.toolbar {
19 height: 14px;
20 border-top: 1px solid ThreeDHighlight;
21 border-bottom: 1px solid ThreeDShadow;
22 padding: 2px 6px;
23 background: ThreeDFace;
24}
25
26.toolbarRight {
27 position: absolute;
28 top: 4px;
29 right: 6px;
30}
31
32#log {
33 overflow: auto;
34 position: absolute;
35 left: 0;
36 width: 100%;
37}
38
39#commandLine {
40 position: absolute;
41 bottom: 0;
42 left: 0;
43 width: 100%;
44 height: 18px;
45 border: none;
46 border-top: 1px solid ThreeDShadow;
47}
48
49/************************************************************************************************/
50
51.logRow {
52 position: relative;
53 border-bottom: 1px solid #D7D7D7;
54 padding: 2px 4px 1px 6px;
55 background-color: #FFFFFF;
56}
57
58.logRow-command {
59 font-family: Monaco, monospace;
60 color: blue;
61}
62
63.objectBox-null {
64 padding: 0 2px;
65 border: 1px solid #666666;
66 background-color: #888888;
67 color: #FFFFFF;
68}
69
70.objectBox-string {
71 font-family: Monaco, monospace;
72 color: red;
73 white-space: pre;
74}
75
76.objectBox-number {
77 color: #000088;
78}
79
80.objectBox-function {
81 font-family: Monaco, monospace;
82 color: DarkGreen;
83}
84
85.objectBox-object {
86 color: DarkGreen;
87 font-weight: bold;
88}
89
90/************************************************************************************************/
91
92.logRow-info,
93.logRow-error,
94.logRow-warning {
95 background: #FFFFFF no-repeat 2px 2px;
96 padding-left: 20px;
97 padding-bottom: 3px;
98}
99
100.logRow-info {
101 background-image: url(infoIcon.png);
102}
103
104.logRow-warning {
105 background-color: cyan;
106 background-image: url(warningIcon.png);
107}
108
109.logRow-error {
110 background-color: LightYellow;
111 background-image: url(errorIcon.png);
112}
113
114.errorMessage {
115 vertical-align: top;
116 color: #FF0000;
117}
118
119.objectBox-sourceLink {
120 position: absolute;
121 right: 4px;
122 top: 2px;
123 padding-left: 8px;
124 font-family: Lucida Grande, sans-serif;
125 font-weight: bold;
126 color: #0000FF;
127}
128
129/************************************************************************************************/
130
131.logRow-group {
132 background: #EEEEEE;
133 border-bottom: none;
134}
135
136.logGroup {
137 background: #EEEEEE;
138}
139
140.logGroupBox {
141 margin-left: 24px;
142 border-top: 1px solid #D7D7D7;
143 border-left: 1px solid #D7D7D7;
144}
145
146/************************************************************************************************/
147
148.selectorTag,
149.selectorId,
150.selectorClass {
151 font-family: Monaco, monospace;
152 font-weight: normal;
153}
154
155.selectorTag {
156 color: #0000FF;
157}
158
159.selectorId {
160 color: DarkBlue;
161}
162
163.selectorClass {
164 color: red;
165}
166
167/************************************************************************************************/
168
169.objectBox-element {
170 font-family: Monaco, monospace;
171 color: #000088;
172}
173
174.nodeChildren {
175 margin-left: 16px;
176}
177
178.nodeTag {
179 color: blue;
180}
181
182.nodeValue {
183 color: #FF0000;
184 font-weight: normal;
185}
186
187.nodeText,
188.nodeComment {
189 margin: 0 2px;
190 vertical-align: top;
191}
192
193.nodeText {
194 color: #333333;
195}
196
197.nodeComment {
198 color: DarkGreen;
199}
200
201/************************************************************************************************/
202
203.propertyNameCell {
204 vertical-align: top;
205}
206
207.propertyName {
208 font-weight: bold;
209}
diff --git a/docroot/lib/firebug/firebug.html b/docroot/lib/firebug/firebug.html
new file mode 100755
index 0000000..861e639
--- /dev/null
+++ b/docroot/lib/firebug/firebug.html
@@ -0,0 +1,23 @@
1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
4<html xmlns="http://www.w3.org/1999/xhtml">
5
6<head>
7 <title>Firebug</title>
8 <link rel="stylesheet" type="text/css" href="firebug.css">
9</head>
10
11<body>
12 <div id="toolbar" class="toolbar">
13 <a href="#" onclick="parent.console.clear()">Clear</a>
14 <span class="toolbarRight">
15 <a href="#" onclick="parent.console.close()">Close</a>
16 </span>
17 </div>
18 <div id="log"></div>
19 <input type="text" id="commandLine">
20
21 <script>parent.onFirebugReady(document);</script>
22</body>
23</html>
diff --git a/docroot/lib/firebug/firebug.js b/docroot/lib/firebug/firebug.js
new file mode 100755
index 0000000..eb853b8
--- /dev/null
+++ b/docroot/lib/firebug/firebug.js
@@ -0,0 +1,672 @@
1
2if (!("console" in window) || !("firebug" in console)) {
3(function()
4{
5 window.console =
6 {
7 log: function()
8 {
9 logFormatted(arguments, "");
10 },
11
12 debug: function()
13 {
14 logFormatted(arguments, "debug");
15 },
16
17 info: function()
18 {
19 logFormatted(arguments, "info");
20 },
21
22 warn: function()
23 {
24 logFormatted(arguments, "warning");
25 },
26
27 error: function()
28 {
29 logFormatted(arguments, "error");
30 },
31
32 assert: function(truth, message)
33 {
34 if (!truth)
35 {
36 var args = [];
37 for (var i = 1; i < arguments.length; ++i)
38 args.push(arguments[i]);
39
40 logFormatted(args.length ? args : ["Assertion Failure"], "error");
41 throw message ? message : "Assertion Failure";
42 }
43 },
44
45 dir: function(object)
46 {
47 var html = [];
48
49 var pairs = [];
50 for (var name in object)
51 {
52 try
53 {
54 pairs.push([name, object[name]]);
55 }
56 catch (exc)
57 {
58 }
59 }
60
61 pairs.sort(function(a, b) { return a[0] < b[0] ? -1 : 1; });
62
63 html.push('<table>');
64 for (var i = 0; i < pairs.length; ++i)
65 {
66 var name = pairs[i][0], value = pairs[i][1];
67
68 html.push('<tr>',
69 '<td class="propertyNameCell"><span class="propertyName">',
70 escapeHTML(name), '</span></td>', '<td><span class="propertyValue">');
71 appendObject(value, html);
72 html.push('</span></td></tr>');
73 }
74 html.push('</table>');
75
76 logRow(html, "dir");
77 },
78
79 dirxml: function(node)
80 {
81 var html = [];
82
83 appendNode(node, html);
84 logRow(html, "dirxml");
85 },
86
87 group: function()
88 {
89 logRow(arguments, "group", pushGroup);
90 },
91
92 groupEnd: function()
93 {
94 logRow(arguments, "", popGroup);
95 },
96
97 time: function(name)
98 {
99 timeMap[name] = (new Date()).getTime();
100 },
101
102 timeEnd: function(name)
103 {
104 if (name in timeMap)
105 {
106 var delta = (new Date()).getTime() - timeMap[name];
107 logFormatted([name+ ":", delta+"ms"]);
108 delete timeMap[name];
109 }
110 },
111
112 count: function()
113 {
114 this.warn(["count() not supported."]);
115 },
116
117 trace: function()
118 {
119 this.warn(["trace() not supported."]);
120 },
121
122 profile: function()
123 {
124 this.warn(["profile() not supported."]);
125 },
126
127 profileEnd: function()
128 {
129 },
130
131 clear: function()
132 {
133 consoleBody.innerHTML = "";
134 },
135
136 open: function()
137 {
138 toggleConsole(true);
139 },
140
141 close: function()
142 {
143 if (frameVisible)
144 toggleConsole();
145 }
146 };
147
148 // ********************************************************************************************
149
150 var consoleFrame = null;
151 var consoleBody = null;
152 var commandLine = null;
153
154 var frameVisible = false;
155 var messageQueue = [];
156 var groupStack = [];
157 var timeMap = {};
158
159 var clPrefix = ">>> ";
160
161 var isFirefox = navigator.userAgent.indexOf("Firefox") != -1;
162 var isIE = navigator.userAgent.indexOf("MSIE") != -1;
163 var isOpera = navigator.userAgent.indexOf("Opera") != -1;
164 var isSafari = navigator.userAgent.indexOf("AppleWebKit") != -1;
165
166 // ********************************************************************************************
167
168 function toggleConsole(forceOpen)
169 {
170 frameVisible = forceOpen || !frameVisible;
171 if (consoleFrame)
172 consoleFrame.style.visibility = frameVisible ? "visible" : "hidden";
173 else
174 waitForBody();
175 }
176
177 function focusCommandLine()
178 {
179 toggleConsole(true);
180 if (commandLine)
181 commandLine.focus();
182 }
183
184 function waitForBody()
185 {
186 if (document.body)
187 createFrame();
188 else
189 setTimeout(waitForBody, 200);
190 }
191
192 function createFrame()
193 {
194 if (consoleFrame)
195 return;
196
197 window.onFirebugReady = function(doc)
198 {
199 window.onFirebugReady = null;
200
201 var toolbar = doc.getElementById("toolbar");
202 toolbar.onmousedown = onSplitterMouseDown;
203
204 commandLine = doc.getElementById("commandLine");
205 addEvent(commandLine, "keydown", onCommandLineKeyDown);
206
207 addEvent(doc, isIE || isSafari ? "keydown" : "keypress", onKeyDown);
208
209 consoleBody = doc.getElementById("log");
210 layout();
211 flush();
212 }
213
214 var baseURL = getFirebugURL();
215
216 consoleFrame = document.createElement("iframe");
217 consoleFrame.setAttribute("src", baseURL+"/firebug.html");
218 consoleFrame.setAttribute("frameBorder", "0");
219 consoleFrame.style.visibility = (frameVisible ? "visible" : "hidden");
220 consoleFrame.style.zIndex = "2147483647";
221 consoleFrame.style.position = "fixed";
222 consoleFrame.style.width = "100%";
223 consoleFrame.style.left = "0";
224 consoleFrame.style.bottom = "0";
225 consoleFrame.style.height = "200px";
226 document.body.appendChild(consoleFrame);
227 }
228
229 function getFirebugURL()
230 {
231 var scripts = document.getElementsByTagName("script");
232 for (var i = 0; i < scripts.length; ++i)
233 {
234 if (scripts[i].src.indexOf("firebug.js") != -1)
235 {
236 var lastSlash = scripts[i].src.lastIndexOf("/");
237 return scripts[i].src.substr(0, lastSlash);
238 }
239 }
240 }
241
242 function evalCommandLine()
243 {
244 var text = commandLine.value;
245 commandLine.value = "";
246
247 logRow([clPrefix, text], "command");
248
249 var value;
250 try
251 {
252 value = eval(text);
253 }
254 catch (exc)
255 {
256 }
257
258 console.log(value);
259 }
260
261 function layout()
262 {
263 var toolbar = consoleBody.ownerDocument.getElementById("toolbar");
264 var height = consoleFrame.offsetHeight - (toolbar.offsetHeight + commandLine.offsetHeight);
265 consoleBody.style.top = toolbar.offsetHeight + "px";
266 consoleBody.style.height = height + "px";
267
268 commandLine.style.top = (consoleFrame.offsetHeight - commandLine.offsetHeight) + "px";
269 }
270
271 function logRow(message, className, handler)
272 {
273 if (consoleBody)
274 writeMessage(message, className, handler);
275 else
276 {
277 messageQueue.push([message, className, handler]);
278 waitForBody();
279 }
280 }
281
282 function flush()
283 {
284 var queue = messageQueue;
285 messageQueue = [];
286
287 for (var i = 0; i < queue.length; ++i)
288 writeMessage(queue[i][0], queue[i][1], queue[i][2]);
289 }
290
291 function writeMessage(message, className, handler)
292 {
293 var isScrolledToBottom =
294 consoleBody.scrollTop + consoleBody.offsetHeight >= consoleBody.scrollHeight;
295
296 if (!handler)
297 handler = writeRow;
298
299 handler(message, className);
300
301 if (isScrolledToBottom)
302 consoleBody.scrollTop = consoleBody.scrollHeight - consoleBody.offsetHeight;
303 }
304
305 function appendRow(row)
306 {
307 var container = groupStack.length ? groupStack[groupStack.length-1] : consoleBody;
308 container.appendChild(row);
309 }
310
311 function writeRow(message, className)
312 {
313 var row = consoleBody.ownerDocument.createElement("div");
314 row.className = "logRow" + (className ? " logRow-"+className : "");
315 row.innerHTML = message.join("");
316 appendRow(row);
317 }
318
319 function pushGroup(message, className)
320 {
321 logFormatted(message, className);
322
323 var groupRow = consoleBody.ownerDocument.createElement("div");
324 groupRow.className = "logGroup";
325 var groupRowBox = consoleBody.ownerDocument.createElement("div");
326 groupRowBox.className = "logGroupBox";
327 groupRow.appendChild(groupRowBox);
328 appendRow(groupRowBox);
329 groupStack.push(groupRowBox);
330 }
331
332 function popGroup()
333 {
334 groupStack.pop();
335 }
336
337 // ********************************************************************************************
338
339 function logFormatted(objects, className)
340 {
341 var html = [];
342
343 var format = objects[0];
344 var objIndex = 0;
345
346 if (typeof(format) != "string")
347 {
348 format = "";
349 objIndex = -1;
350 }
351
352 var parts = parseFormat(format);
353 for (var i = 0; i < parts.length; ++i)
354 {
355 var part = parts[i];
356 if (part && typeof(part) == "object")
357 {
358 var object = objects[++objIndex];
359 part.appender(object, html);
360 }
361 else
362 appendText(part, html);
363 }
364
365 for (var i = objIndex+1; i < objects.length; ++i)
366 {
367 appendText(" ", html);
368
369 var object = objects[i];
370 if (typeof(object) == "string")
371 appendText(object, html);
372 else
373 appendObject(object, html);
374 }
375
376 logRow(html, className);
377 }
378
379 function parseFormat(format)
380 {
381 var parts = [];
382
383 var reg = /((^%|[^\\]%)(\d+)?(\.)([a-zA-Z]))|((^%|[^\\]%)([a-zA-Z]))/;
384 var appenderMap = {s: appendText, d: appendInteger, i: appendInteger, f: appendFloat};
385
386 for (var m = reg.exec(format); m; m = reg.exec(format))
387 {
388 var type = m[8] ? m[8] : m[5];
389 var appender = type in appenderMap ? appenderMap[type] : appendObject;
390 var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0);
391
392 parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1));
393 parts.push({appender: appender, precision: precision});
394
395 format = format.substr(m.index+m[0].length);
396 }
397
398 parts.push(format);
399
400 return parts;
401 }
402
403 function escapeHTML(value)
404 {
405 function replaceChars(ch)
406 {
407 switch (ch)
408 {
409 case "<":
410 return "&lt;";
411 case ">":
412 return "&gt;";
413 case "&":
414 return "&amp;";
415 case "'":
416 return "&#39;";
417 case '"':
418 return "&quot;";
419 }
420 return "?";
421 };
422 return String(value).replace(/[<>&"']/g, replaceChars);
423 }
424
425 function objectToString(object)
426 {
427 try
428 {
429 return object+"";
430 }
431 catch (exc)
432 {
433 return null;
434 }
435 }
436
437 // ********************************************************************************************
438
439 function appendText(object, html)
440 {
441 html.push(escapeHTML(objectToString(object)));
442 }
443
444 function appendNull(object, html)
445 {
446 html.push('<span class="objectBox-null">', escapeHTML(objectToString(object)), '</span>');
447 }
448
449 function appendString(object, html)
450 {
451 html.push('<span class="objectBox-string">&quot;', escapeHTML(objectToString(object)),
452 '&quot;</span>');
453 }
454
455 function appendInteger(object, html)
456 {
457 html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>');
458 }
459
460 function appendFloat(object, html)
461 {
462 html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>');
463 }
464
465 function appendFunction(object, html)
466 {
467 var reName = /function ?(.*?)\(/;
468 var m = reName.exec(objectToString(object));
469 var name = m ? m[1] : "function";
470 html.push('<span class="objectBox-function">', escapeHTML(name), '()</span>');
471 }
472
473 function appendObject(object, html)
474 {
475 try
476 {
477 if (object == undefined)
478 appendNull("undefined", html);
479 else if (object == null)
480 appendNull("null", html);
481 else if (typeof object == "string")
482 appendString(object, html);
483 else if (typeof object == "number")
484 appendInteger(object, html);
485 else if (typeof object == "function")
486 appendFunction(object, html);
487 else if (object.nodeType == 1)
488 appendSelector(object, html);
489 else if (typeof object == "object")
490 appendObjectFormatted(object, html);
491 else
492 appendText(object, html);
493 }
494 catch (exc)
495 {
496 }
497 }
498
499 function appendObjectFormatted(object, html)
500 {
501 var text = objectToString(object);
502 var reObject = /\[object (.*?)\]/;
503
504 var m = reObject.exec(text);
505 html.push('<span class="objectBox-object">', m ? m[1] : text, '</span>')
506 }
507
508 function appendSelector(object, html)
509 {
510 html.push('<span class="objectBox-selector">');
511
512 html.push('<span class="selectorTag">', escapeHTML(object.nodeName.toLowerCase()), '</span>');
513 if (object.id)
514 html.push('<span class="selectorId">#', escapeHTML(object.id), '</span>');
515 if (object.className)
516 html.push('<span class="selectorClass">.', escapeHTML(object.className), '</span>');
517
518 html.push('</span>');
519 }
520
521 function appendNode(node, html)
522 {
523 if (node.nodeType == 1)
524 {
525 html.push(
526 '<div class="objectBox-element">',
527 '&lt;<span class="nodeTag">', node.nodeName.toLowerCase(), '</span>');
528
529 for (var i = 0; i < node.attributes.length; ++i)
530 {
531 var attr = node.attributes[i];
532 if (!attr.specified)
533 continue;
534
535 html.push('&nbsp;<span class="nodeName">', attr.nodeName.toLowerCase(),
536 '</span>=&quot;<span class="nodeValue">', escapeHTML(attr.nodeValue),
537 '</span>&quot;')
538 }
539
540 if (node.firstChild)
541 {
542 html.push('&gt;</div><div class="nodeChildren">');
543
544 for (var child = node.firstChild; child; child = child.nextSibling)
545 appendNode(child, html);
546
547 html.push('</div><div class="objectBox-element">&lt;/<span class="nodeTag">',
548 node.nodeName.toLowerCase(), '&gt;</span></div>');
549 }
550 else
551 html.push('/&gt;</div>');
552 }
553 else if (node.nodeType == 3)
554 {
555 html.push('<div class="nodeText">', escapeHTML(node.nodeValue),
556 '</div>');
557 }
558 }
559
560 // ********************************************************************************************
561
562 function addEvent(object, name, handler)
563 {
564 if (document.all)
565 object.attachEvent("on"+name, handler);
566 else
567 object.addEventListener(name, handler, false);
568 }
569
570 function removeEvent(object, name, handler)
571 {
572 if (document.all)
573 object.detachEvent("on"+name, handler);
574 else
575 object.removeEventListener(name, handler, false);
576 }
577
578 function cancelEvent(event)
579 {
580 if (document.all)
581 event.cancelBubble = true;
582 else
583 event.stopPropagation();
584 }
585
586 function onError(msg, href, lineNo)
587 {
588 var html = [];
589
590 var lastSlash = href.lastIndexOf("/");
591 var fileName = lastSlash == -1 ? href : href.substr(lastSlash+1);
592
593 html.push(
594 '<span class="errorMessage">', msg, '</span>',
595 '<div class="objectBox-sourceLink">', fileName, ' (line ', lineNo, ')</div>'
596 );
597
598 logRow(html, "error");
599 };
600
601 function onKeyDown(event)
602 {
603 if (event.keyCode == 123)
604 toggleConsole();
605 else if ((event.keyCode == 108 || event.keyCode == 76) && event.shiftKey
606 && (event.metaKey || event.ctrlKey))
607 focusCommandLine();
608 else
609 return;
610
611 cancelEvent(event);
612 }
613
614 function onSplitterMouseDown(event)
615 {
616 if (isSafari || isOpera)
617 return;
618
619 addEvent(document, "mousemove", onSplitterMouseMove);
620 addEvent(document, "mouseup", onSplitterMouseUp);
621
622 for (var i = 0; i < frames.length; ++i)
623 {
624 addEvent(frames[i].document, "mousemove", onSplitterMouseMove);
625 addEvent(frames[i].document, "mouseup", onSplitterMouseUp);
626 }
627 }
628
629 function onSplitterMouseMove(event)
630 {
631 var win = document.all
632 ? event.srcElement.ownerDocument.parentWindow
633 : event.target.ownerDocument.defaultView;
634
635 var clientY = event.clientY;
636 if (win != win.parent)
637 clientY += win.frameElement ? win.frameElement.offsetTop : 0;
638
639 var height = consoleFrame.offsetTop + consoleFrame.clientHeight;
640 var y = height - clientY;
641
642 consoleFrame.style.height = y + "px";
643 layout();
644 }
645
646 function onSplitterMouseUp(event)
647 {
648 removeEvent(document, "mousemove", onSplitterMouseMove);
649 removeEvent(document, "mouseup", onSplitterMouseUp);
650
651 for (var i = 0; i < frames.length; ++i)
652 {
653 removeEvent(frames[i].document, "mousemove", onSplitterMouseMove);
654 removeEvent(frames[i].document, "mouseup", onSplitterMouseUp);
655 }
656 }
657
658 function onCommandLineKeyDown(event)
659 {
660 if (event.keyCode == 13)
661 evalCommandLine();
662 else if (event.keyCode == 27)
663 commandLine.value = "";
664 }
665
666 window.onerror = onError;
667 addEvent(document, isIE || isSafari ? "keydown" : "keypress", onKeyDown);
668
669 if (document.documentElement.getAttribute("debug") == "true")
670 toggleConsole(true);
671})();
672}
diff --git a/docroot/lib/firebug/firebugx.js b/docroot/lib/firebug/firebugx.js
new file mode 100755
index 0000000..5a467fc
--- /dev/null
+++ b/docroot/lib/firebug/firebugx.js
@@ -0,0 +1,10 @@
1
2if (!("console" in window) || !("firebug" in console))
3{
4 var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
5 "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
6
7 window.console = {};
8 for (var i = 0; i < names.length; ++i)
9 window.console[names[i]] = function() {}
10} \ No newline at end of file
diff --git a/docroot/lib/firebug/infoIcon.png b/docroot/lib/firebug/infoIcon.png
new file mode 100755
index 0000000..da1e533
--- /dev/null
+++ b/docroot/lib/firebug/infoIcon.png
Binary files differ
diff --git a/docroot/lib/firebug/warningIcon.png b/docroot/lib/firebug/warningIcon.png
new file mode 100755
index 0000000..de51084
--- /dev/null
+++ b/docroot/lib/firebug/warningIcon.png
Binary files differ
diff --git a/docroot/lib/prototype.js b/docroot/lib/prototype.js
new file mode 100755
index 0000000..5806f35
--- /dev/null
+++ b/docroot/lib/prototype.js
@@ -0,0 +1,3277 @@
1/* Prototype JavaScript framework, version 1.5.1.1
2 * (c) 2005-2007 Sam Stephenson
3 *
4 * Prototype is freely distributable under the terms of an MIT-style license.
5 * For details, see the Prototype web site: http://www.prototypejs.org/
6 *
7/*--------------------------------------------------------------------------*/
8
9var Prototype = {
10 Version: '1.5.1.1',
11
12 Browser: {
13 IE: !!(window.attachEvent && !window.opera),
14 Opera: !!window.opera,
15 WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
16 Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
17 },
18
19 BrowserFeatures: {
20 XPath: !!document.evaluate,
21 ElementExtensions: !!window.HTMLElement,
22 SpecificElementExtensions:
23 (document.createElement('div').__proto__ !==
24 document.createElement('form').__proto__)
25 },
26
27 ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
28 JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
29
30 emptyFunction: function() { },
31 K: function(x) { return x }
32}
33
34var Class = {
35 create: function() {
36 return function() {
37 this.initialize.apply(this, arguments);
38 }
39 }
40}
41
42var Abstract = new Object();
43
44Object.extend = function(destination, source) {
45 for (var property in source) {
46 destination[property] = source[property];
47 }
48 return destination;
49}
50
51Object.extend(Object, {
52 inspect: function(object) {
53 try {
54 if (object === undefined) return 'undefined';
55 if (object === null) return 'null';
56 return object.inspect ? object.inspect() : object.toString();
57 } catch (e) {
58 if (e instanceof RangeError) return '...';
59 throw e;
60 }
61 },
62
63 toJSON: function(object) {
64 var type = typeof object;
65 switch(type) {
66 case 'undefined':
67 case 'function':
68 case 'unknown': return;
69 case 'boolean': return object.toString();
70 }
71 if (object === null) return 'null';
72 if (object.toJSON) return object.toJSON();
73 if (object.ownerDocument === document) return;
74 var results = [];
75 for (var property in object) {
76 var value = Object.toJSON(object[property]);
77 if (value !== undefined)
78 results.push(property.toJSON() + ': ' + value);
79 }
80 return '{' + results.join(', ') + '}';
81 },
82
83 keys: function(object) {
84 var keys = [];
85 for (var property in object)
86 keys.push(property);
87 return keys;
88 },
89
90 values: function(object) {
91 var values = [];
92 for (var property in object)
93 values.push(object[property]);
94 return values;
95 },
96
97 clone: function(object) {
98 return Object.extend({}, object);
99 }
100});
101
102Function.prototype.bind = function() {
103 var __method = this, args = $A(arguments), object = args.shift();
104 return function() {
105 return __method.apply(object, args.concat($A(arguments)));
106 }
107}
108
109Function.prototype.bindAsEventListener = function(object) {
110 var __method = this, args = $A(arguments), object = args.shift();
111 return function(event) {
112 return __method.apply(object, [event || window.event].concat(args));
113 }
114}
115
116Object.extend(Number.prototype, {
117 toColorPart: function() {
118 return this.toPaddedString(2, 16);
119 },
120
121 succ: function() {
122 return this + 1;
123 },
124
125 times: function(iterator) {
126 $R(0, this, true).each(iterator);
127 return this;
128 },
129
130 toPaddedString: function(length, radix) {
131 var string = this.toString(radix || 10);
132 return '0'.times(length - string.length) + string;
133 },
134
135 toJSON: function() {
136 return isFinite(this) ? this.toString() : 'null';
137 }
138});
139
140Date.prototype.toJSON = function() {
141 return '"' + this.getFullYear() + '-' +
142 (this.getMonth() + 1).toPaddedString(2) + '-' +
143 this.getDate().toPaddedString(2) + 'T' +
144 this.getHours().toPaddedString(2) + ':' +
145 this.getMinutes().toPaddedString(2) + ':' +
146 this.getSeconds().toPaddedString(2) + '"';
147};
148
149var Try = {
150 these: function() {
151 var returnValue;
152
153 for (var i = 0, length = arguments.length; i < length; i++) {
154 var lambda = arguments[i];
155 try {
156 returnValue = lambda();
157 break;
158 } catch (e) {}
159 }
160
161 return returnValue;
162 }
163}
164
165/*--------------------------------------------------------------------------*/
166
167var PeriodicalExecuter = Class.create();
168PeriodicalExecuter.prototype = {
169 initialize: function(callback, frequency) {
170 this.callback = callback;
171 this.frequency = frequency;
172 this.currentlyExecuting = false;
173
174 this.registerCallback();
175 },
176
177 registerCallback: function() {
178 this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
179 },
180
181 stop: function() {
182 if (!this.timer) return;
183 clearInterval(this.timer);
184 this.timer = null;
185 },
186
187 onTimerEvent: function() {
188 if (!this.currentlyExecuting) {
189 try {
190 this.currentlyExecuting = true;
191 this.callback(this);
192 } finally {
193 this.currentlyExecuting = false;
194 }
195 }
196 }
197}
198Object.extend(String, {
199 interpret: function(value) {
200 return value == null ? '' : String(value);
201 },
202 specialChar: {
203 '\b': '\\b',
204 '\t': '\\t',
205 '\n': '\\n',
206 '\f': '\\f',
207 '\r': '\\r',
208 '\\': '\\\\'
209 }
210});
211
212Object.extend(String.prototype, {
213 gsub: function(pattern, replacement) {
214 var result = '', source = this, match;
215 replacement = arguments.callee.prepareReplacement(replacement);
216
217 while (source.length > 0) {
218 if (match = source.match(pattern)) {
219 result += source.slice(0, match.index);
220 result += String.interpret(replacement(match));
221 source = source.slice(match.index + match[0].length);
222 } else {
223 result += source, source = '';
224 }
225 }
226 return result;
227 },
228
229 sub: function(pattern, replacement, count) {
230 replacement = this.gsub.prepareReplacement(replacement);
231 count = count === undefined ? 1 : count;
232
233 return this.gsub(pattern, function(match) {
234 if (--count < 0) return match[0];
235 return replacement(match);
236 });
237 },
238
239 scan: function(pattern, iterator) {
240 this.gsub(pattern, iterator);
241 return this;
242 },
243
244 truncate: function(length, truncation) {
245 length = length || 30;
246 truncation = truncation === undefined ? '...' : truncation;
247 return this.length > length ?
248 this.slice(0, length - truncation.length) + truncation : this;
249 },
250
251 strip: function() {
252 return this.replace(/^\s+/, '').replace(/\s+$/, '');
253 },
254
255 stripTags: function() {
256 return this.replace(/<\/?[^>]+>/gi, '');
257 },
258
259 stripScripts: function() {
260 return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
261 },
262
263 extractScripts: function() {
264 var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
265 var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
266 return (this.match(matchAll) || []).map(function(scriptTag) {
267 return (scriptTag.match(matchOne) || ['', ''])[1];
268 });
269 },
270
271 evalScripts: function() {
272 return this.extractScripts().map(function(script) { return eval(script) });
273 },
274
275 escapeHTML: function() {
276 var self = arguments.callee;
277 self.text.data = this;
278 return self.div.innerHTML;
279 },
280
281 unescapeHTML: function() {
282 var div = document.createElement('div');
283 div.innerHTML = this.stripTags();
284 return div.childNodes[0] ? (div.childNodes.length > 1 ?
285 $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
286 div.childNodes[0].nodeValue) : '';
287 },
288
289 toQueryParams: function(separator) {
290 var match = this.strip().match(/([^?#]*)(#.*)?$/);
291 if (!match) return {};
292
293 return match[1].split(separator || '&').inject({}, function(hash, pair) {
294 if ((pair = pair.split('='))[0]) {
295 var key = decodeURIComponent(pair.shift());
296 var value = pair.length > 1 ? pair.join('=') : pair[0];
297 if (value != undefined) value = decodeURIComponent(value);
298
299 if (key in hash) {
300 if (hash[key].constructor != Array) hash[key] = [hash[key]];
301 hash[key].push(value);
302 }
303 else hash[key] = value;
304 }
305 return hash;
306 });
307 },
308
309 toArray: function() {
310 return this.split('');
311 },
312
313 succ: function() {
314 return this.slice(0, this.length - 1) +
315 String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
316 },
317
318 times: function(count) {
319 var result = '';
320 for (var i = 0; i < count; i++) result += this;
321 return result;
322 },
323
324 camelize: function() {
325 var parts = this.split('-'), len = parts.length;
326 if (len == 1) return parts[0];
327
328 var camelized = this.charAt(0) == '-'
329 ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
330 : parts[0];
331
332 for (var i = 1; i < len; i++)
333 camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
334
335 return camelized;
336 },
337
338 capitalize: function() {
339 return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
340 },
341
342 underscore: function() {
343 return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
344 },
345
346 dasherize: function() {
347 return this.gsub(/_/,'-');
348 },
349
350 inspect: function(useDoubleQuotes) {
351 var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
352 var character = String.specialChar[match[0]];
353 return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
354 });
355 if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
356 return "'" + escapedString.replace(/'/g, '\\\'') + "'";
357 },
358
359 toJSON: function() {
360 return this.inspect(true);
361 },
362
363 unfilterJSON: function(filter) {
364 return this.sub(filter || Prototype.JSONFilter, '#{1}');
365 },
366
367 isJSON: function() {
368 var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
369 return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
370 },
371
372 evalJSON: function(sanitize) {
373 var json = this.unfilterJSON();
374 try {
375 if (!sanitize || json.isJSON()) return eval('(' + json + ')');
376 } catch (e) { }
377 throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
378 },
379
380 include: function(pattern) {
381 return this.indexOf(pattern) > -1;
382 },
383
384 startsWith: function(pattern) {
385 return this.indexOf(pattern) === 0;
386 },
387
388 endsWith: function(pattern) {
389 var d = this.length - pattern.length;
390 return d >= 0 && this.lastIndexOf(pattern) === d;
391 },
392
393 empty: function() {
394 return this == '';
395 },
396
397 blank: function() {
398 return /^\s*$/.test(this);
399 }
400});
401
402if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
403 escapeHTML: function() {
404 return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
405 },
406 unescapeHTML: function() {
407 return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
408 }
409});
410
411String.prototype.gsub.prepareReplacement = function(replacement) {
412 if (typeof replacement == 'function') return replacement;
413 var template = new Template(replacement);
414 return function(match) { return template.evaluate(match) };
415}
416
417String.prototype.parseQuery = String.prototype.toQueryParams;
418
419Object.extend(String.prototype.escapeHTML, {
420 div: document.createElement('div'),
421 text: document.createTextNode('')
422});
423
424with (String.prototype.escapeHTML) div.appendChild(text);
425
426var Template = Class.create();
427Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
428Template.prototype = {
429 initialize: function(template, pattern) {
430 this.template = template.toString();
431 this.pattern = pattern || Template.Pattern;
432 },
433
434 evaluate: function(object) {
435 return this.template.gsub(this.pattern, function(match) {
436 var before = match[1];
437 if (before == '\\') return match[2];
438 return before + String.interpret(object[match[3]]);
439 });
440 }
441}
442
443var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead');
444
445var Enumerable = {
446 each: function(iterator) {
447 var index = 0;
448 try {
449 this._each(function(value) {
450 iterator(value, index++);
451 });
452 } catch (e) {
453 if (e != $break) throw e;
454 }
455 return this;
456 },
457
458 eachSlice: function(number, iterator) {
459 var index = -number, slices = [], array = this.toArray();
460 while ((index += number) < array.length)
461 slices.push(array.slice(index, index+number));
462 return slices.map(iterator);
463 },
464
465 all: function(iterator) {
466 var result = true;
467 this.each(function(value, index) {
468 result = result && !!(iterator || Prototype.K)(value, index);
469 if (!result) throw $break;
470 });
471 return result;
472 },
473
474 any: function(iterator) {
475 var result = false;
476 this.each(function(value, index) {
477 if (result = !!(iterator || Prototype.K)(value, index))
478 throw $break;
479 });
480 return result;
481 },
482
483 collect: function(iterator) {
484 var results = [];
485 this.each(function(value, index) {
486 results.push((iterator || Prototype.K)(value, index));
487 });
488 return results;
489 },
490
491 detect: function(iterator) {
492 var result;
493 this.each(function(value, index) {
494 if (iterator(value, index)) {
495 result = value;
496 throw $break;
497 }
498 });
499 return result;
500 },
501
502 findAll: function(iterator) {
503 var results = [];
504 this.each(function(value, index) {
505 if (iterator(value, index))
506 results.push(value);
507 });
508 return results;
509 },
510
511 grep: function(pattern, iterator) {
512 var results = [];
513 this.each(function(value, index) {
514 var stringValue = value.toString();
515 if (stringValue.match(pattern))
516 results.push((iterator || Prototype.K)(value, index));
517 })
518 return results;
519 },
520
521 include: function(object) {
522 var found = false;
523 this.each(function(value) {
524 if (value == object) {
525 found = true;
526 throw $break;
527 }
528 });
529 return found;
530 },
531
532 inGroupsOf: function(number, fillWith) {
533 fillWith = fillWith === undefined ? null : fillWith;
534 return this.eachSlice(number, function(slice) {
535 while(slice.length < number) slice.push(fillWith);
536 return slice;
537 });
538 },
539
540 inject: function(memo, iterator) {
541 this.each(function(value, index) {
542 memo = iterator(memo, value, index);
543 });
544 return memo;
545 },
546
547 invoke: function(method) {
548 var args = $A(arguments).slice(1);
549 return this.map(function(value) {
550 return value[method].apply(value, args);
551 });
552 },
553
554 max: function(iterator) {
555 var result;
556 this.each(function(value, index) {
557 value = (iterator || Prototype.K)(value, index);
558 if (result == undefined || value >= result)
559 result = value;
560 });
561 return result;
562 },
563
564 min: function(iterator) {
565 var result;
566 this.each(function(value, index) {
567 value = (iterator || Prototype.K)(value, index);
568 if (result == undefined || value < result)
569 result = value;
570 });
571 return result;
572 },
573
574 partition: function(iterator) {
575 var trues = [], falses = [];
576 this.each(function(value, index) {
577 ((iterator || Prototype.K)(value, index) ?
578 trues : falses).push(value);
579 });
580 return [trues, falses];
581 },
582
583 pluck: function(property) {
584 var results = [];
585 this.each(function(value, index) {
586 results.push(value[property]);
587 });
588 return results;
589 },
590
591 reject: function(iterator) {
592 var results = [];
593 this.each(function(value, index) {
594 if (!iterator(value, index))
595 results.push(value);
596 });
597 return results;
598 },
599
600 sortBy: function(iterator) {
601 return this.map(function(value, index) {
602 return {value: value, criteria: iterator(value, index)};
603 }).sort(function(left, right) {
604 var a = left.criteria, b = right.criteria;
605 return a < b ? -1 : a > b ? 1 : 0;
606 }).pluck('value');
607 },
608
609 toArray: function() {
610 return this.map();
611 },
612
613 zip: function() {
614 var iterator = Prototype.K, args = $A(arguments);
615 if (typeof args.last() == 'function')
616 iterator = args.pop();
617
618 var collections = [this].concat(args).map($A);
619 return this.map(function(value, index) {
620 return iterator(collections.pluck(index));
621 });
622 },
623
624 size: function() {
625 return this.toArray().length;
626 },
627
628 inspect: function() {
629 return '#<Enumerable:' + this.toArray().inspect() + '>';
630 }
631}
632
633Object.extend(Enumerable, {
634 map: Enumerable.collect,
635 find: Enumerable.detect,
636 select: Enumerable.findAll,
637 member: Enumerable.include,
638 entries: Enumerable.toArray
639});
640var $A = Array.from = function(iterable) {
641 if (!iterable) return [];
642 if (iterable.toArray) {
643 return iterable.toArray();
644 } else {
645 var results = [];
646 for (var i = 0, length = iterable.length; i < length; i++)
647 results.push(iterable[i]);
648 return results;
649 }
650}
651
652if (Prototype.Browser.WebKit) {
653 $A = Array.from = function(iterable) {
654 if (!iterable) return [];
655 if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
656 iterable.toArray) {
657 return iterable.toArray();
658 } else {
659 var results = [];
660 for (var i = 0, length = iterable.length; i < length; i++)
661 results.push(iterable[i]);
662 return results;
663 }
664 }
665}
666
667Object.extend(Array.prototype, Enumerable);
668
669if (!Array.prototype._reverse)
670 Array.prototype._reverse = Array.prototype.reverse;
671
672Object.extend(Array.prototype, {
673 _each: function(iterator) {
674 for (var i = 0, length = this.length; i < length; i++)
675 iterator(this[i]);
676 },
677
678 clear: function() {
679 this.length = 0;
680 return this;
681 },
682
683 first: function() {
684 return this[0];
685 },
686
687 last: function() {
688 return this[this.length - 1];
689 },
690
691 compact: function() {
692 return this.select(function(value) {
693 return value != null;
694 });
695 },
696
697 flatten: function() {
698 return this.inject([], function(array, value) {
699 return array.concat(value && value.constructor == Array ?
700 value.flatten() : [value]);
701 });
702 },
703
704 without: function() {
705 var values = $A(arguments);
706 return this.select(function(value) {
707 return !values.include(value);
708 });
709 },
710
711 indexOf: function(object) {
712 for (var i = 0, length = this.length; i < length; i++)
713 if (this[i] == object) return i;
714 return -1;
715 },
716
717 reverse: function(inline) {
718 return (inline !== false ? this : this.toArray())._reverse();
719 },
720
721 reduce: function() {
722 return this.length > 1 ? this : this[0];
723 },
724
725 uniq: function(sorted) {
726 return this.inject([], function(array, value, index) {
727 if (0 == index || (sorted ? array.last() != value : !array.include(value)))
728 array.push(value);
729 return array;
730 });
731 },
732
733 clone: function() {
734 return [].concat(this);
735 },
736
737 size: function() {
738 return this.length;
739 },
740
741 inspect: function() {
742 return '[' + this.map(Object.inspect).join(', ') + ']';
743 },
744
745 toJSON: function() {
746 var results = [];
747 this.each(function(object) {
748 var value = Object.toJSON(object);
749 if (value !== undefined) results.push(value);
750 });
751 return '[' + results.join(', ') + ']';
752 }
753});
754
755Array.prototype.toArray = Array.prototype.clone;
756
757function $w(string) {
758 string = string.strip();
759 return string ? string.split(/\s+/) : [];
760}
761
762if (Prototype.Browser.Opera){
763 Array.prototype.concat = function() {
764 var array = [];
765 for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
766 for (var i = 0, length = arguments.length; i < length; i++) {
767 if (arguments[i].constructor == Array) {
768 for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
769 array.push(arguments[i][j]);
770 } else {
771 array.push(arguments[i]);
772 }
773 }
774 return array;
775 }
776}
777var Hash = function(object) {
778 if (object instanceof Hash) this.merge(object);
779 else Object.extend(this, object || {});
780};
781
782Object.extend(Hash, {
783 toQueryString: function(obj) {
784 var parts = [];
785 parts.add = arguments.callee.addPair;
786
787 this.prototype._each.call(obj, function(pair) {
788 if (!pair.key) return;
789 var value = pair.value;
790
791 if (value && typeof value == 'object') {
792 if (value.constructor == Array) value.each(function(value) {
793 parts.add(pair.key, value);
794 });
795 return;
796 }
797 parts.add(pair.key, value);
798 });
799
800 return parts.join('&');
801 },
802
803 toJSON: function(object) {
804 var results = [];
805 this.prototype._each.call(object, function(pair) {
806 var value = Object.toJSON(pair.value);
807 if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value);
808 });
809 return '{' + results.join(', ') + '}';
810 }
811});
812
813Hash.toQueryString.addPair = function(key, value, prefix) {
814 key = encodeURIComponent(key);
815 if (value === undefined) this.push(key);
816 else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
817}
818
819Object.extend(Hash.prototype, Enumerable);
820Object.extend(Hash.prototype, {
821 _each: function(iterator) {
822 for (var key in this) {
823 var value = this[key];
824 if (value && value == Hash.prototype[key]) continue;
825
826 var pair = [key, value];
827 pair.key = key;
828 pair.value = value;
829 iterator(pair);
830 }
831 },
832
833 keys: function() {
834 return this.pluck('key');
835 },
836
837 values: function() {
838 return this.pluck('value');
839 },
840
841 merge: function(hash) {
842 return $H(hash).inject(this, function(mergedHash, pair) {
843 mergedHash[pair.key] = pair.value;
844 return mergedHash;
845 });
846 },
847
848 remove: function() {
849 var result;
850 for(var i = 0, length = arguments.length; i < length; i++) {
851 var value = this[arguments[i]];
852 if (value !== undefined){
853 if (result === undefined) result = value;
854 else {
855 if (result.constructor != Array) result = [result];
856 result.push(value)
857 }
858 }
859 delete this[arguments[i]];
860 }
861 return result;
862 },
863
864 toQueryString: function() {
865 return Hash.toQueryString(this);
866 },
867
868 inspect: function() {
869 return '#<Hash:{' + this.map(function(pair) {
870 return pair.map(Object.inspect).join(': ');
871 }).join(', ') + '}>';
872 },
873
874 toJSON: function() {
875 return Hash.toJSON(this);
876 }
877});
878
879function $H(object) {
880 if (object instanceof Hash) return object;
881 return new Hash(object);
882};
883
884// Safari iterates over shadowed properties
885if (function() {
886 var i = 0, Test = function(value) { this.key = value };
887 Test.prototype.key = 'foo';
888 for (var property in new Test('bar')) i++;
889 return i > 1;
890}()) Hash.prototype._each = function(iterator) {
891 var cache = [];
892 for (var key in this) {
893 var value = this[key];
894 if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
895 cache.push(key);
896 var pair = [key, value];
897 pair.key = key;
898 pair.value = value;
899 iterator(pair);
900 }
901};
902ObjectRange = Class.create();
903Object.extend(ObjectRange.prototype, Enumerable);
904Object.extend(ObjectRange.prototype, {
905 initialize: function(start, end, exclusive) {
906 this.start = start;
907 this.end = end;
908 this.exclusive = exclusive;
909 },
910
911 _each: function(iterator) {
912 var value = this.start;
913 while (this.include(value)) {
914 iterator(value);
915 value = value.succ();
916 }
917 },
918
919 include: function(value) {
920 if (value < this.start)
921 return false;
922 if (this.exclusive)
923 return value < this.end;
924 return value <= this.end;
925 }
926});
927
928var $R = function(start, end, exclusive) {
929 return new ObjectRange(start, end, exclusive);
930}
931
932var Ajax = {
933 getTransport: function() {
934 return Try.these(
935 function() {return new XMLHttpRequest()},
936 function() {return new ActiveXObject('Msxml2.XMLHTTP')},
937 function() {return new ActiveXObject('Microsoft.XMLHTTP')}
938 ) || false;
939 },
940
941 activeRequestCount: 0
942}
943
944Ajax.Responders = {
945 responders: [],
946
947 _each: function(iterator) {
948 this.responders._each(iterator);
949 },
950
951 register: function(responder) {
952 if (!this.include(responder))
953 this.responders.push(responder);
954 },
955
956 unregister: function(responder) {
957 this.responders = this.responders.without(responder);
958 },
959
960 dispatch: function(callback, request, transport, json) {
961 this.each(function(responder) {
962 if (typeof responder[callback] == 'function') {
963 try {
964 responder[callback].apply(responder, [request, transport, json]);
965 } catch (e) {}
966 }
967 });
968 }
969};
970
971Object.extend(Ajax.Responders, Enumerable);
972
973Ajax.Responders.register({
974 onCreate: function() {
975 Ajax.activeRequestCount++;
976 },
977 onComplete: function() {
978 Ajax.activeRequestCount--;
979 }
980});
981
982Ajax.Base = function() {};
983Ajax.Base.prototype = {
984 setOptions: function(options) {
985 this.options = {
986 method: 'post',
987 asynchronous: true,
988 contentType: 'application/x-www-form-urlencoded',
989 encoding: 'UTF-8',
990 parameters: ''
991 }
992 Object.extend(this.options, options || {});
993
994 this.options.method = this.options.method.toLowerCase();
995 if (typeof this.options.parameters == 'string')
996 this.options.parameters = this.options.parameters.toQueryParams();
997 }
998}
999
1000Ajax.Request = Class.create();
1001Ajax.Request.Events =
1002 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1003
1004Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
1005 _complete: false,
1006
1007 initialize: function(url, options) {
1008 this.transport = Ajax.getTransport();
1009 this.setOptions(options);
1010 this.request(url);
1011 },
1012
1013 request: function(url) {
1014 this.url = url;
1015 this.method = this.options.method;
1016 var params = Object.clone(this.options.parameters);
1017
1018 if (!['get', 'post'].include(this.method)) {
1019 // simulate other verbs over post
1020 params['_method'] = this.method;
1021 this.method = 'post';
1022 }
1023
1024 this.parameters = params;
1025
1026 if (params = Hash.toQueryString(params)) {
1027 // when GET, append parameters to URL
1028 if (this.method == 'get')
1029 this.url += (this.url.include('?') ? '&' : '?') + params;
1030 else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1031 params += '&_=';
1032 }
1033
1034 try {
1035 if (this.options.onCreate) this.options.onCreate(this.transport);
1036 Ajax.Responders.dispatch('onCreate', this, this.transport);
1037
1038 this.transport.open(this.method.toUpperCase(), this.url,
1039 this.options.asynchronous);
1040
1041 if (this.options.asynchronous)
1042 setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
1043
1044 this.transport.onreadystatechange = this.onStateChange.bind(this);
1045 this.setRequestHeaders();
1046
1047 this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1048 this.transport.send(this.body);
1049
1050 /* Force Firefox to handle ready state 4 for synchronous requests */
1051 if (!this.options.asynchronous && this.transport.overrideMimeType)
1052 this.onStateChange();
1053
1054 }
1055 catch (e) {
1056 this.dispatchException(e);
1057 }
1058 },
1059
1060 onStateChange: function() {
1061 var readyState = this.transport.readyState;
1062 if (readyState > 1 && !((readyState == 4) && this._complete))
1063 this.respondToReadyState(this.transport.readyState);
1064 },
1065
1066 setRequestHeaders: function() {
1067 var headers = {
1068 'X-Requested-With': 'XMLHttpRequest',
1069 'X-Prototype-Version': Prototype.Version,
1070 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1071 };
1072
1073 if (this.method == 'post') {
1074 headers['Content-type'] = this.options.contentType +
1075 (this.options.encoding ? '; charset=' + this.options.encoding : '');
1076
1077 /* Force "Connection: close" for older Mozilla browsers to work
1078 * around a bug where XMLHttpRequest sends an incorrect
1079 * Content-length header. See Mozilla Bugzilla #246651.
1080 */
1081 if (this.transport.overrideMimeType &&
1082 (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1083 headers['Connection'] = 'close';
1084 }
1085
1086 // user-defined headers
1087 if (typeof this.options.requestHeaders == 'object') {
1088 var extras = this.options.requestHeaders;
1089
1090 if (typeof extras.push == 'function')
1091 for (var i = 0, length = extras.length; i < length; i += 2)
1092 headers[extras[i]] = extras[i+1];
1093 else
1094 $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1095 }
1096
1097 for (var name in headers)
1098 this.transport.setRequestHeader(name, headers[name]);
1099 },
1100
1101 success: function() {
1102 return !this.transport.status
1103 || (this.transport.status >= 200 && this.transport.status < 300);
1104 },
1105
1106 respondToReadyState: function(readyState) {
1107 var state = Ajax.Request.Events[readyState];
1108 var transport = this.transport, json = this.evalJSON();
1109
1110 if (state == 'Complete') {
1111 try {
1112 this._complete = true;
1113 (this.options['on' + this.transport.status]
1114 || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1115 || Prototype.emptyFunction)(transport, json);
1116 } catch (e) {
1117 this.dispatchException(e);
1118 }
1119
1120 var contentType = this.getHeader('Content-type');
1121 if (contentType && contentType.strip().
1122 match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
1123 this.evalResponse();
1124 }
1125
1126 try {
1127 (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
1128 Ajax.Responders.dispatch('on' + state, this, transport, json);
1129 } catch (e) {
1130 this.dispatchException(e);
1131 }
1132
1133 if (state == 'Complete') {
1134 // avoid memory leak in MSIE: clean up
1135 this.transport.onreadystatechange = Prototype.emptyFunction;
1136 }
1137 },
1138
1139 getHeader: function(name) {
1140 try {
1141 return this.transport.getResponseHeader(name);
1142 } catch (e) { return null }
1143 },
1144
1145 evalJSON: function() {
1146 try {
1147 var json = this.getHeader('X-JSON');
1148 return json ? json.evalJSON() : null;
1149 } catch (e) { return null }
1150 },
1151
1152 evalResponse: function() {
1153 try {
1154 return eval((this.transport.responseText || '').unfilterJSON());
1155 } catch (e) {
1156 this.dispatchException(e);
1157 }
1158 },
1159
1160 dispatchException: function(exception) {
1161 (this.options.onException || Prototype.emptyFunction)(this, exception);
1162 Ajax.Responders.dispatch('onException', this, exception);
1163 }
1164});
1165
1166Ajax.Updater = Class.create();
1167
1168Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
1169 initialize: function(container, url, options) {
1170 this.container = {
1171 success: (container.success || container),
1172 failure: (container.failure || (container.success ? null : container))
1173 }
1174
1175 this.transport = Ajax.getTransport();
1176 this.setOptions(options);
1177
1178 var onComplete = this.options.onComplete || Prototype.emptyFunction;
1179 this.options.onComplete = (function(transport, param) {
1180 this.updateContent();
1181 onComplete(transport, param);
1182 }).bind(this);
1183
1184 this.request(url);
1185 },
1186
1187 updateContent: function() {
1188 var receiver = this.container[this.success() ? 'success' : 'failure'];
1189 var response = this.transport.responseText;
1190
1191 if (!this.options.evalScripts) response = response.stripScripts();
1192
1193 if (receiver = $(receiver)) {
1194 if (this.options.insertion)
1195 new this.options.insertion(receiver, response);
1196 else
1197 receiver.update(response);
1198 }
1199
1200 if (this.success()) {
1201 if (this.onComplete)
1202 setTimeout(this.onComplete.bind(this), 10);
1203 }
1204 }
1205});
1206
1207Ajax.PeriodicalUpdater = Class.create();
1208Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
1209 initialize: function(container, url, options) {
1210 this.setOptions(options);
1211 this.onComplete = this.options.onComplete;
1212
1213 this.frequency = (this.options.frequency || 2);
1214 this.decay = (this.options.decay || 1);
1215
1216 this.updater = {};
1217 this.container = container;
1218 this.url = url;
1219
1220 this.start();
1221 },
1222
1223 start: function() {
1224 this.options.onComplete = this.updateComplete.bind(this);
1225 this.onTimerEvent();
1226 },
1227
1228 stop: function() {
1229 this.updater.options.onComplete = undefined;
1230 clearTimeout(this.timer);
1231 (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1232 },
1233
1234 updateComplete: function(request) {
1235 if (this.options.decay) {
1236 this.decay = (request.responseText == this.lastText ?
1237 this.decay * this.options.decay : 1);
1238
1239 this.lastText = request.responseText;
1240 }
1241 this.timer = setTimeout(this.onTimerEvent.bind(this),
1242 this.decay * this.frequency * 1000);
1243 },
1244
1245 onTimerEvent: function() {
1246 this.updater = new Ajax.Updater(this.container, this.url, this.options);
1247 }
1248});
1249function $(element) {
1250 if (arguments.length > 1) {
1251 for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1252 elements.push($(arguments[i]));
1253 return elements;
1254 }
1255 if (typeof element == 'string')
1256 element = document.getElementById(element);
1257 return Element.extend(element);
1258}
1259
1260if (Prototype.BrowserFeatures.XPath) {
1261 document._getElementsByXPath = function(expression, parentElement) {
1262 var results = [];
1263 var query = document.evaluate(expression, $(parentElement) || document,
1264 null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1265 for (var i = 0, length = query.snapshotLength; i < length; i++)
1266 results.push(query.snapshotItem(i));
1267 return results;
1268 };
1269
1270 document.getElementsByClassName = function(className, parentElement) {
1271 var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
1272 return document._getElementsByXPath(q, parentElement);
1273 }
1274
1275} else document.getElementsByClassName = function(className, parentElement) {
1276 var children = ($(parentElement) || document.body).getElementsByTagName('*');
1277 var elements = [], child, pattern = new RegExp("(^|\\s)" + className + "(\\s|$)");
1278 for (var i = 0, length = children.length; i < length; i++) {
1279 child = children[i];
1280 var elementClassName = child.className;
1281 if (elementClassName.length == 0) continue;
1282 if (elementClassName == className || elementClassName.match(pattern))
1283 elements.push(Element.extend(child));
1284 }
1285 return elements;
1286};
1287
1288/*--------------------------------------------------------------------------*/
1289
1290if (!window.Element) var Element = {};
1291
1292Element.extend = function(element) {
1293 var F = Prototype.BrowserFeatures;
1294 if (!element || !element.tagName || element.nodeType == 3 ||
1295 element._extended || F.SpecificElementExtensions || element == window)
1296 return element;
1297
1298 var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
1299 T = Element.Methods.ByTag;
1300
1301 // extend methods for all tags (Safari doesn't need this)
1302 if (!F.ElementExtensions) {
1303 Object.extend(methods, Element.Methods),
1304 Object.extend(methods, Element.Methods.Simulated);
1305 }
1306
1307 // extend methods for specific tags
1308 if (T[tagName]) Object.extend(methods, T[tagName]);
1309
1310 for (var property in methods) {
1311 var value = methods[property];
1312 if (typeof value == 'function' && !(property in element))
1313 element[property] = cache.findOrStore(value);
1314 }
1315
1316 element._extended = Prototype.emptyFunction;
1317 return element;
1318};
1319
1320Element.extend.cache = {
1321 findOrStore: function(value) {
1322 return this[value] = this[value] || function() {
1323 return value.apply(null, [this].concat($A(arguments)));
1324 }
1325 }
1326};
1327
1328Element.Methods = {
1329 visible: function(element) {
1330 return $(element).style.display != 'none';
1331 },
1332
1333 toggle: function(element) {
1334 element = $(element);
1335 Element[Element.visible(element) ? 'hide' : 'show'](element);
1336 return element;
1337 },
1338
1339 hide: function(element) {
1340 $(element).style.display = 'none';
1341 return element;
1342 },
1343
1344 show: function(element) {
1345 $(element).style.display = '';
1346 return element;
1347 },
1348
1349 remove: function(element) {
1350 element = $(element);
1351 element.parentNode.removeChild(element);
1352 return element;
1353 },
1354
1355 update: function(element, html) {
1356 html = typeof html == 'undefined' ? '' : html.toString();
1357 $(element).innerHTML = html.stripScripts();
1358 setTimeout(function() {html.evalScripts()}, 10);
1359 return element;
1360 },
1361
1362 replace: function(element, html) {
1363 element = $(element);
1364 html = typeof html == 'undefined' ? '' : html.toString();
1365 if (element.outerHTML) {
1366 element.outerHTML = html.stripScripts();
1367 } else {
1368 var range = element.ownerDocument.createRange();
1369 range.selectNodeContents(element);
1370 element.parentNode.replaceChild(
1371 range.createContextualFragment(html.stripScripts()), element);
1372 }
1373 setTimeout(function() {html.evalScripts()}, 10);
1374 return element;
1375 },
1376
1377 inspect: function(element) {
1378 element = $(element);
1379 var result = '<' + element.tagName.toLowerCase();
1380 $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1381 var property = pair.first(), attribute = pair.last();
1382 var value = (element[property] || '').toString();
1383 if (value) result += ' ' + attribute + '=' + value.inspect(true);
1384 });
1385 return result + '>';
1386 },
1387
1388 recursivelyCollect: function(element, property) {
1389 element = $(element);
1390 var elements = [];
1391 while (element = element[property])
1392 if (element.nodeType == 1)
1393 elements.push(Element.extend(element));
1394 return elements;
1395 },
1396
1397 ancestors: function(element) {
1398 return $(element).recursivelyCollect('parentNode');
1399 },
1400
1401 descendants: function(element) {
1402 return $A($(element).getElementsByTagName('*')).each(Element.extend);
1403 },
1404
1405 firstDescendant: function(element) {
1406 element = $(element).firstChild;
1407 while (element && element.nodeType != 1) element = element.nextSibling;
1408 return $(element);
1409 },
1410
1411 immediateDescendants: function(element) {
1412 if (!(element = $(element).firstChild)) return [];
1413 while (element && element.nodeType != 1) element = element.nextSibling;
1414 if (element) return [element].concat($(element).nextSiblings());
1415 return [];
1416 },
1417
1418 previousSiblings: function(element) {
1419 return $(element).recursivelyCollect('previousSibling');
1420 },
1421
1422 nextSiblings: function(element) {
1423 return $(element).recursivelyCollect('nextSibling');
1424 },
1425
1426 siblings: function(element) {
1427 element = $(element);
1428 return element.previousSiblings().reverse().concat(element.nextSiblings());
1429 },
1430
1431 match: function(element, selector) {
1432 if (typeof selector == 'string')
1433 selector = new Selector(selector);
1434 return selector.match($(element));
1435 },
1436
1437 up: function(element, expression, index) {
1438 element = $(element);
1439 if (arguments.length == 1) return $(element.parentNode);
1440 var ancestors = element.ancestors();
1441 return expression ? Selector.findElement(ancestors, expression, index) :
1442 ancestors[index || 0];
1443 },
1444
1445 down: function(element, expression, index) {
1446 element = $(element);
1447 if (arguments.length == 1) return element.firstDescendant();
1448 var descendants = element.descendants();
1449 return expression ? Selector.findElement(descendants, expression, index) :
1450 descendants[index || 0];
1451 },
1452
1453 previous: function(element, expression, index) {
1454 element = $(element);
1455 if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
1456 var previousSiblings = element.previousSiblings();
1457 return expression ? Selector.findElement(previousSiblings, expression, index) :
1458 previousSiblings[index || 0];
1459 },
1460
1461 next: function(element, expression, index) {
1462 element = $(element);
1463 if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
1464 var nextSiblings = element.nextSiblings();
1465 return expression ? Selector.findElement(nextSiblings, expression, index) :
1466 nextSiblings[index || 0];
1467 },
1468
1469 getElementsBySelector: function() {
1470 var args = $A(arguments), element = $(args.shift());
1471 return Selector.findChildElements(element, args);
1472 },
1473
1474 getElementsByClassName: function(element, className) {
1475 return document.getElementsByClassName(className, element);
1476 },
1477
1478 readAttribute: function(element, name) {
1479 element = $(element);
1480 if (Prototype.Browser.IE) {
1481 if (!element.attributes) return null;
1482 var t = Element._attributeTranslations;
1483 if (t.values[name]) return t.values[name](element, name);
1484 if (t.names[name]) name = t.names[name];
1485 var attribute = element.attributes[name];
1486 return attribute ? attribute.nodeValue : null;
1487 }
1488 return element.getAttribute(name);
1489 },
1490
1491 getHeight: function(element) {
1492 return $(element).getDimensions().height;
1493 },
1494
1495 getWidth: function(element) {
1496 return $(element).getDimensions().width;
1497 },
1498
1499 classNames: function(element) {
1500 return new Element.ClassNames(element);
1501 },
1502
1503 hasClassName: function(element, className) {
1504 if (!(element = $(element))) return;
1505 var elementClassName = element.className;
1506 if (elementClassName.length == 0) return false;
1507 if (elementClassName == className ||
1508 elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
1509 return true;
1510 return false;
1511 },
1512
1513 addClassName: function(element, className) {
1514 if (!(element = $(element))) return;
1515 Element.classNames(element).add(className);
1516 return element;
1517 },
1518
1519 removeClassName: function(element, className) {
1520 if (!(element = $(element))) return;
1521 Element.classNames(element).remove(className);
1522 return element;
1523 },
1524
1525 toggleClassName: function(element, className) {
1526 if (!(element = $(element))) return;
1527 Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
1528 return element;
1529 },
1530
1531 observe: function() {
1532 Event.observe.apply(Event, arguments);
1533 return $A(arguments).first();
1534 },
1535
1536 stopObserving: function() {
1537 Event.stopObserving.apply(Event, arguments);
1538 return $A(arguments).first();
1539 },
1540
1541 // removes whitespace-only text node children
1542 cleanWhitespace: function(element) {
1543 element = $(element);
1544 var node = element.firstChild;
1545 while (node) {
1546 var nextNode = node.nextSibling;
1547 if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
1548 element.removeChild(node);
1549 node = nextNode;
1550 }
1551 return element;
1552 },
1553
1554 empty: function(element) {
1555 return $(element).innerHTML.blank();
1556 },
1557
1558 descendantOf: function(element, ancestor) {
1559 element = $(element), ancestor = $(ancestor);
1560 while (element = element.parentNode)
1561 if (element == ancestor) return true;
1562 return false;
1563 },
1564
1565 scrollTo: function(element) {
1566 element = $(element);
1567 var pos = Position.cumulativeOffset(element);
1568 window.scrollTo(pos[0], pos[1]);
1569 return element;
1570 },
1571
1572 getStyle: function(element, style) {
1573 element = $(element);
1574 style = style == 'float' ? 'cssFloat' : style.camelize();
1575 var value = element.style[style];
1576 if (!value) {
1577 var css = document.defaultView.getComputedStyle(element, null);
1578 value = css ? css[style] : null;
1579 }
1580 if (style == 'opacity') return value ? parseFloat(value) : 1.0;
1581 return value == 'auto' ? null : value;
1582 },
1583
1584 getOpacity: function(element) {
1585 return $(element).getStyle('opacity');
1586 },
1587
1588 setStyle: function(element, styles, camelized) {
1589 element = $(element);
1590 var elementStyle = element.style;
1591
1592 for (var property in styles)
1593 if (property == 'opacity') element.setOpacity(styles[property])
1594 else
1595 elementStyle[(property == 'float' || property == 'cssFloat') ?
1596 (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
1597 (camelized ? property : property.camelize())] = styles[property];
1598
1599 return element;
1600 },
1601
1602 setOpacity: function(element, value) {
1603 element = $(element);
1604 element.style.opacity = (value == 1 || value === '') ? '' :
1605 (value < 0.00001) ? 0 : value;
1606 return element;
1607 },
1608
1609 getDimensions: function(element) {
1610 element = $(element);
1611 var display = $(element).getStyle('display');
1612 if (display != 'none' && display != null) // Safari bug
1613 return {width: element.offsetWidth, height: element.offsetHeight};
1614
1615 // All *Width and *Height properties give 0 on elements with display none,
1616 // so enable the element temporarily
1617 var els = element.style;
1618 var originalVisibility = els.visibility;
1619 var originalPosition = els.position;
1620 var originalDisplay = els.display;
1621 els.visibility = 'hidden';
1622 els.position = 'absolute';
1623 els.display = 'block';
1624 var originalWidth = element.clientWidth;
1625 var originalHeight = element.clientHeight;
1626 els.display = originalDisplay;
1627 els.position = originalPosition;
1628 els.visibility = originalVisibility;
1629 return {width: originalWidth, height: originalHeight};
1630 },
1631
1632 makePositioned: function(element) {
1633 element = $(element);
1634 var pos = Element.getStyle(element, 'position');
1635 if (pos == 'static' || !pos) {
1636 element._madePositioned = true;
1637 element.style.position = 'relative';
1638 // Opera returns the offset relative to the positioning context, when an
1639 // element is position relative but top and left have not been defined
1640 if (window.opera) {
1641 element.style.top = 0;
1642 element.style.left = 0;
1643 }
1644 }
1645 return element;
1646 },
1647
1648 undoPositioned: function(element) {
1649 element = $(element);
1650 if (element._madePositioned) {
1651 element._madePositioned = undefined;
1652 element.style.position =
1653 element.style.top =
1654 element.style.left =
1655 element.style.bottom =
1656 element.style.right = '';
1657 }
1658 return element;
1659 },
1660
1661 makeClipping: function(element) {
1662 element = $(element);
1663 if (element._overflow) return element;
1664 element._overflow = element.style.overflow || 'auto';
1665 if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1666 element.style.overflow = 'hidden';
1667 return element;
1668 },
1669
1670 undoClipping: function(element) {
1671 element = $(element);
1672 if (!element._overflow) return element;
1673 element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
1674 element._overflow = null;
1675 return element;
1676 }
1677};
1678
1679Object.extend(Element.Methods, {
1680 childOf: Element.Methods.descendantOf,
1681 childElements: Element.Methods.immediateDescendants
1682});
1683
1684if (Prototype.Browser.Opera) {
1685 Element.Methods._getStyle = Element.Methods.getStyle;
1686 Element.Methods.getStyle = function(element, style) {
1687 switch(style) {
1688 case 'left':
1689 case 'top':
1690 case 'right':
1691 case 'bottom':
1692 if (Element._getStyle(element, 'position') == 'static') return null;
1693 default: return Element._getStyle(element, style);
1694 }
1695 };
1696}
1697else if (Prototype.Browser.IE) {
1698 Element.Methods.getStyle = function(element, style) {
1699 element = $(element);
1700 style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
1701 var value = element.style[style];
1702 if (!value && element.currentStyle) value = element.currentStyle[style];
1703
1704 if (style == 'opacity') {
1705 if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
1706 if (value[1]) return parseFloat(value[1]) / 100;
1707 return 1.0;
1708 }
1709
1710 if (value == 'auto') {
1711 if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
1712 return element['offset'+style.capitalize()] + 'px';
1713 return null;
1714 }
1715 return value;
1716 };
1717
1718 Element.Methods.setOpacity = function(element, value) {
1719 element = $(element);
1720 var filter = element.getStyle('filter'), style = element.style;
1721 if (value == 1 || value === '') {
1722 style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
1723 return element;
1724 } else if (value < 0.00001) value = 0;
1725 style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') +
1726 'alpha(opacity=' + (value * 100) + ')';
1727 return element;
1728 };
1729
1730 // IE is missing .innerHTML support for TABLE-related elements
1731 Element.Methods.update = function(element, html) {
1732 element = $(element);
1733 html = typeof html == 'undefined' ? '' : html.toString();
1734 var tagName = element.tagName.toUpperCase();
1735 if (['THEAD','TBODY','TR','TD'].include(tagName)) {
1736 var div = document.createElement('div');
1737 switch (tagName) {
1738 case 'THEAD':
1739 case 'TBODY':
1740 div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>';
1741 depth = 2;
1742 break;
1743 case 'TR':
1744 div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>';
1745 depth = 3;
1746 break;
1747 case 'TD':
1748 div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>';
1749 depth = 4;
1750 }
1751 $A(element.childNodes).each(function(node) { element.removeChild(node) });
1752 depth.times(function() { div = div.firstChild });
1753 $A(div.childNodes).each(function(node) { element.appendChild(node) });
1754 } else {
1755 element.innerHTML = html.stripScripts();
1756 }
1757 setTimeout(function() { html.evalScripts() }, 10);
1758 return element;
1759 }
1760}
1761else if (Prototype.Browser.Gecko) {
1762 Element.Methods.setOpacity = function(element, value) {
1763 element = $(element);
1764 element.style.opacity = (value == 1) ? 0.999999 :
1765 (value === '') ? '' : (value < 0.00001) ? 0 : value;
1766 return element;
1767 };
1768}
1769
1770Element._attributeTranslations = {
1771 names: {
1772 colspan: "colSpan",
1773 rowspan: "rowSpan",
1774 valign: "vAlign",
1775 datetime: "dateTime",
1776 accesskey: "accessKey",
1777 tabindex: "tabIndex",
1778 enctype: "encType",
1779 maxlength: "maxLength",
1780 readonly: "readOnly",
1781 longdesc: "longDesc"
1782 },
1783 values: {
1784 _getAttr: function(element, attribute) {
1785 return element.getAttribute(attribute, 2);
1786 },
1787 _flag: function(element, attribute) {
1788 return $(element).hasAttribute(attribute) ? attribute : null;
1789 },
1790 style: function(element) {
1791 return element.style.cssText.toLowerCase();
1792 },
1793 title: function(element) {
1794 var node = element.getAttributeNode('title');
1795 return node.specified ? node.nodeValue : null;
1796 }
1797 }
1798};
1799
1800(function() {
1801 Object.extend(this, {
1802 href: this._getAttr,
1803 src: this._getAttr,
1804 type: this._getAttr,
1805 disabled: this._flag,
1806 checked: this._flag,
1807 readonly: this._flag,
1808 multiple: this._flag
1809 });
1810}).call(Element._attributeTranslations.values);
1811
1812Element.Methods.Simulated = {
1813 hasAttribute: function(element, attribute) {
1814 var t = Element._attributeTranslations, node;
1815 attribute = t.names[attribute] || attribute;
1816 node = $(element).getAttributeNode(attribute);
1817 return node && node.specified;
1818 }
1819};
1820
1821Element.Methods.ByTag = {};
1822
1823Object.extend(Element, Element.Methods);
1824
1825if (!Prototype.BrowserFeatures.ElementExtensions &&
1826 document.createElement('div').__proto__) {
1827 window.HTMLElement = {};
1828 window.HTMLElement.prototype = document.createElement('div').__proto__;
1829 Prototype.BrowserFeatures.ElementExtensions = true;
1830}
1831
1832Element.hasAttribute = function(element, attribute) {
1833 if (element.hasAttribute) return element.hasAttribute(attribute);
1834 return Element.Methods.Simulated.hasAttribute(element, attribute);
1835};
1836
1837Element.addMethods = function(methods) {
1838 var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
1839
1840 if (!methods) {
1841 Object.extend(Form, Form.Methods);
1842 Object.extend(Form.Element, Form.Element.Methods);
1843 Object.extend(Element.Methods.ByTag, {
1844 "FORM": Object.clone(Form.Methods),
1845 "INPUT": Object.clone(Form.Element.Methods),
1846 "SELECT": Object.clone(Form.Element.Methods),
1847 "TEXTAREA": Object.clone(Form.Element.Methods)
1848 });
1849 }
1850
1851 if (arguments.length == 2) {
1852 var tagName = methods;
1853 methods = arguments[1];
1854 }
1855
1856 if (!tagName) Object.extend(Element.Methods, methods || {});
1857 else {
1858 if (tagName.constructor == Array) tagName.each(extend);
1859 else extend(tagName);
1860 }
1861
1862 function extend(tagName) {
1863 tagName = tagName.toUpperCase();
1864 if (!Element.Methods.ByTag[tagName])
1865 Element.Methods.ByTag[tagName] = {};
1866 Object.extend(Element.Methods.ByTag[tagName], methods);
1867 }
1868
1869 function copy(methods, destination, onlyIfAbsent) {
1870 onlyIfAbsent = onlyIfAbsent || false;
1871 var cache = Element.extend.cache;
1872 for (var property in methods) {
1873 var value = methods[property];
1874 if (!onlyIfAbsent || !(property in destination))
1875 destination[property] = cache.findOrStore(value);
1876 }
1877 }
1878
1879 function findDOMClass(tagName) {
1880 var klass;
1881 var trans = {
1882 "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
1883 "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
1884 "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
1885 "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
1886 "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
1887 "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
1888 "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
1889 "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
1890 "FrameSet", "IFRAME": "IFrame"
1891 };
1892 if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
1893 if (window[klass]) return window[klass];
1894 klass = 'HTML' + tagName + 'Element';
1895 if (window[klass]) return window[klass];
1896 klass = 'HTML' + tagName.capitalize() + 'Element';
1897 if (window[klass]) return window[klass];
1898
1899 window[klass] = {};
1900 window[klass].prototype = document.createElement(tagName).__proto__;
1901 return window[klass];
1902 }
1903
1904 if (F.ElementExtensions) {
1905 copy(Element.Methods, HTMLElement.prototype);
1906 copy(Element.Methods.Simulated, HTMLElement.prototype, true);
1907 }
1908
1909 if (F.SpecificElementExtensions) {
1910 for (var tag in Element.Methods.ByTag) {
1911 var klass = findDOMClass(tag);
1912 if (typeof klass == "undefined") continue;
1913 copy(T[tag], klass.prototype);
1914 }
1915 }
1916
1917 Object.extend(Element, Element.Methods);
1918 delete Element.ByTag;
1919};
1920
1921var Toggle = { display: Element.toggle };
1922
1923/*--------------------------------------------------------------------------*/
1924
1925Abstract.Insertion = function(adjacency) {
1926 this.adjacency = adjacency;
1927}
1928
1929Abstract.Insertion.prototype = {
1930 initialize: function(element, content) {
1931 this.element = $(element);
1932 this.content = content.stripScripts();
1933
1934 if (this.adjacency && this.element.insertAdjacentHTML) {
1935 try {
1936 this.element.insertAdjacentHTML(this.adjacency, this.content);
1937 } catch (e) {
1938 var tagName = this.element.tagName.toUpperCase();
1939 if (['TBODY', 'TR'].include(tagName)) {
1940 this.insertContent(this.contentFromAnonymousTable());
1941 } else {
1942 throw e;
1943 }
1944 }
1945 } else {
1946 this.range = this.element.ownerDocument.createRange();
1947 if (this.initializeRange) this.initializeRange();
1948 this.insertContent([this.range.createContextualFragment(this.content)]);
1949 }
1950
1951 setTimeout(function() {content.evalScripts()}, 10);
1952 },
1953
1954 contentFromAnonymousTable: function() {
1955 var div = document.createElement('div');
1956 div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
1957 return $A(div.childNodes[0].childNodes[0].childNodes);
1958 }
1959}
1960
1961var Insertion = new Object();
1962
1963Insertion.Before = Class.create();
1964Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
1965 initializeRange: function() {
1966 this.range.setStartBefore(this.element);
1967 },
1968
1969 insertContent: function(fragments) {
1970 fragments.each((function(fragment) {
1971 this.element.parentNode.insertBefore(fragment, this.element);
1972 }).bind(this));
1973 }
1974});
1975
1976Insertion.Top = Class.create();
1977Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
1978 initializeRange: function() {
1979 this.range.selectNodeContents(this.element);
1980 this.range.collapse(true);
1981 },
1982
1983 insertContent: function(fragments) {
1984 fragments.reverse(false).each((function(fragment) {
1985 this.element.insertBefore(fragment, this.element.firstChild);
1986 }).bind(this));
1987 }
1988});
1989
1990Insertion.Bottom = Class.create();
1991Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
1992 initializeRange: function() {
1993 this.range.selectNodeContents(this.element);
1994 this.range.collapse(this.element);
1995 },
1996
1997 insertContent: function(fragments) {
1998 fragments.each((function(fragment) {
1999 this.element.appendChild(fragment);
2000 }).bind(this));
2001 }
2002});
2003
2004Insertion.After = Class.create();
2005Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
2006 initializeRange: function() {
2007 this.range.setStartAfter(this.element);
2008 },
2009
2010 insertContent: function(fragments) {
2011 fragments.each((function(fragment) {
2012 this.element.parentNode.insertBefore(fragment,
2013 this.element.nextSibling);
2014 }).bind(this));
2015 }
2016});
2017
2018/*--------------------------------------------------------------------------*/
2019
2020Element.ClassNames = Class.create();
2021Element.ClassNames.prototype = {
2022 initialize: function(element) {
2023 this.element = $(element);
2024 },
2025
2026 _each: function(iterator) {
2027 this.element.className.split(/\s+/).select(function(name) {
2028 return name.length > 0;
2029 })._each(iterator);
2030 },
2031
2032 set: function(className) {
2033 this.element.className = className;
2034 },
2035
2036 add: function(classNameToAdd) {
2037 if (this.include(classNameToAdd)) return;
2038 this.set($A(this).concat(classNameToAdd).join(' '));
2039 },
2040
2041 remove: function(classNameToRemove) {
2042 if (!this.include(classNameToRemove)) return;
2043 this.set($A(this).without(classNameToRemove).join(' '));
2044 },
2045
2046 toString: function() {
2047 return $A(this).join(' ');
2048 }
2049};
2050
2051Object.extend(Element.ClassNames.prototype, Enumerable);
2052/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
2053 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
2054 * license. Please see http://www.yui-ext.com/ for more information. */
2055
2056var Selector = Class.create();
2057
2058Selector.prototype = {
2059 initialize: function(expression) {
2060 this.expression = expression.strip();
2061 this.compileMatcher();
2062 },
2063
2064 compileMatcher: function() {
2065 // Selectors with namespaced attributes can't use the XPath version
2066 if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression))
2067 return this.compileXPathMatcher();
2068
2069 var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
2070 c = Selector.criteria, le, p, m;
2071
2072 if (Selector._cache[e]) {
2073 this.matcher = Selector._cache[e]; return;
2074 }
2075 this.matcher = ["this.matcher = function(root) {",
2076 "var r = root, h = Selector.handlers, c = false, n;"];
2077
2078 while (e && le != e && (/\S/).test(e)) {
2079 le = e;
2080 for (var i in ps) {
2081 p = ps[i];
2082 if (m = e.match(p)) {
2083 this.matcher.push(typeof c[i] == 'function' ? c[i](m) :
2084 new Template(c[i]).evaluate(m));
2085 e = e.replace(m[0], '');
2086 break;
2087 }
2088 }
2089 }
2090
2091 this.matcher.push("return h.unique(n);\n}");
2092 eval(this.matcher.join('\n'));
2093 Selector._cache[this.expression] = this.matcher;
2094 },
2095
2096 compileXPathMatcher: function() {
2097 var e = this.expression, ps = Selector.patterns,
2098 x = Selector.xpath, le, m;
2099
2100 if (Selector._cache[e]) {
2101 this.xpath = Selector._cache[e]; return;
2102 }
2103
2104 this.matcher = ['.//*'];
2105 while (e && le != e && (/\S/).test(e)) {
2106 le = e;
2107 for (var i in ps) {
2108 if (m = e.match(ps[i])) {
2109 this.matcher.push(typeof x[i] == 'function' ? x[i](m) :
2110 new Template(x[i]).evaluate(m));
2111 e = e.replace(m[0], '');
2112 break;
2113 }
2114 }
2115 }
2116
2117 this.xpath = this.matcher.join('');
2118 Selector._cache[this.expression] = this.xpath;
2119 },
2120
2121 findElements: function(root) {
2122 root = root || document;
2123 if (this.xpath) return document._getElementsByXPath(this.xpath, root);
2124 return this.matcher(root);
2125 },
2126
2127 match: function(element) {
2128 return this.findElements(document).include(element);
2129 },
2130
2131 toString: function() {
2132 return this.expression;
2133 },
2134
2135 inspect: function() {
2136 return "#<Selector:" + this.expression.inspect() + ">";
2137 }
2138};
2139
2140Object.extend(Selector, {
2141 _cache: {},
2142
2143 xpath: {
2144 descendant: "//*",
2145 child: "/*",
2146 adjacent: "/following-sibling::*[1]",
2147 laterSibling: '/following-sibling::*',
2148 tagName: function(m) {
2149 if (m[1] == '*') return '';
2150 return "[local-name()='" + m[1].toLowerCase() +
2151 "' or local-name()='" + m[1].toUpperCase() + "']";
2152 },
2153 className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
2154 id: "[@id='#{1}']",
2155 attrPresence: "[@#{1}]",
2156 attr: function(m) {
2157 m[3] = m[5] || m[6];
2158 return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
2159 },
2160 pseudo: function(m) {
2161 var h = Selector.xpath.pseudos[m[1]];
2162 if (!h) return '';
2163 if (typeof h === 'function') return h(m);
2164 return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
2165 },
2166 operators: {
2167 '=': "[@#{1}='#{3}']",
2168 '!=': "[@#{1}!='#{3}']",
2169 '^=': "[starts-with(@#{1}, '#{3}')]",
2170 '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
2171 '*=': "[contains(@#{1}, '#{3}')]",
2172 '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
2173 '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
2174 },
2175 pseudos: {
2176 'first-child': '[not(preceding-sibling::*)]',
2177 'last-child': '[not(following-sibling::*)]',
2178 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
2179 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
2180 'checked': "[@checked]",
2181 'disabled': "[@disabled]",
2182 'enabled': "[not(@disabled)]",
2183 'not': function(m) {
2184 var e = m[6], p = Selector.patterns,
2185 x = Selector.xpath, le, m, v;
2186
2187 var exclusion = [];
2188 while (e && le != e && (/\S/).test(e)) {
2189 le = e;
2190 for (var i in p) {
2191 if (m = e.match(p[i])) {
2192 v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m);
2193 exclusion.push("(" + v.substring(1, v.length - 1) + ")");
2194 e = e.replace(m[0], '');
2195 break;
2196 }
2197 }
2198 }
2199 return "[not(" + exclusion.join(" and ") + ")]";
2200 },
2201 'nth-child': function(m) {
2202 return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
2203 },
2204 'nth-last-child': function(m) {
2205 return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
2206 },
2207 'nth-of-type': function(m) {
2208 return Selector.xpath.pseudos.nth("position() ", m);
2209 },
2210 'nth-last-of-type': function(m) {
2211 return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
2212 },
2213 'first-of-type': function(m) {
2214 m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
2215 },
2216 'last-of-type': function(m) {
2217 m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
2218 },
2219 'only-of-type': function(m) {
2220 var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
2221 },
2222 nth: function(fragment, m) {
2223 var mm, formula = m[6], predicate;
2224 if (formula == 'even') formula = '2n+0';
2225 if (formula == 'odd') formula = '2n+1';
2226 if (mm = formula.match(/^(\d+)$/)) // digit only
2227 return '[' + fragment + "= " + mm[1] + ']';
2228 if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2229 if (mm[1] == "-") mm[1] = -1;
2230 var a = mm[1] ? Number(mm[1]) : 1;
2231 var b = mm[2] ? Number(mm[2]) : 0;
2232 predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
2233 "((#{fragment} - #{b}) div #{a} >= 0)]";
2234 return new Template(predicate).evaluate({
2235 fragment: fragment, a: a, b: b });
2236 }
2237 }
2238 }
2239 },
2240
2241 criteria: {
2242 tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
2243 className: 'n = h.className(n, r, "#{1}", c); c = false;',
2244 id: 'n = h.id(n, r, "#{1}", c); c = false;',
2245 attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
2246 attr: function(m) {
2247 m[3] = (m[5] || m[6]);
2248 return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
2249 },
2250 pseudo: function(m) {
2251 if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
2252 return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
2253 },
2254 descendant: 'c = "descendant";',
2255 child: 'c = "child";',
2256 adjacent: 'c = "adjacent";',
2257 laterSibling: 'c = "laterSibling";'
2258 },
2259
2260 patterns: {
2261 // combinators must be listed first
2262 // (and descendant needs to be last combinator)
2263 laterSibling: /^\s*~\s*/,
2264 child: /^\s*>\s*/,
2265 adjacent: /^\s*\+\s*/,
2266 descendant: /^\s/,
2267
2268 // selectors follow
2269 tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
2270 id: /^#([\w\-\*]+)(\b|$)/,
2271 className: /^\.([\w\-\*]+)(\b|$)/,
2272 pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s|(?=:))/,
2273 attrPresence: /^\[([\w]+)\]/,
2274 attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/
2275 },
2276
2277 handlers: {
2278 // UTILITY FUNCTIONS
2279 // joins two collections
2280 concat: function(a, b) {
2281 for (var i = 0, node; node = b[i]; i++)
2282 a.push(node);
2283 return a;
2284 },
2285
2286 // marks an array of nodes for counting
2287 mark: function(nodes) {
2288 for (var i = 0, node; node = nodes[i]; i++)
2289 node._counted = true;
2290 return nodes;
2291 },
2292
2293 unmark: function(nodes) {
2294 for (var i = 0, node; node = nodes[i]; i++)
2295 node._counted = undefined;
2296 return nodes;
2297 },
2298
2299 // mark each child node with its position (for nth calls)
2300 // "ofType" flag indicates whether we're indexing for nth-of-type
2301 // rather than nth-child
2302 index: function(parentNode, reverse, ofType) {
2303 parentNode._counted = true;
2304 if (reverse) {
2305 for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
2306 node = nodes[i];
2307 if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
2308 }
2309 } else {
2310 for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
2311 if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
2312 }
2313 },
2314
2315 // filters out duplicates and extends all nodes
2316 unique: function(nodes) {
2317 if (nodes.length == 0) return nodes;
2318 var results = [], n;
2319 for (var i = 0, l = nodes.length; i < l; i++)
2320 if (!(n = nodes[i])._counted) {
2321 n._counted = true;
2322 results.push(Element.extend(n));
2323 }
2324 return Selector.handlers.unmark(results);
2325 },
2326
2327 // COMBINATOR FUNCTIONS
2328 descendant: function(nodes) {
2329 var h = Selector.handlers;
2330 for (var i = 0, results = [], node; node = nodes[i]; i++)
2331 h.concat(results, node.getElementsByTagName('*'));
2332 return results;
2333 },
2334
2335 child: function(nodes) {
2336 var h = Selector.handlers;
2337 for (var i = 0, results = [], node; node = nodes[i]; i++) {
2338 for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
2339 if (child.nodeType == 1 && child.tagName != '!') results.push(child);
2340 }
2341 return results;
2342 },
2343
2344 adjacent: function(nodes) {
2345 for (var i = 0, results = [], node; node = nodes[i]; i++) {
2346 var next = this.nextElementSibling(node);
2347 if (next) results.push(next);
2348 }
2349 return results;
2350 },
2351
2352 laterSibling: function(nodes) {
2353 var h = Selector.handlers;
2354 for (var i = 0, results = [], node; node = nodes[i]; i++)
2355 h.concat(results, Element.nextSiblings(node));
2356 return results;
2357 },
2358
2359 nextElementSibling: function(node) {
2360 while (node = node.nextSibling)
2361 if (node.nodeType == 1) return node;
2362 return null;
2363 },
2364
2365 previousElementSibling: function(node) {
2366 while (node = node.previousSibling)
2367 if (node.nodeType == 1) return node;
2368 return null;
2369 },
2370
2371 // TOKEN FUNCTIONS
2372 tagName: function(nodes, root, tagName, combinator) {
2373 tagName = tagName.toUpperCase();
2374 var results = [], h = Selector.handlers;
2375 if (nodes) {
2376 if (combinator) {
2377 // fastlane for ordinary descendant combinators
2378 if (combinator == "descendant") {
2379 for (var i = 0, node; node = nodes[i]; i++)
2380 h.concat(results, node.getElementsByTagName(tagName));
2381 return results;
2382 } else nodes = this[combinator](nodes);
2383 if (tagName == "*") return nodes;
2384 }
2385 for (var i = 0, node; node = nodes[i]; i++)
2386 if (node.tagName.toUpperCase() == tagName) results.push(node);
2387 return results;
2388 } else return root.getElementsByTagName(tagName);
2389 },
2390
2391 id: function(nodes, root, id, combinator) {
2392 var targetNode = $(id), h = Selector.handlers;
2393 if (!nodes && root == document) return targetNode ? [targetNode] : [];
2394 if (nodes) {
2395 if (combinator) {
2396 if (combinator == 'child') {
2397 for (var i = 0, node; node = nodes[i]; i++)
2398 if (targetNode.parentNode == node) return [targetNode];
2399 } else if (combinator == 'descendant') {
2400 for (var i = 0, node; node = nodes[i]; i++)
2401 if (Element.descendantOf(targetNode, node)) return [targetNode];
2402 } else if (combinator == 'adjacent') {
2403 for (var i = 0, node; node = nodes[i]; i++)
2404 if (Selector.handlers.previousElementSibling(targetNode) == node)
2405 return [targetNode];
2406 } else nodes = h[combinator](nodes);
2407 }
2408 for (var i = 0, node; node = nodes[i]; i++)
2409 if (node == targetNode) return [targetNode];
2410 return [];
2411 }
2412 return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
2413 },
2414
2415 className: function(nodes, root, className, combinator) {
2416 if (nodes && combinator) nodes = this[combinator](nodes);
2417 return Selector.handlers.byClassName(nodes, root, className);
2418 },
2419
2420 byClassName: function(nodes, root, className) {
2421 if (!nodes) nodes = Selector.handlers.descendant([root]);
2422 var needle = ' ' + className + ' ';
2423 for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
2424 nodeClassName = node.className;
2425 if (nodeClassName.length == 0) continue;
2426 if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
2427 results.push(node);
2428 }
2429 return results;
2430 },
2431
2432 attrPresence: function(nodes, root, attr) {
2433 var results = [];
2434 for (var i = 0, node; node = nodes[i]; i++)
2435 if (Element.hasAttribute(node, attr)) results.push(node);
2436 return results;
2437 },
2438
2439 attr: function(nodes, root, attr, value, operator) {
2440 if (!nodes) nodes = root.getElementsByTagName("*");
2441 var handler = Selector.operators[operator], results = [];
2442 for (var i = 0, node; node = nodes[i]; i++) {
2443 var nodeValue = Element.readAttribute(node, attr);
2444 if (nodeValue === null) continue;
2445 if (handler(nodeValue, value)) results.push(node);
2446 }
2447 return results;
2448 },
2449
2450 pseudo: function(nodes, name, value, root, combinator) {
2451 if (nodes && combinator) nodes = this[combinator](nodes);
2452 if (!nodes) nodes = root.getElementsByTagName("*");
2453 return Selector.pseudos[name](nodes, value, root);
2454 }
2455 },
2456
2457 pseudos: {
2458 'first-child': function(nodes, value, root) {
2459 for (var i = 0, results = [], node; node = nodes[i]; i++) {
2460 if (Selector.handlers.previousElementSibling(node)) continue;
2461 results.push(node);
2462 }
2463 return results;
2464 },
2465 'last-child': function(nodes, value, root) {
2466 for (var i = 0, results = [], node; node = nodes[i]; i++) {
2467 if (Selector.handlers.nextElementSibling(node)) continue;
2468 results.push(node);
2469 }
2470 return results;
2471 },
2472 'only-child': function(nodes, value, root) {
2473 var h = Selector.handlers;
2474 for (var i = 0, results = [], node; node = nodes[i]; i++)
2475 if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
2476 results.push(node);
2477 return results;
2478 },
2479 'nth-child': function(nodes, formula, root) {
2480 return Selector.pseudos.nth(nodes, formula, root);
2481 },
2482 'nth-last-child': function(nodes, formula, root) {
2483 return Selector.pseudos.nth(nodes, formula, root, true);
2484 },
2485 'nth-of-type': function(nodes, formula, root) {
2486 return Selector.pseudos.nth(nodes, formula, root, false, true);
2487 },
2488 'nth-last-of-type': function(nodes, formula, root) {
2489 return Selector.pseudos.nth(nodes, formula, root, true, true);
2490 },
2491 'first-of-type': function(nodes, formula, root) {
2492 return Selector.pseudos.nth(nodes, "1", root, false, true);
2493 },
2494 'last-of-type': function(nodes, formula, root) {
2495 return Selector.pseudos.nth(nodes, "1", root, true, true);
2496 },
2497 'only-of-type': function(nodes, formula, root) {
2498 var p = Selector.pseudos;
2499 return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
2500 },
2501
2502 // handles the an+b logic
2503 getIndices: function(a, b, total) {
2504 if (a == 0) return b > 0 ? [b] : [];
2505 return $R(1, total).inject([], function(memo, i) {
2506 if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
2507 return memo;
2508 });
2509 },
2510
2511 // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
2512 nth: function(nodes, formula, root, reverse, ofType) {
2513 if (nodes.length == 0) return [];
2514 if (formula == 'even') formula = '2n+0';
2515 if (formula == 'odd') formula = '2n+1';
2516 var h = Selector.handlers, results = [], indexed = [], m;
2517 h.mark(nodes);
2518 for (var i = 0, node; node = nodes[i]; i++) {
2519 if (!node.parentNode._counted) {
2520 h.index(node.parentNode, reverse, ofType);
2521 indexed.push(node.parentNode);
2522 }
2523 }
2524 if (formula.match(/^\d+$/)) { // just a number
2525 formula = Number(formula);
2526 for (var i = 0, node; node = nodes[i]; i++)
2527 if (node.nodeIndex == formula) results.push(node);
2528 } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2529 if (m[1] == "-") m[1] = -1;
2530 var a = m[1] ? Number(m[1]) : 1;
2531 var b = m[2] ? Number(m[2]) : 0;
2532 var indices = Selector.pseudos.getIndices(a, b, nodes.length);
2533 for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
2534 for (var j = 0; j < l; j++)
2535 if (node.nodeIndex == indices[j]) results.push(node);
2536 }
2537 }
2538 h.unmark(nodes);
2539 h.unmark(indexed);
2540 return results;
2541 },
2542
2543 'empty': function(nodes, value, root) {
2544 for (var i = 0, results = [], node; node = nodes[i]; i++) {
2545 // IE treats comments as element nodes
2546 if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
2547 results.push(node);
2548 }
2549 return results;
2550 },
2551
2552 'not': function(nodes, selector, root) {
2553 var h = Selector.handlers, selectorType, m;
2554 var exclusions = new Selector(selector).findElements(root);
2555 h.mark(exclusions);
2556 for (var i = 0, results = [], node; node = nodes[i]; i++)
2557 if (!node._counted) results.push(node);
2558 h.unmark(exclusions);
2559 return results;
2560 },
2561
2562 'enabled': function(nodes, value, root) {
2563 for (var i = 0, results = [], node; node = nodes[i]; i++)
2564 if (!node.disabled) results.push(node);
2565 return results;
2566 },
2567
2568 'disabled': function(nodes, value, root) {
2569 for (var i = 0, results = [], node; node = nodes[i]; i++)
2570 if (node.disabled) results.push(node);
2571 return results;
2572 },
2573
2574 'checked': function(nodes, value, root) {
2575 for (var i = 0, results = [], node; node = nodes[i]; i++)
2576 if (node.checked) results.push(node);
2577 return results;
2578 }
2579 },
2580
2581 operators: {
2582 '=': function(nv, v) { return nv == v; },
2583 '!=': function(nv, v) { return nv != v; },
2584 '^=': function(nv, v) { return nv.startsWith(v); },
2585 '$=': function(nv, v) { return nv.endsWith(v); },
2586 '*=': function(nv, v) { return nv.include(v); },
2587 '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
2588 '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
2589 },
2590
2591 matchElements: function(elements, expression) {
2592 var matches = new Selector(expression).findElements(), h = Selector.handlers;
2593 h.mark(matches);
2594 for (var i = 0, results = [], element; element = elements[i]; i++)
2595 if (element._counted) results.push(element);
2596 h.unmark(matches);
2597 return results;
2598 },
2599
2600 findElement: function(elements, expression, index) {
2601 if (typeof expression == 'number') {
2602 index = expression; expression = false;
2603 }
2604 return Selector.matchElements(elements, expression || '*')[index || 0];
2605 },
2606
2607 findChildElements: function(element, expressions) {
2608 var exprs = expressions.join(','), expressions = [];
2609 exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
2610 expressions.push(m[1].strip());
2611 });
2612 var results = [], h = Selector.handlers;
2613 for (var i = 0, l = expressions.length, selector; i < l; i++) {
2614 selector = new Selector(expressions[i].strip());
2615 h.concat(results, selector.findElements(element));
2616 }
2617 return (l > 1) ? h.unique(results) : results;
2618 }
2619});
2620
2621function $$() {
2622 return Selector.findChildElements(document, $A(arguments));
2623}
2624var Form = {
2625 reset: function(form) {
2626 $(form).reset();
2627 return form;
2628 },
2629
2630 serializeElements: function(elements, getHash) {
2631 var data = elements.inject({}, function(result, element) {
2632 if (!element.disabled && element.name) {
2633 var key = element.name, value = $(element).getValue();
2634 if (value != null) {
2635 if (key in result) {
2636 if (result[key].constructor != Array) result[key] = [result[key]];
2637 result[key].push(value);
2638 }
2639 else result[key] = value;
2640 }
2641 }
2642 return result;
2643 });
2644
2645 return getHash ? data : Hash.toQueryString(data);
2646 }
2647};
2648
2649Form.Methods = {
2650 serialize: function(form, getHash) {
2651 return Form.serializeElements(Form.getElements(form), getHash);
2652 },
2653
2654 getElements: function(form) {
2655 return $A($(form).getElementsByTagName('*')).inject([],
2656 function(elements, child) {
2657 if (Form.Element.Serializers[child.tagName.toLowerCase()])
2658 elements.push(Element.extend(child));
2659 return elements;
2660 }
2661 );
2662 },
2663
2664 getInputs: function(form, typeName, name) {
2665 form = $(form);
2666 var inputs = form.getElementsByTagName('input');
2667
2668 if (!typeName && !name) return $A(inputs).map(Element.extend);
2669
2670 for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
2671 var input = inputs[i];
2672 if ((typeName && input.type != typeName) || (name && input.name != name))
2673 continue;
2674 matchingInputs.push(Element.extend(input));
2675 }
2676
2677 return matchingInputs;
2678 },
2679
2680 disable: function(form) {
2681 form = $(form);
2682 Form.getElements(form).invoke('disable');
2683 return form;
2684 },
2685
2686 enable: function(form) {
2687 form = $(form);
2688 Form.getElements(form).invoke('enable');
2689 return form;
2690 },
2691
2692 findFirstElement: function(form) {
2693 return $(form).getElements().find(function(element) {
2694 return element.type != 'hidden' && !element.disabled &&
2695 ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
2696 });
2697 },
2698
2699 focusFirstElement: function(form) {
2700 form = $(form);
2701 form.findFirstElement().activate();
2702 return form;
2703 },
2704
2705 request: function(form, options) {
2706 form = $(form), options = Object.clone(options || {});
2707
2708 var params = options.parameters;
2709 options.parameters = form.serialize(true);
2710
2711 if (params) {
2712 if (typeof params == 'string') params = params.toQueryParams();
2713 Object.extend(options.parameters, params);
2714 }
2715
2716 if (form.hasAttribute('method') && !options.method)
2717 options.method = form.method;
2718
2719 return new Ajax.Request(form.readAttribute('action'), options);
2720 }
2721}
2722
2723/*--------------------------------------------------------------------------*/
2724
2725Form.Element = {
2726 focus: function(element) {
2727 $(element).focus();
2728 return element;
2729 },
2730
2731 select: function(element) {
2732 $(element).select();
2733 return element;
2734 }
2735}
2736
2737Form.Element.Methods = {
2738 serialize: function(element) {
2739 element = $(element);
2740 if (!element.disabled && element.name) {
2741 var value = element.getValue();
2742 if (value != undefined) {
2743 var pair = {};
2744 pair[element.name] = value;
2745 return Hash.toQueryString(pair);
2746 }
2747 }
2748 return '';
2749 },
2750
2751 getValue: function(element) {
2752 element = $(element);
2753 var method = element.tagName.toLowerCase();
2754 return Form.Element.Serializers[method](element);
2755 },
2756
2757 clear: function(element) {
2758 $(element).value = '';
2759 return element;
2760 },
2761
2762 present: function(element) {
2763 return $(element).value != '';
2764 },
2765
2766 activate: function(element) {
2767 element = $(element);
2768 try {
2769 element.focus();
2770 if (element.select && (element.tagName.toLowerCase() != 'input' ||
2771 !['button', 'reset', 'submit'].include(element.type)))
2772 element.select();
2773 } catch (e) {}
2774 return element;
2775 },
2776
2777 disable: function(element) {
2778 element = $(element);
2779 element.blur();
2780 element.disabled = true;
2781 return element;
2782 },
2783
2784 enable: function(element) {
2785 element = $(element);
2786 element.disabled = false;
2787 return element;
2788 }
2789}
2790
2791/*--------------------------------------------------------------------------*/
2792
2793var Field = Form.Element;
2794var $F = Form.Element.Methods.getValue;
2795
2796/*--------------------------------------------------------------------------*/
2797
2798Form.Element.Serializers = {
2799 input: function(element) {
2800 switch (element.type.toLowerCase()) {
2801 case 'checkbox':
2802 case 'radio':
2803 return Form.Element.Serializers.inputSelector(element);
2804 default:
2805 return Form.Element.Serializers.textarea(element);
2806 }
2807 },
2808
2809 inputSelector: function(element) {
2810 return element.checked ? element.value : null;
2811 },
2812
2813 textarea: function(element) {
2814 return element.value;
2815 },
2816
2817 select: function(element) {
2818 return this[element.type == 'select-one' ?
2819 'selectOne' : 'selectMany'](element);
2820 },
2821
2822 selectOne: function(element) {
2823 var index = element.selectedIndex;
2824 return index >= 0 ? this.optionValue(element.options[index]) : null;
2825 },
2826
2827 selectMany: function(element) {
2828 var values, length = element.length;
2829 if (!length) return null;
2830
2831 for (var i = 0, values = []; i < length; i++) {
2832 var opt = element.options[i];
2833 if (opt.selected) values.push(this.optionValue(opt));
2834 }
2835 return values;
2836 },
2837
2838 optionValue: function(opt) {
2839 // extend element because hasAttribute may not be native
2840 return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
2841 }
2842}
2843
2844/*--------------------------------------------------------------------------*/
2845
2846Abstract.TimedObserver = function() {}
2847Abstract.TimedObserver.prototype = {
2848 initialize: function(element, frequency, callback) {
2849 this.frequency = frequency;
2850 this.element = $(element);
2851 this.callback = callback;
2852
2853 this.lastValue = this.getValue();
2854 this.registerCallback();
2855 },
2856
2857 registerCallback: function() {
2858 setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
2859 },
2860
2861 onTimerEvent: function() {
2862 var value = this.getValue();
2863 var changed = ('string' == typeof this.lastValue && 'string' == typeof value
2864 ? this.lastValue != value : String(this.lastValue) != String(value));
2865 if (changed) {
2866 this.callback(this.element, value);
2867 this.lastValue = value;
2868 }
2869 }
2870}
2871
2872Form.Element.Observer = Class.create();
2873Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2874 getValue: function() {
2875 return Form.Element.getValue(this.element);
2876 }
2877});
2878
2879Form.Observer = Class.create();
2880Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2881 getValue: function() {
2882 return Form.serialize(this.element);
2883 }
2884});
2885
2886/*--------------------------------------------------------------------------*/
2887
2888Abstract.EventObserver = function() {}
2889Abstract.EventObserver.prototype = {
2890 initialize: function(element, callback) {
2891 this.element = $(element);
2892 this.callback = callback;
2893
2894 this.lastValue = this.getValue();
2895 if (this.element.tagName.toLowerCase() == 'form')
2896 this.registerFormCallbacks();
2897 else
2898 this.registerCallback(this.element);
2899 },
2900
2901 onElementEvent: function() {
2902 var value = this.getValue();
2903 if (this.lastValue != value) {
2904 this.callback(this.element, value);
2905 this.lastValue = value;
2906 }
2907 },
2908
2909 registerFormCallbacks: function() {
2910 Form.getElements(this.element).each(this.registerCallback.bind(this));
2911 },
2912
2913 registerCallback: function(element) {
2914 if (element.type) {
2915 switch (element.type.toLowerCase()) {
2916 case 'checkbox':
2917 case 'radio':
2918 Event.observe(element, 'click', this.onElementEvent.bind(this));
2919 break;
2920 default:
2921 Event.observe(element, 'change', this.onElementEvent.bind(this));
2922 break;
2923 }
2924 }
2925 }
2926}
2927
2928Form.Element.EventObserver = Class.create();
2929Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2930 getValue: function() {
2931 return Form.Element.getValue(this.element);
2932 }
2933});
2934
2935Form.EventObserver = Class.create();
2936Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2937 getValue: function() {
2938 return Form.serialize(this.element);
2939 }
2940});
2941if (!window.Event) {
2942 var Event = new Object();
2943}
2944
2945Object.extend(Event, {
2946 KEY_BACKSPACE: 8,
2947 KEY_TAB: 9,
2948 KEY_RETURN: 13,
2949 KEY_ESC: 27,
2950 KEY_LEFT: 37,
2951 KEY_UP: 38,
2952 KEY_RIGHT: 39,
2953 KEY_DOWN: 40,
2954 KEY_DELETE: 46,
2955 KEY_HOME: 36,
2956 KEY_END: 35,
2957 KEY_PAGEUP: 33,
2958 KEY_PAGEDOWN: 34,
2959
2960 element: function(event) {
2961 return $(event.target || event.srcElement);
2962 },
2963
2964 isLeftClick: function(event) {
2965 return (((event.which) && (event.which == 1)) ||
2966 ((event.button) && (event.button == 1)));
2967 },
2968
2969 pointerX: function(event) {
2970 return event.pageX || (event.clientX +
2971 (document.documentElement.scrollLeft || document.body.scrollLeft));
2972 },
2973
2974 pointerY: function(event) {
2975 return event.pageY || (event.clientY +
2976 (document.documentElement.scrollTop || document.body.scrollTop));
2977 },
2978
2979 stop: function(event) {
2980 if (event.preventDefault) {
2981 event.preventDefault();
2982 event.stopPropagation();
2983 } else {
2984 event.returnValue = false;
2985 event.cancelBubble = true;
2986 }
2987 },
2988
2989 // find the first node with the given tagName, starting from the
2990 // node the event was triggered on; traverses the DOM upwards
2991 findElement: function(event, tagName) {
2992 var element = Event.element(event);
2993 while (element.parentNode && (!element.tagName ||
2994 (element.tagName.toUpperCase() != tagName.toUpperCase())))
2995 element = element.parentNode;
2996 return element;
2997 },
2998
2999 observers: false,
3000
3001 _observeAndCache: function(element, name, observer, useCapture) {
3002 if (!this.observers) this.observers = [];
3003 if (element.addEventListener) {
3004 this.observers.push([element, name, observer, useCapture]);
3005 element.addEventListener(name, observer, useCapture);
3006 } else if (element.attachEvent) {
3007 this.observers.push([element, name, observer, useCapture]);
3008 element.attachEvent('on' + name, observer);
3009 }
3010 },
3011
3012 unloadCache: function() {
3013 if (!Event.observers) return;
3014 for (var i = 0, length = Event.observers.length; i < length; i++) {
3015 Event.stopObserving.apply(this, Event.observers[i]);
3016 Event.observers[i][0] = null;
3017 }
3018 Event.observers = false;
3019 },
3020
3021 observe: function(element, name, observer, useCapture) {
3022 element = $(element);
3023 useCapture = useCapture || false;
3024
3025 if (name == 'keypress' &&
3026 (Prototype.Browser.WebKit || element.attachEvent))
3027 name = 'keydown';
3028
3029 Event._observeAndCache(element, name, observer, useCapture);
3030 },
3031
3032 stopObserving: function(element, name, observer, useCapture) {
3033 element = $(element);
3034 useCapture = useCapture || false;
3035
3036 if (name == 'keypress' &&
3037 (Prototype.Browser.WebKit || element.attachEvent))
3038 name = 'keydown';
3039
3040 if (element.removeEventListener) {
3041 element.removeEventListener(name, observer, useCapture);
3042 } else if (element.detachEvent) {
3043 try {
3044 element.detachEvent('on' + name, observer);
3045 } catch (e) {}
3046 }
3047 }
3048});
3049
3050/* prevent memory leaks in IE */
3051if (Prototype.Browser.IE)
3052 Event.observe(window, 'unload', Event.unloadCache, false);
3053var Position = {
3054 // set to true if needed, warning: firefox performance problems
3055 // NOT neeeded for page scrolling, only if draggable contained in
3056 // scrollable elements
3057 includeScrollOffsets: false,
3058
3059 // must be called before calling withinIncludingScrolloffset, every time the
3060 // page is scrolled
3061 prepare: function() {
3062 this.deltaX = window.pageXOffset
3063 || document.documentElement.scrollLeft
3064 || document.body.scrollLeft
3065 || 0;
3066 this.deltaY = window.pageYOffset
3067 || document.documentElement.scrollTop
3068 || document.body.scrollTop
3069 || 0;
3070 },
3071
3072 realOffset: function(element) {
3073 var valueT = 0, valueL = 0;
3074 do {
3075 valueT += element.scrollTop || 0;
3076 valueL += element.scrollLeft || 0;
3077 element = element.parentNode;
3078 } while (element);
3079 return [valueL, valueT];
3080 },
3081
3082 cumulativeOffset: function(element) {
3083 var valueT = 0, valueL = 0;
3084 do {
3085 valueT += element.offsetTop || 0;
3086 valueL += element.offsetLeft || 0;
3087 element = element.offsetParent;
3088 } while (element);
3089 return [valueL, valueT];
3090 },
3091
3092 positionedOffset: function(element) {
3093 var valueT = 0, valueL = 0;
3094 do {
3095 valueT += element.offsetTop || 0;
3096 valueL += element.offsetLeft || 0;
3097 element = element.offsetParent;
3098 if (element) {
3099 if(element.tagName=='BODY') break;
3100 var p = Element.getStyle(element, 'position');
3101 if (p == 'relative' || p == 'absolute') break;
3102 }
3103 } while (element);
3104 return [valueL, valueT];
3105 },
3106
3107 offsetParent: function(element) {
3108 if (element.offsetParent) return element.offsetParent;
3109 if (element == document.body) return element;
3110
3111 while ((element = element.parentNode) && element != document.body)
3112 if (Element.getStyle(element, 'position') != 'static')
3113 return element;
3114
3115 return document.body;
3116 },
3117
3118 // caches x/y coordinate pair to use with overlap
3119 within: function(element, x, y) {
3120 if (this.includeScrollOffsets)
3121 return this.withinIncludingScrolloffsets(element, x, y);
3122 this.xcomp = x;
3123 this.ycomp = y;
3124 this.offset = this.cumulativeOffset(element);
3125
3126 return (y >= this.offset[1] &&
3127 y < this.offset[1] + element.offsetHeight &&
3128 x >= this.offset[0] &&
3129 x < this.offset[0] + element.offsetWidth);
3130 },
3131
3132 withinIncludingScrolloffsets: function(element, x, y) {
3133 var offsetcache = this.realOffset(element);
3134
3135 this.xcomp = x + offsetcache[0] - this.deltaX;
3136 this.ycomp = y + offsetcache[1] - this.deltaY;
3137 this.offset = this.cumulativeOffset(element);
3138
3139 return (this.ycomp >= this.offset[1] &&
3140 this.ycomp < this.offset[1] + element.offsetHeight &&
3141 this.xcomp >= this.offset[0] &&
3142 this.xcomp < this.offset[0] + element.offsetWidth);
3143 },
3144
3145 // within must be called directly before
3146 overlap: function(mode, element) {
3147 if (!mode) return 0;
3148 if (mode == 'vertical')
3149 return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
3150 element.offsetHeight;
3151 if (mode == 'horizontal')
3152 return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
3153 element.offsetWidth;
3154 },
3155
3156 page: function(forElement) {
3157 var valueT = 0, valueL = 0;
3158
3159 var element = forElement;
3160 do {
3161 valueT += element.offsetTop || 0;
3162 valueL += element.offsetLeft || 0;
3163
3164 // Safari fix
3165 if (element.offsetParent == document.body)
3166 if (Element.getStyle(element,'position')=='absolute') break;
3167
3168 } while (element = element.offsetParent);
3169
3170 element = forElement;
3171 do {
3172 if (!window.opera || element.tagName=='BODY') {
3173 valueT -= element.scrollTop || 0;
3174 valueL -= element.scrollLeft || 0;
3175 }
3176 } while (element = element.parentNode);
3177
3178 return [valueL, valueT];
3179 },
3180
3181 clone: function(source, target) {
3182 var options = Object.extend({
3183 setLeft: true,
3184 setTop: true,
3185 setWidth: true,
3186 setHeight: true,
3187 offsetTop: 0,
3188 offsetLeft: 0
3189 }, arguments[2] || {})
3190
3191 // find page position of source
3192 source = $(source);
3193 var p = Position.page(source);
3194
3195 // find coordinate system to use
3196 target = $(target);
3197 var delta = [0, 0];
3198 var parent = null;
3199 // delta [0,0] will do fine with position: fixed elements,
3200 // position:absolute needs offsetParent deltas
3201 if (Element.getStyle(target,'position') == 'absolute') {
3202 parent = Position.offsetParent(target);
3203 delta = Position.page(parent);
3204 }
3205
3206 // correct by body offsets (fixes Safari)
3207 if (parent == document.body) {
3208 delta[0] -= document.body.offsetLeft;
3209 delta[1] -= document.body.offsetTop;
3210 }
3211
3212 // set position
3213 if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
3214 if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
3215 if(options.setWidth) target.style.width = source.offsetWidth + 'px';
3216 if(options.setHeight) target.style.height = source.offsetHeight + 'px';
3217 },
3218
3219 absolutize: function(element) {
3220 element = $(element);
3221 if (element.style.position == 'absolute') return;
3222 Position.prepare();
3223
3224 var offsets = Position.positionedOffset(element);
3225 var top = offsets[1];
3226 var left = offsets[0];
3227 var width = element.clientWidth;
3228 var height = element.clientHeight;
3229
3230 element._originalLeft = left - parseFloat(element.style.left || 0);
3231 element._originalTop = top - parseFloat(element.style.top || 0);
3232 element._originalWidth = element.style.width;
3233 element._originalHeight = element.style.height;
3234
3235 element.style.position = 'absolute';
3236 element.style.top = top + 'px';
3237 element.style.left = left + 'px';
3238 element.style.width = width + 'px';
3239 element.style.height = height + 'px';
3240 },
3241
3242 relativize: function(element) {
3243 element = $(element);
3244 if (element.style.position == 'relative') return;
3245 Position.prepare();
3246
3247 element.style.position = 'relative';
3248 var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
3249 var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
3250
3251 element.style.top = top + 'px';
3252 element.style.left = left + 'px';
3253 element.style.height = element._originalHeight;
3254 element.style.width = element._originalWidth;
3255 }
3256}
3257
3258// Safari returns margins on body which is incorrect if the child is absolutely
3259// positioned. For performance reasons, redefine Position.cumulativeOffset for
3260// KHTML/WebKit only.
3261if (Prototype.Browser.WebKit) {
3262 Position.cumulativeOffset = function(element) {
3263 var valueT = 0, valueL = 0;
3264 do {
3265 valueT += element.offsetTop || 0;
3266 valueL += element.offsetLeft || 0;
3267 if (element.offsetParent == document.body)
3268 if (Element.getStyle(element, 'position') == 'absolute') break;
3269
3270 element = element.offsetParent;
3271 } while (element);
3272
3273 return [valueL, valueT];
3274 }
3275}
3276
3277Element.addMethods(); \ No newline at end of file
diff --git a/docroot/lib/scriptaculous/builder.js b/docroot/lib/scriptaculous/builder.js
new file mode 100755
index 0000000..5b4ce87
--- /dev/null
+++ b/docroot/lib/scriptaculous/builder.js
@@ -0,0 +1,136 @@
1// script.aculo.us builder.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007
2
3// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4//
5// script.aculo.us is freely distributable under the terms of an MIT-style license.
6// For details, see the script.aculo.us web site: http://script.aculo.us/
7
8var Builder = {
9 NODEMAP: {
10 AREA: 'map',
11 CAPTION: 'table',
12 COL: 'table',
13 COLGROUP: 'table',
14 LEGEND: 'fieldset',
15 OPTGROUP: 'select',
16 OPTION: 'select',
17 PARAM: 'object',
18 TBODY: 'table',
19 TD: 'table',
20 TFOOT: 'table',
21 TH: 'table',
22 THEAD: 'table',
23 TR: 'table'
24 },
25 // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
26 // due to a Firefox bug
27 node: function(elementName) {
28 elementName = elementName.toUpperCase();
29
30 // try innerHTML approach
31 var parentTag = this.NODEMAP[elementName] || 'div';
32 var parentElement = document.createElement(parentTag);
33 try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
34 parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
35 } catch(e) {}
36 var element = parentElement.firstChild || null;
37
38 // see if browser added wrapping tags
39 if(element && (element.tagName.toUpperCase() != elementName))
40 element = element.getElementsByTagName(elementName)[0];
41
42 // fallback to createElement approach
43 if(!element) element = document.createElement(elementName);
44
45 // abort if nothing could be created
46 if(!element) return;
47
48 // attributes (or text)
49 if(arguments[1])
50 if(this._isStringOrNumber(arguments[1]) ||
51 (arguments[1] instanceof Array) ||
52 arguments[1].tagName) {
53 this._children(element, arguments[1]);
54 } else {
55 var attrs = this._attributes(arguments[1]);
56 if(attrs.length) {
57 try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
58 parentElement.innerHTML = "<" +elementName + " " +
59 attrs + "></" + elementName + ">";
60 } catch(e) {}
61 element = parentElement.firstChild || null;
62 // workaround firefox 1.0.X bug
63 if(!element) {
64 element = document.createElement(elementName);
65 for(attr in arguments[1])
66 element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
67 }
68 if(element.tagName.toUpperCase() != elementName)
69 element = parentElement.getElementsByTagName(elementName)[0];
70 }
71 }
72
73 // text, or array of children
74 if(arguments[2])
75 this._children(element, arguments[2]);
76
77 return element;
78 },
79 _text: function(text) {
80 return document.createTextNode(text);
81 },
82
83 ATTR_MAP: {
84 'className': 'class',
85 'htmlFor': 'for'
86 },
87
88 _attributes: function(attributes) {
89 var attrs = [];
90 for(attribute in attributes)
91 attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
92 '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
93 return attrs.join(" ");
94 },
95 _children: function(element, children) {
96 if(children.tagName) {
97 element.appendChild(children);
98 return;
99 }
100 if(typeof children=='object') { // array can hold nodes and text
101 children.flatten().each( function(e) {
102 if(typeof e=='object')
103 element.appendChild(e)
104 else
105 if(Builder._isStringOrNumber(e))
106 element.appendChild(Builder._text(e));
107 });
108 } else
109 if(Builder._isStringOrNumber(children))
110 element.appendChild(Builder._text(children));
111 },
112 _isStringOrNumber: function(param) {
113 return(typeof param=='string' || typeof param=='number');
114 },
115 build: function(html) {
116 var element = this.node('div');
117 $(element).update(html.strip());
118 return element.down();
119 },
120 dump: function(scope) {
121 if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
122
123 var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
124 "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
125 "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
126 "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
127 "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
128 "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
129
130 tags.each( function(tag){
131 scope[tag] = function() {
132 return Builder.node.apply(Builder, [tag].concat($A(arguments)));
133 }
134 });
135 }
136}
diff --git a/docroot/lib/scriptaculous/controls.js b/docroot/lib/scriptaculous/controls.js
new file mode 100755
index 0000000..6783bd0
--- /dev/null
+++ b/docroot/lib/scriptaculous/controls.js
@@ -0,0 +1,875 @@
1// script.aculo.us controls.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007
2
3// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
5// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
6// Contributors:
7// Richard Livsey
8// Rahul Bhargava
9// Rob Wills
10//
11// script.aculo.us is freely distributable under the terms of an MIT-style license.
12// For details, see the script.aculo.us web site: http://script.aculo.us/
13
14// Autocompleter.Base handles all the autocompletion functionality
15// that's independent of the data source for autocompletion. This
16// includes drawing the autocompletion menu, observing keyboard
17// and mouse events, and similar.
18//
19// Specific autocompleters need to provide, at the very least,
20// a getUpdatedChoices function that will be invoked every time
21// the text inside the monitored textbox changes. This method
22// should get the text for which to provide autocompletion by
23// invoking this.getToken(), NOT by directly accessing
24// this.element.value. This is to allow incremental tokenized
25// autocompletion. Specific auto-completion logic (AJAX, etc)
26// belongs in getUpdatedChoices.
27//
28// Tokenized incremental autocompletion is enabled automatically
29// when an autocompleter is instantiated with the 'tokens' option
30// in the options parameter, e.g.:
31// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
32// will incrementally autocomplete with a comma as the token.
33// Additionally, ',' in the above example can be replaced with
34// a token array, e.g. { tokens: [',', '\n'] } which
35// enables autocompletion on multiple tokens. This is most
36// useful when one of the tokens is \n (a newline), as it
37// allows smart autocompletion after linebreaks.
38
39if(typeof Effect == 'undefined')
40 throw("controls.js requires including script.aculo.us' effects.js library");
41
42var Autocompleter = {}
43Autocompleter.Base = function() {};
44Autocompleter.Base.prototype = {
45 baseInitialize: function(element, update, options) {
46 element = $(element)
47 this.element = element;
48 this.update = $(update);
49 this.hasFocus = false;
50 this.changed = false;
51 this.active = false;
52 this.index = 0;
53 this.entryCount = 0;
54
55 if(this.setOptions)
56 this.setOptions(options);
57 else
58 this.options = options || {};
59
60 this.options.paramName = this.options.paramName || this.element.name;
61 this.options.tokens = this.options.tokens || [];
62 this.options.frequency = this.options.frequency || 0.4;
63 this.options.minChars = this.options.minChars || 1;
64 this.options.onShow = this.options.onShow ||
65 function(element, update){
66 if(!update.style.position || update.style.position=='absolute') {
67 update.style.position = 'absolute';
68 Position.clone(element, update, {
69 setHeight: false,
70 offsetTop: element.offsetHeight
71 });
72 }
73 Effect.Appear(update,{duration:0.15});
74 };
75 this.options.onHide = this.options.onHide ||
76 function(element, update){ new Effect.Fade(update,{duration:0.15}) };
77
78 if(typeof(this.options.tokens) == 'string')
79 this.options.tokens = new Array(this.options.tokens);
80
81 this.observer = null;
82
83 this.element.setAttribute('autocomplete','off');
84
85 Element.hide(this.update);
86
87 Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
88 Event.observe(this.element, 'keypress', this.onKeyPress.bindAsEventListener(this));
89
90 // Turn autocomplete back on when the user leaves the page, so that the
91 // field's value will be remembered on Mozilla-based browsers.
92 Event.observe(window, 'beforeunload', function(){
93 element.setAttribute('autocomplete', 'on');
94 });
95 },
96
97 show: function() {
98 if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
99 if(!this.iefix &&
100 (Prototype.Browser.IE) &&
101 (Element.getStyle(this.update, 'position')=='absolute')) {
102 new Insertion.After(this.update,
103 '<iframe id="' + this.update.id + '_iefix" '+
104 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
105 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
106 this.iefix = $(this.update.id+'_iefix');
107 }
108 if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
109 },
110
111 fixIEOverlapping: function() {
112 Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
113 this.iefix.style.zIndex = 1;
114 this.update.style.zIndex = 2;
115 Element.show(this.iefix);
116 },
117
118 hide: function() {
119 this.stopIndicator();
120 if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
121 if(this.iefix) Element.hide(this.iefix);
122 },
123
124 startIndicator: function() {
125 if(this.options.indicator) Element.show(this.options.indicator);
126 },
127
128 stopIndicator: function() {
129 if(this.options.indicator) Element.hide(this.options.indicator);
130 },
131
132 onKeyPress: function(event) {
133 if(this.active)
134 switch(event.keyCode) {
135 case Event.KEY_TAB:
136 case Event.KEY_RETURN:
137 this.selectEntry();
138 Event.stop(event);
139 case Event.KEY_ESC:
140 this.hide();
141 this.active = false;
142 Event.stop(event);
143 return;
144 case Event.KEY_LEFT:
145 case Event.KEY_RIGHT:
146 return;
147 case Event.KEY_UP:
148 this.markPrevious();
149 this.render();
150 if(Prototype.Browser.WebKit) Event.stop(event);
151 return;
152 case Event.KEY_DOWN:
153 this.markNext();
154 this.render();
155 if(Prototype.Browser.WebKit) Event.stop(event);
156 return;
157 }
158 else
159 if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
160 (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
161
162 this.changed = true;
163 this.hasFocus = true;
164
165 if(this.observer) clearTimeout(this.observer);
166 this.observer =
167 setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
168 },
169
170 activate: function() {
171 this.changed = false;
172 this.hasFocus = true;
173 this.getUpdatedChoices();
174 },
175
176 onHover: function(event) {
177 var element = Event.findElement(event, 'LI');
178 if(this.index != element.autocompleteIndex)
179 {
180 this.index = element.autocompleteIndex;
181 this.render();
182 }
183 Event.stop(event);
184 },
185
186 onClick: function(event) {
187 var element = Event.findElement(event, 'LI');
188 this.index = element.autocompleteIndex;
189 this.selectEntry();
190 this.hide();
191 },
192
193 onBlur: function(event) {
194 // needed to make click events working
195 setTimeout(this.hide.bind(this), 250);
196 this.hasFocus = false;
197 this.active = false;
198 },
199
200 render: function() {
201 if(this.entryCount > 0) {
202 for (var i = 0; i < this.entryCount; i++)
203 this.index==i ?
204 Element.addClassName(this.getEntry(i),"selected") :
205 Element.removeClassName(this.getEntry(i),"selected");
206 if(this.hasFocus) {
207 this.show();
208 this.active = true;
209 }
210 } else {
211 this.active = false;
212 this.hide();
213 }
214 },
215
216 markPrevious: function() {
217 if(this.index > 0) this.index--
218 else this.index = this.entryCount-1;
219 this.getEntry(this.index).scrollIntoView(true);
220 },
221
222 markNext: function() {
223 if(this.index < this.entryCount-1) this.index++
224 else this.index = 0;
225 this.getEntry(this.index).scrollIntoView(false);
226 },
227
228 getEntry: function(index) {
229 return this.update.firstChild.childNodes[index];
230 },
231
232 getCurrentEntry: function() {
233 return this.getEntry(this.index);
234 },
235
236 selectEntry: function() {
237 this.active = false;
238 this.updateElement(this.getCurrentEntry());
239 },
240
241 updateElement: function(selectedElement) {
242 if (this.options.updateElement) {
243 this.options.updateElement(selectedElement);
244 return;
245 }
246 var value = '';
247 if (this.options.select) {
248 var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
249 if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
250 } else
251 value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
252
253 var lastTokenPos = this.findLastToken();
254 if (lastTokenPos != -1) {
255 var newValue = this.element.value.substr(0, lastTokenPos + 1);
256 var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
257 if (whitespace)
258 newValue += whitespace[0];
259 this.element.value = newValue + value;
260 } else {
261 this.element.value = value;
262 }
263 this.element.focus();
264
265 if (this.options.afterUpdateElement)
266 this.options.afterUpdateElement(this.element, selectedElement);
267 },
268
269 updateChoices: function(choices) {
270 if(!this.changed && this.hasFocus) {
271 this.update.innerHTML = choices;
272 Element.cleanWhitespace(this.update);
273 Element.cleanWhitespace(this.update.down());
274
275 if(this.update.firstChild && this.update.down().childNodes) {
276 this.entryCount =
277 this.update.down().childNodes.length;
278 for (var i = 0; i < this.entryCount; i++) {
279 var entry = this.getEntry(i);
280 entry.autocompleteIndex = i;
281 this.addObservers(entry);
282 }
283 } else {
284 this.entryCount = 0;
285 }
286
287 this.stopIndicator();
288 this.index = 0;
289
290 if(this.entryCount==1 && this.options.autoSelect) {
291 this.selectEntry();
292 this.hide();
293 } else {
294 this.render();
295 }
296 }
297 },
298
299 addObservers: function(element) {
300 Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
301 Event.observe(element, "click", this.onClick.bindAsEventListener(this));
302 },
303
304 onObserverEvent: function() {
305 this.changed = false;
306 if(this.getToken().length>=this.options.minChars) {
307 this.getUpdatedChoices();
308 } else {
309 this.active = false;
310 this.hide();
311 }
312 },
313
314 getToken: function() {
315 var tokenPos = this.findLastToken();
316 if (tokenPos != -1)
317 var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
318 else
319 var ret = this.element.value;
320
321 return /\n/.test(ret) ? '' : ret;
322 },
323
324 findLastToken: function() {
325 var lastTokenPos = -1;
326
327 for (var i=0; i<this.options.tokens.length; i++) {
328 var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
329 if (thisTokenPos > lastTokenPos)
330 lastTokenPos = thisTokenPos;
331 }
332 return lastTokenPos;
333 }
334}
335
336Ajax.Autocompleter = Class.create();
337Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
338 initialize: function(element, update, url, options) {
339 this.baseInitialize(element, update, options);
340 this.options.asynchronous = true;
341 this.options.onComplete = this.onComplete.bind(this);
342 this.options.defaultParams = this.options.parameters || null;
343 this.url = url;
344 },
345
346 getUpdatedChoices: function() {
347 this.startIndicator();
348
349 var entry = encodeURIComponent(this.options.paramName) + '=' +
350 encodeURIComponent(this.getToken());
351
352 this.options.parameters = this.options.callback ?
353 this.options.callback(this.element, entry) : entry;
354
355 if(this.options.defaultParams)
356 this.options.parameters += '&' + this.options.defaultParams;
357
358 new Ajax.Request(this.url, this.options);
359 },
360
361 onComplete: function(request) {
362 this.updateChoices(request.responseText);
363 }
364
365});
366
367// The local array autocompleter. Used when you'd prefer to
368// inject an array of autocompletion options into the page, rather
369// than sending out Ajax queries, which can be quite slow sometimes.
370//
371// The constructor takes four parameters. The first two are, as usual,
372// the id of the monitored textbox, and id of the autocompletion menu.
373// The third is the array you want to autocomplete from, and the fourth
374// is the options block.
375//
376// Extra local autocompletion options:
377// - choices - How many autocompletion choices to offer
378//
379// - partialSearch - If false, the autocompleter will match entered
380// text only at the beginning of strings in the
381// autocomplete array. Defaults to true, which will
382// match text at the beginning of any *word* in the
383// strings in the autocomplete array. If you want to
384// search anywhere in the string, additionally set
385// the option fullSearch to true (default: off).
386//
387// - fullSsearch - Search anywhere in autocomplete array strings.
388//
389// - partialChars - How many characters to enter before triggering
390// a partial match (unlike minChars, which defines
391// how many characters are required to do any match
392// at all). Defaults to 2.
393//
394// - ignoreCase - Whether to ignore case when autocompleting.
395// Defaults to true.
396//
397// It's possible to pass in a custom function as the 'selector'
398// option, if you prefer to write your own autocompletion logic.
399// In that case, the other options above will not apply unless
400// you support them.
401
402Autocompleter.Local = Class.create();
403Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
404 initialize: function(element, update, array, options) {
405 this.baseInitialize(element, update, options);
406 this.options.array = array;
407 },
408
409 getUpdatedChoices: function() {
410 this.updateChoices(this.options.selector(this));
411 },
412
413 setOptions: function(options) {
414 this.options = Object.extend({
415 choices: 10,
416 partialSearch: true,
417 partialChars: 2,
418 ignoreCase: true,
419 fullSearch: false,
420 selector: function(instance) {
421 var ret = []; // Beginning matches
422 var partial = []; // Inside matches
423 var entry = instance.getToken();
424 var count = 0;
425
426 for (var i = 0; i < instance.options.array.length &&
427 ret.length < instance.options.choices ; i++) {
428
429 var elem = instance.options.array[i];
430 var foundPos = instance.options.ignoreCase ?
431 elem.toLowerCase().indexOf(entry.toLowerCase()) :
432 elem.indexOf(entry);
433
434 while (foundPos != -1) {
435 if (foundPos == 0 && elem.length != entry.length) {
436 ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
437 elem.substr(entry.length) + "</li>");
438 break;
439 } else if (entry.length >= instance.options.partialChars &&
440 instance.options.partialSearch && foundPos != -1) {
441 if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
442 partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
443 elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
444 foundPos + entry.length) + "</li>");
445 break;
446 }
447 }
448
449 foundPos = instance.options.ignoreCase ?
450 elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
451 elem.indexOf(entry, foundPos + 1);
452
453 }
454 }
455 if (partial.length)
456 ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
457 return "<ul>" + ret.join('') + "</ul>";
458 }
459 }, options || {});
460 }
461});
462
463// AJAX in-place editor
464//
465// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
466
467// Use this if you notice weird scrolling problems on some browsers,
468// the DOM might be a bit confused when this gets called so do this
469// waits 1 ms (with setTimeout) until it does the activation
470Field.scrollFreeActivate = function(field) {
471 setTimeout(function() {
472 Field.activate(field);
473 }, 1);
474}
475
476Ajax.InPlaceEditor = Class.create();
477Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
478Ajax.InPlaceEditor.prototype = {
479 initialize: function(element, url, options) {
480 this.url = url;
481 this.element = $(element);
482
483 this.options = Object.extend({
484 paramName: "value",
485 okButton: true,
486 okLink: false,
487 okText: "ok",
488 cancelButton: false,
489 cancelLink: true,
490 cancelText: "cancel",
491 textBeforeControls: '',
492 textBetweenControls: '',
493 textAfterControls: '',
494 savingText: "Saving...",
495 clickToEditText: "Click to edit",
496 okText: "ok",
497 rows: 1,
498 onComplete: function(transport, element) {
499 new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
500 },
501 onFailure: function(transport) {
502 alert("Error communicating with the server: " + transport.responseText.stripTags());
503 },
504 callback: function(form) {
505 return Form.serialize(form);
506 },
507 handleLineBreaks: true,
508 loadingText: 'Loading...',
509 savingClassName: 'inplaceeditor-saving',
510 loadingClassName: 'inplaceeditor-loading',
511 formClassName: 'inplaceeditor-form',
512 highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
513 highlightendcolor: "#FFFFFF",
514 externalControl: null,
515 submitOnBlur: false,
516 ajaxOptions: {},
517 evalScripts: false
518 }, options || {});
519
520 if(!this.options.formId && this.element.id) {
521 this.options.formId = this.element.id + "-inplaceeditor";
522 if ($(this.options.formId)) {
523 // there's already a form with that name, don't specify an id
524 this.options.formId = null;
525 }
526 }
527
528 if (this.options.externalControl) {
529 this.options.externalControl = $(this.options.externalControl);
530 }
531
532 this.originalBackground = Element.getStyle(this.element, 'background-color');
533 if (!this.originalBackground) {
534 this.originalBackground = "transparent";
535 }
536
537 this.element.title = this.options.clickToEditText;
538
539 this.onclickListener = this.enterEditMode.bindAsEventListener(this);
540 this.mouseoverListener = this.enterHover.bindAsEventListener(this);
541 this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
542 Event.observe(this.element, 'click', this.onclickListener);
543 Event.observe(this.element, 'mouseover', this.mouseoverListener);
544 Event.observe(this.element, 'mouseout', this.mouseoutListener);
545 if (this.options.externalControl) {
546 Event.observe(this.options.externalControl, 'click', this.onclickListener);
547 Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
548 Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
549 }
550 },
551 enterEditMode: function(evt) {
552 if (this.saving) return;
553 if (this.editing) return;
554 this.editing = true;
555 this.onEnterEditMode();
556 if (this.options.externalControl) {
557 Element.hide(this.options.externalControl);
558 }
559 Element.hide(this.element);
560 this.createForm();
561 this.element.parentNode.insertBefore(this.form, this.element);
562 if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
563 // stop the event to avoid a page refresh in Safari
564 if (evt) {
565 Event.stop(evt);
566 }
567 return false;
568 },
569 createForm: function() {
570 this.form = document.createElement("form");
571 this.form.id = this.options.formId;
572 Element.addClassName(this.form, this.options.formClassName)
573 this.form.onsubmit = this.onSubmit.bind(this);
574
575 this.createEditField();
576
577 if (this.options.textarea) {
578 var br = document.createElement("br");
579 this.form.appendChild(br);
580 }
581
582 if (this.options.textBeforeControls)
583 this.form.appendChild(document.createTextNode(this.options.textBeforeControls));
584
585 if (this.options.okButton) {
586 var okButton = document.createElement("input");
587 okButton.type = "submit";
588 okButton.value = this.options.okText;
589 okButton.className = 'editor_ok_button';
590 this.form.appendChild(okButton);
591 }
592
593 if (this.options.okLink) {
594 var okLink = document.createElement("a");
595 okLink.href = "#";
596 okLink.appendChild(document.createTextNode(this.options.okText));
597 okLink.onclick = this.onSubmit.bind(this);
598 okLink.className = 'editor_ok_link';
599 this.form.appendChild(okLink);
600 }
601
602 if (this.options.textBetweenControls &&
603 (this.options.okLink || this.options.okButton) &&
604 (this.options.cancelLink || this.options.cancelButton))
605 this.form.appendChild(document.createTextNode(this.options.textBetweenControls));
606
607 if (this.options.cancelButton) {
608 var cancelButton = document.createElement("input");
609 cancelButton.type = "submit";
610 cancelButton.value = this.options.cancelText;
611 cancelButton.onclick = this.onclickCancel.bind(this);
612 cancelButton.className = 'editor_cancel_button';
613 this.form.appendChild(cancelButton);
614 }
615
616 if (this.options.cancelLink) {
617 var cancelLink = document.createElement("a");
618 cancelLink.href = "#";
619 cancelLink.appendChild(document.createTextNode(this.options.cancelText));
620 cancelLink.onclick = this.onclickCancel.bind(this);
621 cancelLink.className = 'editor_cancel editor_cancel_link';
622 this.form.appendChild(cancelLink);
623 }
624
625 if (this.options.textAfterControls)
626 this.form.appendChild(document.createTextNode(this.options.textAfterControls));
627 },
628 hasHTMLLineBreaks: function(string) {
629 if (!this.options.handleLineBreaks) return false;
630 return string.match(/<br/i) || string.match(/<p>/i);
631 },
632 convertHTMLLineBreaks: function(string) {
633 return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
634 },
635 createEditField: function() {
636 var text;
637 if(this.options.loadTextURL) {
638 text = this.options.loadingText;
639 } else {
640 text = this.getText();
641 }
642
643 var obj = this;
644
645 if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
646 this.options.textarea = false;
647 var textField = document.createElement("input");
648 textField.obj = this;
649 textField.type = "text";
650 textField.name = this.options.paramName;
651 textField.value = text;
652 textField.style.backgroundColor = this.options.highlightcolor;
653 textField.className = 'editor_field';
654 var size = this.options.size || this.options.cols || 0;
655 if (size != 0) textField.size = size;
656 if (this.options.submitOnBlur)
657 textField.onblur = this.onSubmit.bind(this);
658 this.editField = textField;
659 } else {
660 this.options.textarea = true;
661 var textArea = document.createElement("textarea");
662 textArea.obj = this;
663 textArea.name = this.options.paramName;
664 textArea.value = this.convertHTMLLineBreaks(text);
665 textArea.rows = this.options.rows;
666 textArea.cols = this.options.cols || 40;
667 textArea.className = 'editor_field';
668 if (this.options.submitOnBlur)
669 textArea.onblur = this.onSubmit.bind(this);
670 this.editField = textArea;
671 }
672
673 if(this.options.loadTextURL) {
674 this.loadExternalText();
675 }
676 this.form.appendChild(this.editField);
677 },
678 getText: function() {
679 return this.element.innerHTML;
680 },
681 loadExternalText: function() {
682 Element.addClassName(this.form, this.options.loadingClassName);
683 this.editField.disabled = true;
684 new Ajax.Request(
685 this.options.loadTextURL,
686 Object.extend({
687 asynchronous: true,
688 onComplete: this.onLoadedExternalText.bind(this)
689 }, this.options.ajaxOptions)
690 );
691 },
692 onLoadedExternalText: function(transport) {
693 Element.removeClassName(this.form, this.options.loadingClassName);
694 this.editField.disabled = false;
695 this.editField.value = transport.responseText.stripTags();
696 Field.scrollFreeActivate(this.editField);
697 },
698 onclickCancel: function() {
699 this.onComplete();
700 this.leaveEditMode();
701 return false;
702 },
703 onFailure: function(transport) {
704 this.options.onFailure(transport);
705 if (this.oldInnerHTML) {
706 this.element.innerHTML = this.oldInnerHTML;
707 this.oldInnerHTML = null;
708 }
709 return false;
710 },
711 onSubmit: function() {
712 // onLoading resets these so we need to save them away for the Ajax call
713 var form = this.form;
714 var value = this.editField.value;
715
716 // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
717 // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
718 // to be displayed indefinitely
719 this.onLoading();
720
721 if (this.options.evalScripts) {
722 new Ajax.Request(
723 this.url, Object.extend({
724 parameters: this.options.callback(form, value),
725 onComplete: this.onComplete.bind(this),
726 onFailure: this.onFailure.bind(this),
727 asynchronous:true,
728 evalScripts:true
729 }, this.options.ajaxOptions));
730 } else {
731 new Ajax.Updater(
732 { success: this.element,
733 // don't update on failure (this could be an option)
734 failure: null },
735 this.url, Object.extend({
736 parameters: this.options.callback(form, value),
737 onComplete: this.onComplete.bind(this),
738 onFailure: this.onFailure.bind(this)
739 }, this.options.ajaxOptions));
740 }
741 // stop the event to avoid a page refresh in Safari
742 if (arguments.length > 1) {
743 Event.stop(arguments[0]);
744 }
745 return false;
746 },
747 onLoading: function() {
748 this.saving = true;
749 this.removeForm();
750 this.leaveHover();
751 this.showSaving();
752 },
753 showSaving: function() {
754 this.oldInnerHTML = this.element.innerHTML;
755 this.element.innerHTML = this.options.savingText;
756 Element.addClassName(this.element, this.options.savingClassName);
757 this.element.style.backgroundColor = this.originalBackground;
758 Element.show(this.element);
759 },
760 removeForm: function() {
761 if(this.form) {
762 if (this.form.parentNode) Element.remove(this.form);
763 this.form = null;
764 }
765 },
766 enterHover: function() {
767 if (this.saving) return;
768 this.element.style.backgroundColor = this.options.highlightcolor;
769 if (this.effect) {
770 this.effect.cancel();
771 }
772 Element.addClassName(this.element, this.options.hoverClassName)
773 },
774 leaveHover: function() {
775 if (this.options.backgroundColor) {
776 this.element.style.backgroundColor = this.oldBackground;
777 }
778 Element.removeClassName(this.element, this.options.hoverClassName)
779 if (this.saving) return;
780 this.effect = new Effect.Highlight(this.element, {
781 startcolor: this.options.highlightcolor,
782 endcolor: this.options.highlightendcolor,
783 restorecolor: this.originalBackground
784 });
785 },
786 leaveEditMode: function() {
787 Element.removeClassName(this.element, this.options.savingClassName);
788 this.removeForm();
789 this.leaveHover();
790 this.element.style.backgroundColor = this.originalBackground;
791 Element.show(this.element);
792 if (this.options.externalControl) {
793 Element.show(this.options.externalControl);
794 }
795 this.editing = false;
796 this.saving = false;
797 this.oldInnerHTML = null;
798 this.onLeaveEditMode();
799 },
800 onComplete: function(transport) {
801 this.leaveEditMode();
802 this.options.onComplete.bind(this)(transport, this.element);
803 },
804 onEnterEditMode: function() {},
805 onLeaveEditMode: function() {},
806 dispose: function() {
807 if (this.oldInnerHTML) {
808 this.element.innerHTML = this.oldInnerHTML;
809 }
810 this.leaveEditMode();
811 Event.stopObserving(this.element, 'click', this.onclickListener);
812 Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
813 Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
814 if (this.options.externalControl) {
815 Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
816 Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
817 Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
818 }
819 }
820};
821
822Ajax.InPlaceCollectionEditor = Class.create();
823Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
824Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
825 createEditField: function() {
826 if (!this.cached_selectTag) {
827 var selectTag = document.createElement("select");
828 var collection = this.options.collection || [];
829 var optionTag;
830 collection.each(function(e,i) {
831 optionTag = document.createElement("option");
832 optionTag.value = (e instanceof Array) ? e[0] : e;
833 if((typeof this.options.value == 'undefined') &&
834 ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
835 if(this.options.value==optionTag.value) optionTag.selected = true;
836 optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
837 selectTag.appendChild(optionTag);
838 }.bind(this));
839 this.cached_selectTag = selectTag;
840 }
841
842 this.editField = this.cached_selectTag;
843 if(this.options.loadTextURL) this.loadExternalText();
844 this.form.appendChild(this.editField);
845 this.options.callback = function(form, value) {
846 return "value=" + encodeURIComponent(value);
847 }
848 }
849});
850
851// Delayed observer, like Form.Element.Observer,
852// but waits for delay after last key input
853// Ideal for live-search fields
854
855Form.Element.DelayedObserver = Class.create();
856Form.Element.DelayedObserver.prototype = {
857 initialize: function(element, delay, callback) {
858 this.delay = delay || 0.5;
859 this.element = $(element);
860 this.callback = callback;
861 this.timer = null;
862 this.lastValue = $F(this.element);
863 Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
864 },
865 delayedListener: function(event) {
866 if(this.lastValue == $F(this.element)) return;
867 if(this.timer) clearTimeout(this.timer);
868 this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
869 this.lastValue = $F(this.element);
870 },
871 onTimerEvent: function() {
872 this.timer = null;
873 this.callback(this.element, $F(this.element));
874 }
875};
diff --git a/docroot/lib/scriptaculous/dragdrop.js b/docroot/lib/scriptaculous/dragdrop.js
new file mode 100755
index 0000000..108dd66
--- /dev/null
+++ b/docroot/lib/scriptaculous/dragdrop.js
@@ -0,0 +1,970 @@
1// script.aculo.us dragdrop.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007
2
3// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4// (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
5//
6// script.aculo.us is freely distributable under the terms of an MIT-style license.
7// For details, see the script.aculo.us web site: http://script.aculo.us/
8
9if(typeof Effect == 'undefined')
10 throw("dragdrop.js requires including script.aculo.us' effects.js library");
11
12var Droppables = {
13 drops: [],
14
15 remove: function(element) {
16 this.drops = this.drops.reject(function(d) { return d.element==$(element) });
17 },
18
19 add: function(element) {
20 element = $(element);
21 var options = Object.extend({
22 greedy: true,
23 hoverclass: null,
24 tree: false
25 }, arguments[1] || {});
26
27 // cache containers
28 if(options.containment) {
29 options._containers = [];
30 var containment = options.containment;
31 if((typeof containment == 'object') &&
32 (containment.constructor == Array)) {
33 containment.each( function(c) { options._containers.push($(c)) });
34 } else {
35 options._containers.push($(containment));
36 }
37 }
38
39 if(options.accept) options.accept = [options.accept].flatten();
40
41 Element.makePositioned(element); // fix IE
42 options.element = element;
43
44 this.drops.push(options);
45 },
46
47 findDeepestChild: function(drops) {
48 deepest = drops[0];
49
50 for (i = 1; i < drops.length; ++i)
51 if (Element.isParent(drops[i].element, deepest.element))
52 deepest = drops[i];
53
54 return deepest;
55 },
56
57 isContained: function(element, drop) {
58 var containmentNode;
59 if(drop.tree) {
60 containmentNode = element.treeNode;
61 } else {
62 containmentNode = element.parentNode;
63 }
64 return drop._containers.detect(function(c) { return containmentNode == c });
65 },
66
67 isAffected: function(point, element, drop) {
68 return (
69 (drop.element!=element) &&
70 ((!drop._containers) ||
71 this.isContained(element, drop)) &&
72 ((!drop.accept) ||
73 (Element.classNames(element).detect(
74 function(v) { return drop.accept.include(v) } ) )) &&
75 Position.within(drop.element, point[0], point[1]) );
76 },
77
78 deactivate: function(drop) {
79 if(drop.hoverclass)
80 Element.removeClassName(drop.element, drop.hoverclass);
81 this.last_active = null;
82 },
83
84 activate: function(drop) {
85 if(drop.hoverclass)
86 Element.addClassName(drop.element, drop.hoverclass);
87 this.last_active = drop;
88 },
89
90 show: function(point, element) {
91 if(!this.drops.length) return;
92 var affected = [];
93
94 if(this.last_active) this.deactivate(this.last_active);
95 this.drops.each( function(drop) {
96 if(Droppables.isAffected(point, element, drop))
97 affected.push(drop);
98 });
99
100 if(affected.length>0) {
101 drop = Droppables.findDeepestChild(affected);
102 Position.within(drop.element, point[0], point[1]);
103 if(drop.onHover)
104 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
105
106 Droppables.activate(drop);
107 }
108 },
109
110 fire: function(event, element) {
111 if(!this.last_active) return;
112 Position.prepare();
113
114 if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
115 if (this.last_active.onDrop) {
116 this.last_active.onDrop(element, this.last_active.element, event);
117 return true;
118 }
119 },
120
121 reset: function() {
122 if(this.last_active)
123 this.deactivate(this.last_active);
124 }
125}
126
127var Draggables = {
128 drags: [],
129 observers: [],
130
131 register: function(draggable) {
132 if(this.drags.length == 0) {
133 this.eventMouseUp = this.endDrag.bindAsEventListener(this);
134 this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
135 this.eventKeypress = this.keyPress.bindAsEventListener(this);
136
137 Event.observe(document, "mouseup", this.eventMouseUp);
138 Event.observe(document, "mousemove", this.eventMouseMove);
139 Event.observe(document, "keypress", this.eventKeypress);
140 }
141 this.drags.push(draggable);
142 },
143
144 unregister: function(draggable) {
145 this.drags = this.drags.reject(function(d) { return d==draggable });
146 if(this.drags.length == 0) {
147 Event.stopObserving(document, "mouseup", this.eventMouseUp);
148 Event.stopObserving(document, "mousemove", this.eventMouseMove);
149 Event.stopObserving(document, "keypress", this.eventKeypress);
150 }
151 },
152
153 activate: function(draggable) {
154 if(draggable.options.delay) {
155 this._timeout = setTimeout(function() {
156 Draggables._timeout = null;
157 window.focus();
158 Draggables.activeDraggable = draggable;
159 }.bind(this), draggable.options.delay);
160 } else {
161 window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
162 this.activeDraggable = draggable;
163 }
164 },
165
166 deactivate: function() {
167 this.activeDraggable = null;
168 },
169
170 updateDrag: function(event) {
171 if(!this.activeDraggable) return;
172 var pointer = [Event.pointerX(event), Event.pointerY(event)];
173 // Mozilla-based browsers fire successive mousemove events with
174 // the same coordinates, prevent needless redrawing (moz bug?)
175 if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
176 this._lastPointer = pointer;
177
178 this.activeDraggable.updateDrag(event, pointer);
179 },
180
181 endDrag: function(event) {
182 if(this._timeout) {
183 clearTimeout(this._timeout);
184 this._timeout = null;
185 }
186 if(!this.activeDraggable) return;
187 this._lastPointer = null;
188 this.activeDraggable.endDrag(event);
189 this.activeDraggable = null;
190 },
191
192 keyPress: function(event) {
193 if(this.activeDraggable)
194 this.activeDraggable.keyPress(event);
195 },
196
197 addObserver: function(observer) {
198 this.observers.push(observer);
199 this._cacheObserverCallbacks();
200 },
201
202 removeObserver: function(element) { // element instead of observer fixes mem leaks
203 this.observers = this.observers.reject( function(o) { return o.element==element });
204 this._cacheObserverCallbacks();
205 },
206
207 notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
208 if(this[eventName+'Count'] > 0)
209 this.observers.each( function(o) {
210 if(o[eventName]) o[eventName](eventName, draggable, event);
211 });
212 if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
213 },
214
215 _cacheObserverCallbacks: function() {
216 ['onStart','onEnd','onDrag'].each( function(eventName) {
217 Draggables[eventName+'Count'] = Draggables.observers.select(
218 function(o) { return o[eventName]; }
219 ).length;
220 });
221 }
222}
223
224/*--------------------------------------------------------------------------*/
225
226var Draggable = Class.create();
227Draggable._dragging = {};
228
229Draggable.prototype = {
230 initialize: function(element) {
231 var defaults = {
232 handle: false,
233 reverteffect: function(element, top_offset, left_offset) {
234 var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
235 new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
236 queue: {scope:'_draggable', position:'end'}
237 });
238 },
239 endeffect: function(element) {
240 var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
241 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
242 queue: {scope:'_draggable', position:'end'},
243 afterFinish: function(){
244 Draggable._dragging[element] = false
245 }
246 });
247 },
248 zindex: 1000,
249 revert: false,
250 quiet: false,
251 scroll: false,
252 scrollSensitivity: 20,
253 scrollSpeed: 15,
254 snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
255 delay: 0
256 };
257
258 if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
259 Object.extend(defaults, {
260 starteffect: function(element) {
261 element._opacity = Element.getOpacity(element);
262 Draggable._dragging[element] = true;
263 new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
264 }
265 });
266
267 var options = Object.extend(defaults, arguments[1] || {});
268
269 this.element = $(element);
270
271 if(options.handle && (typeof options.handle == 'string'))
272 this.handle = this.element.down('.'+options.handle, 0);
273
274 if(!this.handle) this.handle = $(options.handle);
275 if(!this.handle) this.handle = this.element;
276
277 if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
278 options.scroll = $(options.scroll);
279 this._isScrollChild = Element.childOf(this.element, options.scroll);
280 }
281
282 Element.makePositioned(this.element); // fix IE
283
284 this.delta = this.currentDelta();
285 this.options = options;
286 this.dragging = false;
287
288 this.eventMouseDown = this.initDrag.bindAsEventListener(this);
289 Event.observe(this.handle, "mousedown", this.eventMouseDown);
290
291 Draggables.register(this);
292 },
293
294 destroy: function() {
295 Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
296 Draggables.unregister(this);
297 },
298
299 currentDelta: function() {
300 return([
301 parseInt(Element.getStyle(this.element,'left') || '0'),
302 parseInt(Element.getStyle(this.element,'top') || '0')]);
303 },
304
305 initDrag: function(event) {
306 if(typeof Draggable._dragging[this.element] != 'undefined' &&
307 Draggable._dragging[this.element]) return;
308 if(Event.isLeftClick(event)) {
309 // abort on form elements, fixes a Firefox issue
310 var src = Event.element(event);
311 if((tag_name = src.tagName.toUpperCase()) && (
312 tag_name=='INPUT' ||
313 tag_name=='SELECT' ||
314 tag_name=='OPTION' ||
315 tag_name=='BUTTON' ||
316 tag_name=='TEXTAREA')) return;
317
318 var pointer = [Event.pointerX(event), Event.pointerY(event)];
319 var pos = Position.cumulativeOffset(this.element);
320 this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
321
322 Draggables.activate(this);
323 Event.stop(event);
324 }
325 },
326
327 startDrag: function(event) {
328 this.dragging = true;
329
330 if(this.options.zindex) {
331 this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
332 this.element.style.zIndex = this.options.zindex;
333 }
334
335 if(this.options.ghosting) {
336 this._clone = this.element.cloneNode(true);
337 Position.absolutize(this.element);
338 this.element.parentNode.insertBefore(this._clone, this.element);
339 }
340
341 if(this.options.scroll) {
342 if (this.options.scroll == window) {
343 var where = this._getWindowScroll(this.options.scroll);
344 this.originalScrollLeft = where.left;
345 this.originalScrollTop = where.top;
346 } else {
347 this.originalScrollLeft = this.options.scroll.scrollLeft;
348 this.originalScrollTop = this.options.scroll.scrollTop;
349 }
350 }
351
352 Draggables.notify('onStart', this, event);
353
354 if(this.options.starteffect) this.options.starteffect(this.element);
355 },
356
357 updateDrag: function(event, pointer) {
358 if(!this.dragging) this.startDrag(event);
359
360 if(!this.options.quiet){
361 Position.prepare();
362 Droppables.show(pointer, this.element);
363 }
364
365 Draggables.notify('onDrag', this, event);
366
367 this.draw(pointer);
368 if(this.options.change) this.options.change(this);
369
370 if(this.options.scroll) {
371 this.stopScrolling();
372
373 var p;
374 if (this.options.scroll == window) {
375 with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
376 } else {
377 p = Position.page(this.options.scroll);
378 p[0] += this.options.scroll.scrollLeft + Position.deltaX;
379 p[1] += this.options.scroll.scrollTop + Position.deltaY;
380 p.push(p[0]+this.options.scroll.offsetWidth);
381 p.push(p[1]+this.options.scroll.offsetHeight);
382 }
383 var speed = [0,0];
384 if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
385 if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
386 if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
387 if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
388 this.startScrolling(speed);
389 }
390
391 // fix AppleWebKit rendering
392 if(Prototype.Browser.WebKit) window.scrollBy(0,0);
393
394 Event.stop(event);
395 },
396
397 finishDrag: function(event, success) {
398 this.dragging = false;
399
400 if(this.options.quiet){
401 Position.prepare();
402 var pointer = [Event.pointerX(event), Event.pointerY(event)];
403 Droppables.show(pointer, this.element);
404 }
405
406 if(this.options.ghosting) {
407 Position.relativize(this.element);
408 Element.remove(this._clone);
409 this._clone = null;
410 }
411
412 var dropped = false;
413 if(success) {
414 dropped = Droppables.fire(event, this.element);
415 if (!dropped) dropped = false;
416 }
417 if(dropped && this.options.onDropped) this.options.onDropped(this.element);
418 Draggables.notify('onEnd', this, event);
419
420 var revert = this.options.revert;
421 if(revert && typeof revert == 'function') revert = revert(this.element);
422
423 var d = this.currentDelta();
424 if(revert && this.options.reverteffect) {
425 if (dropped == 0 || revert != 'failure')
426 this.options.reverteffect(this.element,
427 d[1]-this.delta[1], d[0]-this.delta[0]);
428 } else {
429 this.delta = d;
430 }
431
432 if(this.options.zindex)
433 this.element.style.zIndex = this.originalZ;
434
435 if(this.options.endeffect)
436 this.options.endeffect(this.element);
437
438 Draggables.deactivate(this);
439 Droppables.reset();
440 },
441
442 keyPress: function(event) {
443 if(event.keyCode!=Event.KEY_ESC) return;
444 this.finishDrag(event, false);
445 Event.stop(event);
446 },
447
448 endDrag: function(event) {
449 if(!this.dragging) return;
450 this.stopScrolling();
451 this.finishDrag(event, true);
452 Event.stop(event);
453 },
454
455 draw: function(point) {
456 var pos = Position.cumulativeOffset(this.element);
457 if(this.options.ghosting) {
458 var r = Position.realOffset(this.element);
459 pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
460 }
461
462 var d = this.currentDelta();
463 pos[0] -= d[0]; pos[1] -= d[1];
464
465 if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
466 pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
467 pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
468 }
469
470 var p = [0,1].map(function(i){
471 return (point[i]-pos[i]-this.offset[i])
472 }.bind(this));
473
474 if(this.options.snap) {
475 if(typeof this.options.snap == 'function') {
476 p = this.options.snap(p[0],p[1],this);
477 } else {
478 if(this.options.snap instanceof Array) {
479 p = p.map( function(v, i) {
480 return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
481 } else {
482 p = p.map( function(v) {
483 return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
484 }
485 }}
486
487 var style = this.element.style;
488 if((!this.options.constraint) || (this.options.constraint=='horizontal'))
489 style.left = p[0] + "px";
490 if((!this.options.constraint) || (this.options.constraint=='vertical'))
491 style.top = p[1] + "px";
492
493 if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
494 },
495
496 stopScrolling: function() {
497 if(this.scrollInterval) {
498 clearInterval(this.scrollInterval);
499 this.scrollInterval = null;
500 Draggables._lastScrollPointer = null;
501 }
502 },
503
504 startScrolling: function(speed) {
505 if(!(speed[0] || speed[1])) return;
506 this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
507 this.lastScrolled = new Date();
508 this.scrollInterval = setInterval(this.scroll.bind(this), 10);
509 },
510
511 scroll: function() {
512 var current = new Date();
513 var delta = current - this.lastScrolled;
514 this.lastScrolled = current;
515 if(this.options.scroll == window) {
516 with (this._getWindowScroll(this.options.scroll)) {
517 if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
518 var d = delta / 1000;
519 this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
520 }
521 }
522 } else {
523 this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
524 this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
525 }
526
527 Position.prepare();
528 Droppables.show(Draggables._lastPointer, this.element);
529 Draggables.notify('onDrag', this);
530 if (this._isScrollChild) {
531 Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
532 Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
533 Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
534 if (Draggables._lastScrollPointer[0] < 0)
535 Draggables._lastScrollPointer[0] = 0;
536 if (Draggables._lastScrollPointer[1] < 0)
537 Draggables._lastScrollPointer[1] = 0;
538 this.draw(Draggables._lastScrollPointer);
539 }
540
541 if(this.options.change) this.options.change(this);
542 },
543
544 _getWindowScroll: function(w) {
545 var T, L, W, H;
546 with (w.document) {
547 if (w.document.documentElement && documentElement.scrollTop) {
548 T = documentElement.scrollTop;
549 L = documentElement.scrollLeft;
550 } else if (w.document.body) {
551 T = body.scrollTop;
552 L = body.scrollLeft;
553 }
554 if (w.innerWidth) {
555 W = w.innerWidth;
556 H = w.innerHeight;
557 } else if (w.document.documentElement && documentElement.clientWidth) {
558 W = documentElement.clientWidth;
559 H = documentElement.clientHeight;
560 } else {
561 W = body.offsetWidth;
562 H = body.offsetHeight
563 }
564 }
565 return { top: T, left: L, width: W, height: H };
566 }
567}
568
569/*--------------------------------------------------------------------------*/
570
571var SortableObserver = Class.create();
572SortableObserver.prototype = {
573 initialize: function(element, observer) {
574 this.element = $(element);
575 this.observer = observer;
576 this.lastValue = Sortable.serialize(this.element);
577 },
578
579 onStart: function() {
580 this.lastValue = Sortable.serialize(this.element);
581 },
582
583 onEnd: function() {
584 Sortable.unmark();
585 if(this.lastValue != Sortable.serialize(this.element))
586 this.observer(this.element)
587 }
588}
589
590var Sortable = {
591 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
592
593 sortables: {},
594
595 _findRootElement: function(element) {
596 while (element.tagName.toUpperCase() != "BODY") {
597 if(element.id && Sortable.sortables[element.id]) return element;
598 element = element.parentNode;
599 }
600 },
601
602 options: function(element) {
603 element = Sortable._findRootElement($(element));
604 if(!element) return;
605 return Sortable.sortables[element.id];
606 },
607
608 destroy: function(element){
609 var s = Sortable.options(element);
610
611 if(s) {
612 Draggables.removeObserver(s.element);
613 s.droppables.each(function(d){ Droppables.remove(d) });
614 s.draggables.invoke('destroy');
615
616 delete Sortable.sortables[s.element.id];
617 }
618 },
619
620 create: function(element) {
621 element = $(element);
622 var options = Object.extend({
623 element: element,
624 tag: 'li', // assumes li children, override with tag: 'tagname'
625 dropOnEmpty: false,
626 tree: false,
627 treeTag: 'ul',
628 overlap: 'vertical', // one of 'vertical', 'horizontal'
629 constraint: 'vertical', // one of 'vertical', 'horizontal', false
630 containment: element, // also takes array of elements (or id's); or false
631 handle: false, // or a CSS class
632 only: false,
633 delay: 0,
634 hoverclass: null,
635 ghosting: false,
636 quiet: false,
637 scroll: false,
638 scrollSensitivity: 20,
639 scrollSpeed: 15,
640 format: this.SERIALIZE_RULE,
641
642 // these take arrays of elements or ids and can be
643 // used for better initialization performance
644 elements: false,
645 handles: false,
646
647 onChange: Prototype.emptyFunction,
648 onUpdate: Prototype.emptyFunction
649 }, arguments[1] || {});
650
651 // clear any old sortable with same element
652 this.destroy(element);
653
654 // build options for the draggables
655 var options_for_draggable = {
656 revert: true,
657 quiet: options.quiet,
658 scroll: options.scroll,
659 scrollSpeed: options.scrollSpeed,
660 scrollSensitivity: options.scrollSensitivity,
661 delay: options.delay,
662 ghosting: options.ghosting,
663 constraint: options.constraint,
664 handle: options.handle };
665
666 if(options.starteffect)
667 options_for_draggable.starteffect = options.starteffect;
668
669 if(options.reverteffect)
670 options_for_draggable.reverteffect = options.reverteffect;
671 else
672 if(options.ghosting) options_for_draggable.reverteffect = function(element) {
673 element.style.top = 0;
674 element.style.left = 0;
675 };
676
677 if(options.endeffect)
678 options_for_draggable.endeffect = options.endeffect;
679
680 if(options.zindex)
681 options_for_draggable.zindex = options.zindex;
682
683 // build options for the droppables
684 var options_for_droppable = {
685 overlap: options.overlap,
686 containment: options.containment,
687 tree: options.tree,
688 hoverclass: options.hoverclass,
689 onHover: Sortable.onHover
690 }
691
692 var options_for_tree = {
693 onHover: Sortable.onEmptyHover,
694 overlap: options.overlap,
695 containment: options.containment,
696 hoverclass: options.hoverclass
697 }
698
699 // fix for gecko engine
700 Element.cleanWhitespace(element);
701
702 options.draggables = [];
703 options.droppables = [];
704
705 // drop on empty handling
706 if(options.dropOnEmpty || options.tree) {
707 Droppables.add(element, options_for_tree);
708 options.droppables.push(element);
709 }
710
711 (options.elements || this.findElements(element, options) || []).each( function(e,i) {
712 var handle = options.handles ? $(options.handles[i]) :
713 (options.handle ? $(e).getElementsByClassName(options.handle)[0] : e);
714 options.draggables.push(
715 new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
716 Droppables.add(e, options_for_droppable);
717 if(options.tree) e.treeNode = element;
718 options.droppables.push(e);
719 });
720
721 if(options.tree) {
722 (Sortable.findTreeElements(element, options) || []).each( function(e) {
723 Droppables.add(e, options_for_tree);
724 e.treeNode = element;
725 options.droppables.push(e);
726 });
727 }
728
729 // keep reference
730 this.sortables[element.id] = options;
731
732 // for onupdate
733 Draggables.addObserver(new SortableObserver(element, options.onUpdate));
734
735 },
736
737 // return all suitable-for-sortable elements in a guaranteed order
738 findElements: function(element, options) {
739 return Element.findChildren(
740 element, options.only, options.tree ? true : false, options.tag);
741 },
742
743 findTreeElements: function(element, options) {
744 return Element.findChildren(
745 element, options.only, options.tree ? true : false, options.treeTag);
746 },
747
748 onHover: function(element, dropon, overlap) {
749 if(Element.isParent(dropon, element)) return;
750
751 if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
752 return;
753 } else if(overlap>0.5) {
754 Sortable.mark(dropon, 'before');
755 if(dropon.previousSibling != element) {
756 var oldParentNode = element.parentNode;
757 element.style.visibility = "hidden"; // fix gecko rendering
758 dropon.parentNode.insertBefore(element, dropon);
759 if(dropon.parentNode!=oldParentNode)
760 Sortable.options(oldParentNode).onChange(element);
761 Sortable.options(dropon.parentNode).onChange(element);
762 }
763 } else {
764 Sortable.mark(dropon, 'after');
765 var nextElement = dropon.nextSibling || null;
766 if(nextElement != element) {
767 var oldParentNode = element.parentNode;
768 element.style.visibility = "hidden"; // fix gecko rendering
769 dropon.parentNode.insertBefore(element, nextElement);
770 if(dropon.parentNode!=oldParentNode)
771 Sortable.options(oldParentNode).onChange(element);
772 Sortable.options(dropon.parentNode).onChange(element);
773 }
774 }
775 },
776
777 onEmptyHover: function(element, dropon, overlap) {
778 var oldParentNode = element.parentNode;
779 var droponOptions = Sortable.options(dropon);
780
781 if(!Element.isParent(dropon, element)) {
782 var index;
783
784 var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
785 var child = null;
786
787 if(children) {
788 var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
789
790 for (index = 0; index < children.length; index += 1) {
791 if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
792 offset -= Element.offsetSize (children[index], droponOptions.overlap);
793 } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
794 child = index + 1 < children.length ? children[index + 1] : null;
795 break;
796 } else {
797 child = children[index];
798 break;
799 }
800 }
801 }
802
803 dropon.insertBefore(element, child);
804
805 Sortable.options(oldParentNode).onChange(element);
806 droponOptions.onChange(element);
807 }
808 },
809
810 unmark: function() {
811 if(Sortable._marker) Sortable._marker.hide();
812 },
813
814 mark: function(dropon, position) {
815 // mark on ghosting only
816 var sortable = Sortable.options(dropon.parentNode);
817 if(sortable && !sortable.ghosting) return;
818
819 if(!Sortable._marker) {
820 Sortable._marker =
821 ($('dropmarker') || Element.extend(document.createElement('DIV'))).
822 hide().addClassName('dropmarker').setStyle({position:'absolute'});
823 document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
824 }
825 var offsets = Position.cumulativeOffset(dropon);
826 Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
827
828 if(position=='after')
829 if(sortable.overlap == 'horizontal')
830 Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
831 else
832 Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
833
834 Sortable._marker.show();
835 },
836
837 _tree: function(element, options, parent) {
838 var children = Sortable.findElements(element, options) || [];
839
840 for (var i = 0; i < children.length; ++i) {
841 var match = children[i].id.match(options.format);
842
843 if (!match) continue;
844
845 var child = {
846 id: encodeURIComponent(match ? match[1] : null),
847 element: element,
848 parent: parent,
849 children: [],
850 position: parent.children.length,
851 container: $(children[i]).down(options.treeTag)
852 }
853
854 /* Get the element containing the children and recurse over it */
855 if (child.container)
856 this._tree(child.container, options, child)
857
858 parent.children.push (child);
859 }
860
861 return parent;
862 },
863
864 tree: function(element) {
865 element = $(element);
866 var sortableOptions = this.options(element);
867 var options = Object.extend({
868 tag: sortableOptions.tag,
869 treeTag: sortableOptions.treeTag,
870 only: sortableOptions.only,
871 name: element.id,
872 format: sortableOptions.format
873 }, arguments[1] || {});
874
875 var root = {
876 id: null,
877 parent: null,
878 children: [],
879 container: element,
880 position: 0
881 }
882
883 return Sortable._tree(element, options, root);
884 },
885
886 /* Construct a [i] index for a particular node */
887 _constructIndex: function(node) {
888 var index = '';
889 do {
890 if (node.id) index = '[' + node.position + ']' + index;
891 } while ((node = node.parent) != null);
892 return index;
893 },
894
895 sequence: function(element) {
896 element = $(element);
897 var options = Object.extend(this.options(element), arguments[1] || {});
898
899 return $(this.findElements(element, options) || []).map( function(item) {
900 return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
901 });
902 },
903
904 setSequence: function(element, new_sequence) {
905 element = $(element);
906 var options = Object.extend(this.options(element), arguments[2] || {});
907
908 var nodeMap = {};
909 this.findElements(element, options).each( function(n) {
910 if (n.id.match(options.format))
911 nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
912 n.parentNode.removeChild(n);
913 });
914
915 new_sequence.each(function(ident) {
916 var n = nodeMap[ident];
917 if (n) {
918 n[1].appendChild(n[0]);
919 delete nodeMap[ident];
920 }
921 });
922 },
923
924 serialize: function(element) {
925 element = $(element);
926 var options = Object.extend(Sortable.options(element), arguments[1] || {});
927 var name = encodeURIComponent(
928 (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
929
930 if (options.tree) {
931 return Sortable.tree(element, arguments[1]).children.map( function (item) {
932 return [name + Sortable._constructIndex(item) + "[id]=" +
933 encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
934 }).flatten().join('&');
935 } else {
936 return Sortable.sequence(element, arguments[1]).map( function(item) {
937 return name + "[]=" + encodeURIComponent(item);
938 }).join('&');
939 }
940 }
941}
942
943// Returns true if child is contained within element
944Element.isParent = function(child, element) {
945 if (!child.parentNode || child == element) return false;
946 if (child.parentNode == element) return true;
947 return Element.isParent(child.parentNode, element);
948}
949
950Element.findChildren = function(element, only, recursive, tagName) {
951 if(!element.hasChildNodes()) return null;
952 tagName = tagName.toUpperCase();
953 if(only) only = [only].flatten();
954 var elements = [];
955 $A(element.childNodes).each( function(e) {
956 if(e.tagName && e.tagName.toUpperCase()==tagName &&
957 (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
958 elements.push(e);
959 if(recursive) {
960 var grandchildren = Element.findChildren(e, only, recursive, tagName);
961 if(grandchildren) elements.push(grandchildren);
962 }
963 });
964
965 return (elements.length>0 ? elements.flatten() : []);
966}
967
968Element.offsetSize = function (element, type) {
969 return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
970}
diff --git a/docroot/lib/scriptaculous/effects.js b/docroot/lib/scriptaculous/effects.js
new file mode 100755
index 0000000..70d0752
--- /dev/null
+++ b/docroot/lib/scriptaculous/effects.js
@@ -0,0 +1,1094 @@
1// script.aculo.us effects.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007
2
3// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4// Contributors:
5// Justin Palmer (http://encytemedia.com/)
6// Mark Pilgrim (http://diveintomark.org/)
7// Martin Bialasinki
8//
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/
11
12// converts rgb() and #xxx to #xxxxxx format,
13// returns self (or first argument) if not convertable
14String.prototype.parseColor = function() {
15 var color = '#';
16 if(this.slice(0,4) == 'rgb(') {
17 var cols = this.slice(4,this.length-1).split(',');
18 var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
19 } else {
20 if(this.slice(0,1) == '#') {
21 if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
22 if(this.length==7) color = this.toLowerCase();
23 }
24 }
25 return(color.length==7 ? color : (arguments[0] || this));
26}
27
28/*--------------------------------------------------------------------------*/
29
30Element.collectTextNodes = function(element) {
31 return $A($(element).childNodes).collect( function(node) {
32 return (node.nodeType==3 ? node.nodeValue :
33 (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
34 }).flatten().join('');
35}
36
37Element.collectTextNodesIgnoreClass = function(element, className) {
38 return $A($(element).childNodes).collect( function(node) {
39 return (node.nodeType==3 ? node.nodeValue :
40 ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
41 Element.collectTextNodesIgnoreClass(node, className) : ''));
42 }).flatten().join('');
43}
44
45Element.setContentZoom = function(element, percent) {
46 element = $(element);
47 element.setStyle({fontSize: (percent/100) + 'em'});
48 if(Prototype.Browser.WebKit) window.scrollBy(0,0);
49 return element;
50}
51
52Element.getInlineOpacity = function(element){
53 return $(element).style.opacity || '';
54}
55
56Element.forceRerendering = function(element) {
57 try {
58 element = $(element);
59 var n = document.createTextNode(' ');
60 element.appendChild(n);
61 element.removeChild(n);
62 } catch(e) { }
63};
64
65/*--------------------------------------------------------------------------*/
66
67Array.prototype.call = function() {
68 var args = arguments;
69 this.each(function(f){ f.apply(this, args) });
70}
71
72/*--------------------------------------------------------------------------*/
73
74var Effect = {
75 _elementDoesNotExistError: {
76 name: 'ElementDoesNotExistError',
77 message: 'The specified DOM element does not exist, but is required for this effect to operate'
78 },
79 tagifyText: function(element) {
80 if(typeof Builder == 'undefined')
81 throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
82
83 var tagifyStyle = 'position:relative';
84 if(Prototype.Browser.IE) tagifyStyle += ';zoom:1';
85
86 element = $(element);
87 $A(element.childNodes).each( function(child) {
88 if(child.nodeType==3) {
89 child.nodeValue.toArray().each( function(character) {
90 element.insertBefore(
91 Builder.node('span',{style: tagifyStyle},
92 character == ' ' ? String.fromCharCode(160) : character),
93 child);
94 });
95 Element.remove(child);
96 }
97 });
98 },
99 multiple: function(element, effect) {
100 var elements;
101 if(((typeof element == 'object') ||
102 (typeof element == 'function')) &&
103 (element.length))
104 elements = element;
105 else
106 elements = $(element).childNodes;
107
108 var options = Object.extend({
109 speed: 0.1,
110 delay: 0.0
111 }, arguments[2] || {});
112 var masterDelay = options.delay;
113
114 $A(elements).each( function(element, index) {
115 new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
116 });
117 },
118 PAIRS: {
119 'slide': ['SlideDown','SlideUp'],
120 'blind': ['BlindDown','BlindUp'],
121 'appear': ['Appear','Fade']
122 },
123 toggle: function(element, effect) {
124 element = $(element);
125 effect = (effect || 'appear').toLowerCase();
126 var options = Object.extend({
127 queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
128 }, arguments[2] || {});
129 Effect[element.visible() ?
130 Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
131 }
132};
133
134var Effect2 = Effect; // deprecated
135
136/* ------------- transitions ------------- */
137
138Effect.Transitions = {
139 linear: Prototype.K,
140 sinoidal: function(pos) {
141 return (-Math.cos(pos*Math.PI)/2) + 0.5;
142 },
143 reverse: function(pos) {
144 return 1-pos;
145 },
146 flicker: function(pos) {
147 var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
148 return (pos > 1 ? 1 : pos);
149 },
150 wobble: function(pos) {
151 return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
152 },
153 pulse: function(pos, pulses) {
154 pulses = pulses || 5;
155 return (
156 Math.round((pos % (1/pulses)) * pulses) == 0 ?
157 ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) :
158 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
159 );
160 },
161 none: function(pos) {
162 return 0;
163 },
164 full: function(pos) {
165 return 1;
166 }
167};
168
169/* ------------- core effects ------------- */
170
171Effect.ScopedQueue = Class.create();
172Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
173 initialize: function() {
174 this.effects = [];
175 this.interval = null;
176 },
177 _each: function(iterator) {
178 this.effects._each(iterator);
179 },
180 add: function(effect) {
181 var timestamp = new Date().getTime();
182
183 var position = (typeof effect.options.queue == 'string') ?
184 effect.options.queue : effect.options.queue.position;
185
186 switch(position) {
187 case 'front':
188 // move unstarted effects after this effect
189 this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
190 e.startOn += effect.finishOn;
191 e.finishOn += effect.finishOn;
192 });
193 break;
194 case 'with-last':
195 timestamp = this.effects.pluck('startOn').max() || timestamp;
196 break;
197 case 'end':
198 // start effect after last queued effect has finished
199 timestamp = this.effects.pluck('finishOn').max() || timestamp;
200 break;
201 }
202
203 effect.startOn += timestamp;
204 effect.finishOn += timestamp;
205
206 if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
207 this.effects.push(effect);
208
209 if(!this.interval)
210 this.interval = setInterval(this.loop.bind(this), 15);
211 },
212 remove: function(effect) {
213 this.effects = this.effects.reject(function(e) { return e==effect });
214 if(this.effects.length == 0) {
215 clearInterval(this.interval);
216 this.interval = null;
217 }
218 },
219 loop: function() {
220 var timePos = new Date().getTime();
221 for(var i=0, len=this.effects.length;i<len;i++)
222 this.effects[i] && this.effects[i].loop(timePos);
223 }
224});
225
226Effect.Queues = {
227 instances: $H(),
228 get: function(queueName) {
229 if(typeof queueName != 'string') return queueName;
230
231 if(!this.instances[queueName])
232 this.instances[queueName] = new Effect.ScopedQueue();
233
234 return this.instances[queueName];
235 }
236}
237Effect.Queue = Effect.Queues.get('global');
238
239Effect.DefaultOptions = {
240 transition: Effect.Transitions.sinoidal,
241 duration: 1.0, // seconds
242 fps: 100, // 100= assume 66fps max.
243 sync: false, // true for combining
244 from: 0.0,
245 to: 1.0,
246 delay: 0.0,
247 queue: 'parallel'
248}
249
250Effect.Base = function() {};
251Effect.Base.prototype = {
252 position: null,
253 start: function(options) {
254 function codeForEvent(options,eventName){
255 return (
256 (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
257 (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
258 );
259 }
260 if(options.transition === false) options.transition = Effect.Transitions.linear;
261 this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
262 this.currentFrame = 0;
263 this.state = 'idle';
264 this.startOn = this.options.delay*1000;
265 this.finishOn = this.startOn+(this.options.duration*1000);
266 this.fromToDelta = this.options.to-this.options.from;
267 this.totalTime = this.finishOn-this.startOn;
268 this.totalFrames = this.options.fps*this.options.duration;
269
270 eval('this.render = function(pos){ '+
271 'if(this.state=="idle"){this.state="running";'+
272 codeForEvent(options,'beforeSetup')+
273 (this.setup ? 'this.setup();':'')+
274 codeForEvent(options,'afterSetup')+
275 '};if(this.state=="running"){'+
276 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
277 'this.position=pos;'+
278 codeForEvent(options,'beforeUpdate')+
279 (this.update ? 'this.update(pos);':'')+
280 codeForEvent(options,'afterUpdate')+
281 '}}');
282
283 this.event('beforeStart');
284 if(!this.options.sync)
285 Effect.Queues.get(typeof this.options.queue == 'string' ?
286 'global' : this.options.queue.scope).add(this);
287 },
288 loop: function(timePos) {
289 if(timePos >= this.startOn) {
290 if(timePos >= this.finishOn) {
291 this.render(1.0);
292 this.cancel();
293 this.event('beforeFinish');
294 if(this.finish) this.finish();
295 this.event('afterFinish');
296 return;
297 }
298 var pos = (timePos - this.startOn) / this.totalTime,
299 frame = Math.round(pos * this.totalFrames);
300 if(frame > this.currentFrame) {
301 this.render(pos);
302 this.currentFrame = frame;
303 }
304 }
305 },
306 cancel: function() {
307 if(!this.options.sync)
308 Effect.Queues.get(typeof this.options.queue == 'string' ?
309 'global' : this.options.queue.scope).remove(this);
310 this.state = 'finished';
311 },
312 event: function(eventName) {
313 if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
314 if(this.options[eventName]) this.options[eventName](this);
315 },
316 inspect: function() {
317 var data = $H();
318 for(property in this)
319 if(typeof this[property] != 'function') data[property] = this[property];
320 return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
321 }
322}
323
324Effect.Parallel = Class.create();
325Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
326 initialize: function(effects) {
327 this.effects = effects || [];
328 this.start(arguments[1]);
329 },
330 update: function(position) {
331 this.effects.invoke('render', position);
332 },
333 finish: function(position) {
334 this.effects.each( function(effect) {
335 effect.render(1.0);
336 effect.cancel();
337 effect.event('beforeFinish');
338 if(effect.finish) effect.finish(position);
339 effect.event('afterFinish');
340 });
341 }
342});
343
344Effect.Event = Class.create();
345Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
346 initialize: function() {
347 var options = Object.extend({
348 duration: 0
349 }, arguments[0] || {});
350 this.start(options);
351 },
352 update: Prototype.emptyFunction
353});
354
355Effect.Opacity = Class.create();
356Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
357 initialize: function(element) {
358 this.element = $(element);
359 if(!this.element) throw(Effect._elementDoesNotExistError);
360 // make this work on IE on elements without 'layout'
361 if(Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
362 this.element.setStyle({zoom: 1});
363 var options = Object.extend({
364 from: this.element.getOpacity() || 0.0,
365 to: 1.0
366 }, arguments[1] || {});
367 this.start(options);
368 },
369 update: function(position) {
370 this.element.setOpacity(position);
371 }
372});
373
374Effect.Move = Class.create();
375Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
376 initialize: function(element) {
377 this.element = $(element);
378 if(!this.element) throw(Effect._elementDoesNotExistError);
379 var options = Object.extend({
380 x: 0,
381 y: 0,
382 mode: 'relative'
383 }, arguments[1] || {});
384 this.start(options);
385 },
386 setup: function() {
387 // Bug in Opera: Opera returns the "real" position of a static element or
388 // relative element that does not have top/left explicitly set.
389 // ==> Always set top and left for position relative elements in your stylesheets
390 // (to 0 if you do not need them)
391 this.element.makePositioned();
392 this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
393 this.originalTop = parseFloat(this.element.getStyle('top') || '0');
394 if(this.options.mode == 'absolute') {
395 // absolute movement, so we need to calc deltaX and deltaY
396 this.options.x = this.options.x - this.originalLeft;
397 this.options.y = this.options.y - this.originalTop;
398 }
399 },
400 update: function(position) {
401 this.element.setStyle({
402 left: Math.round(this.options.x * position + this.originalLeft) + 'px',
403 top: Math.round(this.options.y * position + this.originalTop) + 'px'
404 });
405 }
406});
407
408// for backwards compatibility
409Effect.MoveBy = function(element, toTop, toLeft) {
410 return new Effect.Move(element,
411 Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
412};
413
414Effect.Scale = Class.create();
415Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
416 initialize: function(element, percent) {
417 this.element = $(element);
418 if(!this.element) throw(Effect._elementDoesNotExistError);
419 var options = Object.extend({
420 scaleX: true,
421 scaleY: true,
422 scaleContent: true,
423 scaleFromCenter: false,
424 scaleMode: 'box', // 'box' or 'contents' or {} with provided values
425 scaleFrom: 100.0,
426 scaleTo: percent
427 }, arguments[2] || {});
428 this.start(options);
429 },
430 setup: function() {
431 this.restoreAfterFinish = this.options.restoreAfterFinish || false;
432 this.elementPositioning = this.element.getStyle('position');
433
434 this.originalStyle = {};
435 ['top','left','width','height','fontSize'].each( function(k) {
436 this.originalStyle[k] = this.element.style[k];
437 }.bind(this));
438
439 this.originalTop = this.element.offsetTop;
440 this.originalLeft = this.element.offsetLeft;
441
442 var fontSize = this.element.getStyle('font-size') || '100%';
443 ['em','px','%','pt'].each( function(fontSizeType) {
444 if(fontSize.indexOf(fontSizeType)>0) {
445 this.fontSize = parseFloat(fontSize);
446 this.fontSizeType = fontSizeType;
447 }
448 }.bind(this));
449
450 this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
451
452 this.dims = null;
453 if(this.options.scaleMode=='box')
454 this.dims = [this.element.offsetHeight, this.element.offsetWidth];
455 if(/^content/.test(this.options.scaleMode))
456 this.dims = [this.element.scrollHeight, this.element.scrollWidth];
457 if(!this.dims)
458 this.dims = [this.options.scaleMode.originalHeight,
459 this.options.scaleMode.originalWidth];
460 },
461 update: function(position) {
462 var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
463 if(this.options.scaleContent && this.fontSize)
464 this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
465 this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
466 },
467 finish: function(position) {
468 if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
469 },
470 setDimensions: function(height, width) {
471 var d = {};
472 if(this.options.scaleX) d.width = Math.round(width) + 'px';
473 if(this.options.scaleY) d.height = Math.round(height) + 'px';
474 if(this.options.scaleFromCenter) {
475 var topd = (height - this.dims[0])/2;
476 var leftd = (width - this.dims[1])/2;
477 if(this.elementPositioning == 'absolute') {
478 if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
479 if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
480 } else {
481 if(this.options.scaleY) d.top = -topd + 'px';
482 if(this.options.scaleX) d.left = -leftd + 'px';
483 }
484 }
485 this.element.setStyle(d);
486 }
487});
488
489Effect.Highlight = Class.create();
490Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
491 initialize: function(element) {
492 this.element = $(element);
493 if(!this.element) throw(Effect._elementDoesNotExistError);
494 var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
495 this.start(options);
496 },
497 setup: function() {
498 // Prevent executing on elements not in the layout flow
499 if(this.element.getStyle('display')=='none') { this.cancel(); return; }
500 // Disable background image during the effect
501 this.oldStyle = {};
502 if (!this.options.keepBackgroundImage) {
503 this.oldStyle.backgroundImage = this.element.getStyle('background-image');
504 this.element.setStyle({backgroundImage: 'none'});
505 }
506 if(!this.options.endcolor)
507 this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
508 if(!this.options.restorecolor)
509 this.options.restorecolor = this.element.getStyle('background-color');
510 // init color calculations
511 this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
512 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));
513 },
514 update: function(position) {
515 this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
516 return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
517 },
518 finish: function() {
519 this.element.setStyle(Object.extend(this.oldStyle, {
520 backgroundColor: this.options.restorecolor
521 }));
522 }
523});
524
525Effect.ScrollTo = Class.create();
526Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
527 initialize: function(element) {
528 this.element = $(element);
529 this.start(arguments[1] || {});
530 },
531 setup: function() {
532 Position.prepare();
533 var offsets = Position.cumulativeOffset(this.element);
534 if(this.options.offset) offsets[1] += this.options.offset;
535 var max = window.innerHeight ?
536 window.height - window.innerHeight :
537 document.body.scrollHeight -
538 (document.documentElement.clientHeight ?
539 document.documentElement.clientHeight : document.body.clientHeight);
540 this.scrollStart = Position.deltaY;
541 this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
542 },
543 update: function(position) {
544 Position.prepare();
545 window.scrollTo(Position.deltaX,
546 this.scrollStart + (position*this.delta));
547 }
548});
549
550/* ------------- combination effects ------------- */
551
552Effect.Fade = function(element) {
553 element = $(element);
554 var oldOpacity = element.getInlineOpacity();
555 var options = Object.extend({
556 from: element.getOpacity() || 1.0,
557 to: 0.0,
558 afterFinishInternal: function(effect) {
559 if(effect.options.to!=0) return;
560 effect.element.hide().setStyle({opacity: oldOpacity});
561 }}, arguments[1] || {});
562 return new Effect.Opacity(element,options);
563}
564
565Effect.Appear = function(element) {
566 element = $(element);
567 var options = Object.extend({
568 from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
569 to: 1.0,
570 // force Safari to render floated elements properly
571 afterFinishInternal: function(effect) {
572 effect.element.forceRerendering();
573 },
574 beforeSetup: function(effect) {
575 effect.element.setOpacity(effect.options.from).show();
576 }}, arguments[1] || {});
577 return new Effect.Opacity(element,options);
578}
579
580Effect.Puff = function(element) {
581 element = $(element);
582 var oldStyle = {
583 opacity: element.getInlineOpacity(),
584 position: element.getStyle('position'),
585 top: element.style.top,
586 left: element.style.left,
587 width: element.style.width,
588 height: element.style.height
589 };
590 return new Effect.Parallel(
591 [ new Effect.Scale(element, 200,
592 { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
593 new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
594 Object.extend({ duration: 1.0,
595 beforeSetupInternal: function(effect) {
596 Position.absolutize(effect.effects[0].element)
597 },
598 afterFinishInternal: function(effect) {
599 effect.effects[0].element.hide().setStyle(oldStyle); }
600 }, arguments[1] || {})
601 );
602}
603
604Effect.BlindUp = function(element) {
605 element = $(element);
606 element.makeClipping();
607 return new Effect.Scale(element, 0,
608 Object.extend({ scaleContent: false,
609 scaleX: false,
610 restoreAfterFinish: true,
611 afterFinishInternal: function(effect) {
612 effect.element.hide().undoClipping();
613 }
614 }, arguments[1] || {})
615 );
616}
617
618Effect.BlindDown = function(element) {
619 element = $(element);
620 var elementDimensions = element.getDimensions();
621 return new Effect.Scale(element, 100, Object.extend({
622 scaleContent: false,
623 scaleX: false,
624 scaleFrom: 0,
625 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
626 restoreAfterFinish: true,
627 afterSetup: function(effect) {
628 effect.element.makeClipping().setStyle({height: '0px'}).show();
629 },
630 afterFinishInternal: function(effect) {
631 effect.element.undoClipping();
632 }
633 }, arguments[1] || {}));
634}
635
636Effect.SwitchOff = function(element) {
637 element = $(element);
638 var oldOpacity = element.getInlineOpacity();
639 return new Effect.Appear(element, Object.extend({
640 duration: 0.4,
641 from: 0,
642 transition: Effect.Transitions.flicker,
643 afterFinishInternal: function(effect) {
644 new Effect.Scale(effect.element, 1, {
645 duration: 0.3, scaleFromCenter: true,
646 scaleX: false, scaleContent: false, restoreAfterFinish: true,
647 beforeSetup: function(effect) {
648 effect.element.makePositioned().makeClipping();
649 },
650 afterFinishInternal: function(effect) {
651 effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
652 }
653 })
654 }
655 }, arguments[1] || {}));
656}
657
658Effect.DropOut = function(element) {
659 element = $(element);
660 var oldStyle = {
661 top: element.getStyle('top'),
662 left: element.getStyle('left'),
663 opacity: element.getInlineOpacity() };
664 return new Effect.Parallel(
665 [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
666 new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
667 Object.extend(
668 { duration: 0.5,
669 beforeSetup: function(effect) {
670 effect.effects[0].element.makePositioned();
671 },
672 afterFinishInternal: function(effect) {
673 effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
674 }
675 }, arguments[1] || {}));
676}
677
678Effect.Shake = function(element) {
679 element = $(element);
680 var oldStyle = {
681 top: element.getStyle('top'),
682 left: element.getStyle('left') };
683 return new Effect.Move(element,
684 { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
685 new Effect.Move(effect.element,
686 { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
687 new Effect.Move(effect.element,
688 { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
689 new Effect.Move(effect.element,
690 { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
691 new Effect.Move(effect.element,
692 { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
693 new Effect.Move(effect.element,
694 { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
695 effect.element.undoPositioned().setStyle(oldStyle);
696 }}) }}) }}) }}) }}) }});
697}
698
699Effect.SlideDown = function(element) {
700 element = $(element).cleanWhitespace();
701 // SlideDown need to have the content of the element wrapped in a container element with fixed height!
702 var oldInnerBottom = element.down().getStyle('bottom');
703 var elementDimensions = element.getDimensions();
704 return new Effect.Scale(element, 100, Object.extend({
705 scaleContent: false,
706 scaleX: false,
707 scaleFrom: window.opera ? 0 : 1,
708 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
709 restoreAfterFinish: true,
710 afterSetup: function(effect) {
711 effect.element.makePositioned();
712 effect.element.down().makePositioned();
713 if(window.opera) effect.element.setStyle({top: ''});
714 effect.element.makeClipping().setStyle({height: '0px'}).show();
715 },
716 afterUpdateInternal: function(effect) {
717 effect.element.down().setStyle({bottom:
718 (effect.dims[0] - effect.element.clientHeight) + 'px' });
719 },
720 afterFinishInternal: function(effect) {
721 effect.element.undoClipping().undoPositioned();
722 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
723 }, arguments[1] || {})
724 );
725}
726
727Effect.SlideUp = function(element) {
728 element = $(element).cleanWhitespace();
729 var oldInnerBottom = element.down().getStyle('bottom');
730 return new Effect.Scale(element, window.opera ? 0 : 1,
731 Object.extend({ scaleContent: false,
732 scaleX: false,
733 scaleMode: 'box',
734 scaleFrom: 100,
735 restoreAfterFinish: true,
736 beforeStartInternal: function(effect) {
737 effect.element.makePositioned();
738 effect.element.down().makePositioned();
739 if(window.opera) effect.element.setStyle({top: ''});
740 effect.element.makeClipping().show();
741 },
742 afterUpdateInternal: function(effect) {
743 effect.element.down().setStyle({bottom:
744 (effect.dims[0] - effect.element.clientHeight) + 'px' });
745 },
746 afterFinishInternal: function(effect) {
747 effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
748 effect.element.down().undoPositioned();
749 }
750 }, arguments[1] || {})
751 );
752}
753
754// Bug in opera makes the TD containing this element expand for a instance after finish
755Effect.Squish = function(element) {
756 return new Effect.Scale(element, window.opera ? 1 : 0, {
757 restoreAfterFinish: true,
758 beforeSetup: function(effect) {
759 effect.element.makeClipping();
760 },
761 afterFinishInternal: function(effect) {
762 effect.element.hide().undoClipping();
763 }
764 });
765}
766
767Effect.Grow = function(element) {
768 element = $(element);
769 var options = Object.extend({
770 direction: 'center',
771 moveTransition: Effect.Transitions.sinoidal,
772 scaleTransition: Effect.Transitions.sinoidal,
773 opacityTransition: Effect.Transitions.full
774 }, arguments[1] || {});
775 var oldStyle = {
776 top: element.style.top,
777 left: element.style.left,
778 height: element.style.height,
779 width: element.style.width,
780 opacity: element.getInlineOpacity() };
781
782 var dims = element.getDimensions();
783 var initialMoveX, initialMoveY;
784 var moveX, moveY;
785
786 switch (options.direction) {
787 case 'top-left':
788 initialMoveX = initialMoveY = moveX = moveY = 0;
789 break;
790 case 'top-right':
791 initialMoveX = dims.width;
792 initialMoveY = moveY = 0;
793 moveX = -dims.width;
794 break;
795 case 'bottom-left':
796 initialMoveX = moveX = 0;
797 initialMoveY = dims.height;
798 moveY = -dims.height;
799 break;
800 case 'bottom-right':
801 initialMoveX = dims.width;
802 initialMoveY = dims.height;
803 moveX = -dims.width;
804 moveY = -dims.height;
805 break;
806 case 'center':
807 initialMoveX = dims.width / 2;
808 initialMoveY = dims.height / 2;
809 moveX = -dims.width / 2;
810 moveY = -dims.height / 2;
811 break;
812 }
813
814 return new Effect.Move(element, {
815 x: initialMoveX,
816 y: initialMoveY,
817 duration: 0.01,
818 beforeSetup: function(effect) {
819 effect.element.hide().makeClipping().makePositioned();
820 },
821 afterFinishInternal: function(effect) {
822 new Effect.Parallel(
823 [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
824 new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
825 new Effect.Scale(effect.element, 100, {
826 scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
827 sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
828 ], Object.extend({
829 beforeSetup: function(effect) {
830 effect.effects[0].element.setStyle({height: '0px'}).show();
831 },
832 afterFinishInternal: function(effect) {
833 effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
834 }
835 }, options)
836 )
837 }
838 });
839}
840
841Effect.Shrink = function(element) {
842 element = $(element);
843 var options = Object.extend({
844 direction: 'center',
845 moveTransition: Effect.Transitions.sinoidal,
846 scaleTransition: Effect.Transitions.sinoidal,
847 opacityTransition: Effect.Transitions.none
848 }, arguments[1] || {});
849 var oldStyle = {
850 top: element.style.top,
851 left: element.style.left,
852 height: element.style.height,
853 width: element.style.width,
854 opacity: element.getInlineOpacity() };
855
856 var dims = element.getDimensions();
857 var moveX, moveY;
858
859 switch (options.direction) {
860 case 'top-left':
861 moveX = moveY = 0;
862 break;
863 case 'top-right':
864 moveX = dims.width;
865 moveY = 0;
866 break;
867 case 'bottom-left':
868 moveX = 0;
869 moveY = dims.height;
870 break;
871 case 'bottom-right':
872 moveX = dims.width;
873 moveY = dims.height;
874 break;
875 case 'center':
876 moveX = dims.width / 2;
877 moveY = dims.height / 2;
878 break;
879 }
880
881 return new Effect.Parallel(
882 [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
883 new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
884 new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
885 ], Object.extend({
886 beforeStartInternal: function(effect) {
887 effect.effects[0].element.makePositioned().makeClipping();
888 },
889 afterFinishInternal: function(effect) {
890 effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
891 }, options)
892 );
893}
894
895Effect.Pulsate = function(element) {
896 element = $(element);
897 var options = arguments[1] || {};
898 var oldOpacity = element.getInlineOpacity();
899 var transition = options.transition || Effect.Transitions.sinoidal;
900 var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
901 reverser.bind(transition);
902 return new Effect.Opacity(element,
903 Object.extend(Object.extend({ duration: 2.0, from: 0,
904 afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
905 }, options), {transition: reverser}));
906}
907
908Effect.Fold = function(element) {
909 element = $(element);
910 var oldStyle = {
911 top: element.style.top,
912 left: element.style.left,
913 width: element.style.width,
914 height: element.style.height };
915 element.makeClipping();
916 return new Effect.Scale(element, 5, Object.extend({
917 scaleContent: false,
918 scaleX: false,
919 afterFinishInternal: function(effect) {
920 new Effect.Scale(element, 1, {
921 scaleContent: false,
922 scaleY: false,
923 afterFinishInternal: function(effect) {
924 effect.element.hide().undoClipping().setStyle(oldStyle);
925 } });
926 }}, arguments[1] || {}));
927};
928
929Effect.Morph = Class.create();
930Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
931 initialize: function(element) {
932 this.element = $(element);
933 if(!this.element) throw(Effect._elementDoesNotExistError);
934 var options = Object.extend({
935 style: {}
936 }, arguments[1] || {});
937 if (typeof options.style == 'string') {
938 if(options.style.indexOf(':') == -1) {
939 var cssText = '', selector = '.' + options.style;
940 $A(document.styleSheets).reverse().each(function(styleSheet) {
941 if (styleSheet.cssRules) cssRules = styleSheet.cssRules;
942 else if (styleSheet.rules) cssRules = styleSheet.rules;
943 $A(cssRules).reverse().each(function(rule) {
944 if (selector == rule.selectorText) {
945 cssText = rule.style.cssText;
946 throw $break;
947 }
948 });
949 if (cssText) throw $break;
950 });
951 this.style = cssText.parseStyle();
952 options.afterFinishInternal = function(effect){
953 effect.element.addClassName(effect.options.style);
954 effect.transforms.each(function(transform) {
955 if(transform.style != 'opacity')
956 effect.element.style[transform.style] = '';
957 });
958 }
959 } else this.style = options.style.parseStyle();
960 } else this.style = $H(options.style)
961 this.start(options);
962 },
963 setup: function(){
964 function parseColor(color){
965 if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
966 color = color.parseColor();
967 return $R(0,2).map(function(i){
968 return parseInt( color.slice(i*2+1,i*2+3), 16 )
969 });
970 }
971 this.transforms = this.style.map(function(pair){
972 var property = pair[0], value = pair[1], unit = null;
973
974 if(value.parseColor('#zzzzzz') != '#zzzzzz') {
975 value = value.parseColor();
976 unit = 'color';
977 } else if(property == 'opacity') {
978 value = parseFloat(value);
979 if(Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
980 this.element.setStyle({zoom: 1});
981 } else if(Element.CSS_LENGTH.test(value)) {
982 var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
983 value = parseFloat(components[1]);
984 unit = (components.length == 3) ? components[2] : null;
985 }
986
987 var originalValue = this.element.getStyle(property);
988 return {
989 style: property.camelize(),
990 originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
991 targetValue: unit=='color' ? parseColor(value) : value,
992 unit: unit
993 };
994 }.bind(this)).reject(function(transform){
995 return (
996 (transform.originalValue == transform.targetValue) ||
997 (
998 transform.unit != 'color' &&
999 (isNaN(transform.originalValue) || isNaN(transform.targetValue))
1000 )
1001 )
1002 });
1003 },
1004 update: function(position) {
1005 var style = {}, transform, i = this.transforms.length;
1006 while(i--)
1007 style[(transform = this.transforms[i]).style] =
1008 transform.unit=='color' ? '#'+
1009 (Math.round(transform.originalValue[0]+
1010 (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
1011 (Math.round(transform.originalValue[1]+
1012 (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
1013 (Math.round(transform.originalValue[2]+
1014 (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
1015 transform.originalValue + Math.round(
1016 ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
1017 this.element.setStyle(style, true);
1018 }
1019});
1020
1021Effect.Transform = Class.create();
1022Object.extend(Effect.Transform.prototype, {
1023 initialize: function(tracks){
1024 this.tracks = [];
1025 this.options = arguments[1] || {};
1026 this.addTracks(tracks);
1027 },
1028 addTracks: function(tracks){
1029 tracks.each(function(track){
1030 var data = $H(track).values().first();
1031 this.tracks.push($H({
1032 ids: $H(track).keys().first(),
1033 effect: Effect.Morph,
1034 options: { style: data }
1035 }));
1036 }.bind(this));
1037 return this;
1038 },
1039 play: function(){
1040 return new Effect.Parallel(
1041 this.tracks.map(function(track){
1042 var elements = [$(track.ids) || $$(track.ids)].flatten();
1043 return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
1044 }).flatten(),
1045 this.options
1046 );
1047 }
1048});
1049
1050Element.CSS_PROPERTIES = $w(
1051 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
1052 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
1053 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
1054 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
1055 'fontSize fontWeight height left letterSpacing lineHeight ' +
1056 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
1057 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
1058 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
1059 'right textIndent top width wordSpacing zIndex');
1060
1061Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1062
1063String.prototype.parseStyle = function(){
1064 var element = document.createElement('div');
1065 element.innerHTML = '<div style="' + this + '"></div>';
1066 var style = element.childNodes[0].style, styleRules = $H();
1067
1068 Element.CSS_PROPERTIES.each(function(property){
1069 if(style[property]) styleRules[property] = style[property];
1070 });
1071 if(Prototype.Browser.IE && this.indexOf('opacity') > -1) {
1072 styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1];
1073 }
1074 return styleRules;
1075};
1076
1077Element.morph = function(element, style) {
1078 new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
1079 return element;
1080};
1081
1082['getInlineOpacity','forceRerendering','setContentZoom',
1083 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each(
1084 function(f) { Element.Methods[f] = Element[f]; }
1085);
1086
1087Element.Methods.visualEffect = function(element, effect, options) {
1088 s = effect.dasherize().camelize();
1089 effect_class = s.charAt(0).toUpperCase() + s.substring(1);
1090 new Effect[effect_class](element, options);
1091 return $(element);
1092};
1093
1094Element.addMethods(); \ No newline at end of file
diff --git a/docroot/lib/scriptaculous/scriptaculous.js b/docroot/lib/scriptaculous/scriptaculous.js
new file mode 100755
index 0000000..7c472a6
--- /dev/null
+++ b/docroot/lib/scriptaculous/scriptaculous.js
@@ -0,0 +1,58 @@
1// script.aculo.us scriptaculous.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007
2
3// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4//
5// Permission is hereby granted, free of charge, to any person obtaining
6// a copy of this software and associated documentation files (the
7// "Software"), to deal in the Software without restriction, including
8// without limitation the rights to use, copy, modify, merge, publish,
9// distribute, sublicense, and/or sell copies of the Software, and to
10// permit persons to whom the Software is furnished to do so, subject to
11// the following conditions:
12//
13// The above copyright notice and this permission notice shall be
14// included in all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23//
24// For details, see the script.aculo.us web site: http://script.aculo.us/
25
26var Scriptaculous = {
27 Version: '1.7.1_beta3',
28 require: function(libraryName) {
29 // inserting via DOM fails in Safari 2.0, so brute force approach
30 document.write('<script type="text/javascript" src="'+libraryName+'"></script>');
31 },
32 REQUIRED_PROTOTYPE: '1.5.1',
33 load: function() {
34 function convertVersionString(versionString){
35 var r = versionString.split('.');
36 return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
37 }
38
39 if((typeof Prototype=='undefined') ||
40 (typeof Element == 'undefined') ||
41 (typeof Element.Methods=='undefined') ||
42 (convertVersionString(Prototype.Version) <
43 convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
44 throw("script.aculo.us requires the Prototype JavaScript framework >= " +
45 Scriptaculous.REQUIRED_PROTOTYPE);
46
47 $A(document.getElementsByTagName("script")).findAll( function(s) {
48 return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
49 }).each( function(s) {
50 var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
51 var includes = s.src.match(/\?.*load=([a-z,]*)/);
52 (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
53 function(include) { Scriptaculous.require(path+include+'.js') });
54 });
55 }
56}
57
58Scriptaculous.load(); \ No newline at end of file
diff --git a/docroot/lib/scriptaculous/slider.js b/docroot/lib/scriptaculous/slider.js
new file mode 100755
index 0000000..c1a84eb
--- /dev/null
+++ b/docroot/lib/scriptaculous/slider.js
@@ -0,0 +1,277 @@
1// script.aculo.us slider.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007
2
3// Copyright (c) 2005-2007 Marty Haught, Thomas Fuchs
4//
5// script.aculo.us is freely distributable under the terms of an MIT-style license.
6// For details, see the script.aculo.us web site: http://script.aculo.us/
7
8if(!Control) var Control = {};
9Control.Slider = Class.create();
10
11// options:
12// axis: 'vertical', or 'horizontal' (default)
13//
14// callbacks:
15// onChange(value)
16// onSlide(value)
17Control.Slider.prototype = {
18 initialize: function(handle, track, options) {
19 var slider = this;
20
21 if(handle instanceof Array) {
22 this.handles = handle.collect( function(e) { return $(e) });
23 } else {
24 this.handles = [$(handle)];
25 }
26
27 this.track = $(track);
28 this.options = options || {};
29
30 this.axis = this.options.axis || 'horizontal';
31 this.increment = this.options.increment || 1;
32 this.step = parseInt(this.options.step || '1');
33 this.range = this.options.range || $R(0,1);
34
35 this.value = 0; // assure backwards compat
36 this.values = this.handles.map( function() { return 0 });
37 this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
38 this.options.startSpan = $(this.options.startSpan || null);
39 this.options.endSpan = $(this.options.endSpan || null);
40
41 this.restricted = this.options.restricted || false;
42
43 this.maximum = this.options.maximum || this.range.end;
44 this.minimum = this.options.minimum || this.range.start;
45
46 // Will be used to align the handle onto the track, if necessary
47 this.alignX = parseInt(this.options.alignX || '0');
48 this.alignY = parseInt(this.options.alignY || '0');
49
50 this.trackLength = this.maximumOffset() - this.minimumOffset();
51
52 this.handleLength = this.isVertical() ?
53 (this.handles[0].offsetHeight != 0 ?
54 this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) :
55 (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth :
56 this.handles[0].style.width.replace(/px$/,""));
57
58 this.active = false;
59 this.dragging = false;
60 this.disabled = false;
61
62 if(this.options.disabled) this.setDisabled();
63
64 // Allowed values array
65 this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
66 if(this.allowedValues) {
67 this.minimum = this.allowedValues.min();
68 this.maximum = this.allowedValues.max();
69 }
70
71 this.eventMouseDown = this.startDrag.bindAsEventListener(this);
72 this.eventMouseUp = this.endDrag.bindAsEventListener(this);
73 this.eventMouseMove = this.update.bindAsEventListener(this);
74
75 // Initialize handles in reverse (make sure first handle is active)
76 this.handles.each( function(h,i) {
77 i = slider.handles.length-1-i;
78 slider.setValue(parseFloat(
79 (slider.options.sliderValue instanceof Array ?
80 slider.options.sliderValue[i] : slider.options.sliderValue) ||
81 slider.range.start), i);
82 Element.makePositioned(h); // fix IE
83 Event.observe(h, "mousedown", slider.eventMouseDown);
84 });
85
86 Event.observe(this.track, "mousedown", this.eventMouseDown);
87 Event.observe(document, "mouseup", this.eventMouseUp);
88 Event.observe(document, "mousemove", this.eventMouseMove);
89
90 this.initialized = true;
91 },
92 dispose: function() {
93 var slider = this;
94 Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
95 Event.stopObserving(document, "mouseup", this.eventMouseUp);
96 Event.stopObserving(document, "mousemove", this.eventMouseMove);
97 this.handles.each( function(h) {
98 Event.stopObserving(h, "mousedown", slider.eventMouseDown);
99 });
100 },
101 setDisabled: function(){
102 this.disabled = true;
103 },
104 setEnabled: function(){
105 this.disabled = false;
106 },
107 getNearestValue: function(value){
108 if(this.allowedValues){
109 if(value >= this.allowedValues.max()) return(this.allowedValues.max());
110 if(value <= this.allowedValues.min()) return(this.allowedValues.min());
111
112 var offset = Math.abs(this.allowedValues[0] - value);
113 var newValue = this.allowedValues[0];
114 this.allowedValues.each( function(v) {
115 var currentOffset = Math.abs(v - value);
116 if(currentOffset <= offset){
117 newValue = v;
118 offset = currentOffset;
119 }
120 });
121 return newValue;
122 }
123 if(value > this.range.end) return this.range.end;
124 if(value < this.range.start) return this.range.start;
125 return value;
126 },
127 setValue: function(sliderValue, handleIdx){
128 if(!this.active) {
129 this.activeHandleIdx = handleIdx || 0;
130 this.activeHandle = this.handles[this.activeHandleIdx];
131 this.updateStyles();
132 }
133 handleIdx = handleIdx || this.activeHandleIdx || 0;
134 if(this.initialized && this.restricted) {
135 if((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
136 sliderValue = this.values[handleIdx-1];
137 if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
138 sliderValue = this.values[handleIdx+1];
139 }
140 sliderValue = this.getNearestValue(sliderValue);
141 this.values[handleIdx] = sliderValue;
142 this.value = this.values[0]; // assure backwards compat
143
144 this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
145 this.translateToPx(sliderValue);
146
147 this.drawSpans();
148 if(!this.dragging || !this.event) this.updateFinished();
149 },
150 setValueBy: function(delta, handleIdx) {
151 this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
152 handleIdx || this.activeHandleIdx || 0);
153 },
154 translateToPx: function(value) {
155 return Math.round(
156 ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
157 (value - this.range.start)) + "px";
158 },
159 translateToValue: function(offset) {
160 return ((offset/(this.trackLength-this.handleLength) *
161 (this.range.end-this.range.start)) + this.range.start);
162 },
163 getRange: function(range) {
164 var v = this.values.sortBy(Prototype.K);
165 range = range || 0;
166 return $R(v[range],v[range+1]);
167 },
168 minimumOffset: function(){
169 return(this.isVertical() ? this.alignY : this.alignX);
170 },
171 maximumOffset: function(){
172 return(this.isVertical() ?
173 (this.track.offsetHeight != 0 ? this.track.offsetHeight :
174 this.track.style.height.replace(/px$/,"")) - this.alignY :
175 (this.track.offsetWidth != 0 ? this.track.offsetWidth :
176 this.track.style.width.replace(/px$/,"")) - this.alignY);
177 },
178 isVertical: function(){
179 return (this.axis == 'vertical');
180 },
181 drawSpans: function() {
182 var slider = this;
183 if(this.spans)
184 $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
185 if(this.options.startSpan)
186 this.setSpan(this.options.startSpan,
187 $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
188 if(this.options.endSpan)
189 this.setSpan(this.options.endSpan,
190 $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
191 },
192 setSpan: function(span, range) {
193 if(this.isVertical()) {
194 span.style.top = this.translateToPx(range.start);
195 span.style.height = this.translateToPx(range.end - range.start + this.range.start);
196 } else {
197 span.style.left = this.translateToPx(range.start);
198 span.style.width = this.translateToPx(range.end - range.start + this.range.start);
199 }
200 },
201 updateStyles: function() {
202 this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
203 Element.addClassName(this.activeHandle, 'selected');
204 },
205 startDrag: function(event) {
206 if(Event.isLeftClick(event)) {
207 if(!this.disabled){
208 this.active = true;
209
210 var handle = Event.element(event);
211 var pointer = [Event.pointerX(event), Event.pointerY(event)];
212 var track = handle;
213 if(track==this.track) {
214 var offsets = Position.cumulativeOffset(this.track);
215 this.event = event;
216 this.setValue(this.translateToValue(
217 (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
218 ));
219 var offsets = Position.cumulativeOffset(this.activeHandle);
220 this.offsetX = (pointer[0] - offsets[0]);
221 this.offsetY = (pointer[1] - offsets[1]);
222 } else {
223 // find the handle (prevents issues with Safari)
224 while((this.handles.indexOf(handle) == -1) && handle.parentNode)
225 handle = handle.parentNode;
226
227 if(this.handles.indexOf(handle)!=-1) {
228 this.activeHandle = handle;
229 this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
230 this.updateStyles();
231
232 var offsets = Position.cumulativeOffset(this.activeHandle);
233 this.offsetX = (pointer[0] - offsets[0]);
234 this.offsetY = (pointer[1] - offsets[1]);
235 }
236 }
237 }
238 Event.stop(event);
239 }
240 },
241 update: function(event) {
242 if(this.active) {
243 if(!this.dragging) this.dragging = true;
244 this.draw(event);
245 if(Prototype.Browser.WebKit) window.scrollBy(0,0);
246 Event.stop(event);
247 }
248 },
249 draw: function(event) {
250 var pointer = [Event.pointerX(event), Event.pointerY(event)];
251 var offsets = Position.cumulativeOffset(this.track);
252 pointer[0] -= this.offsetX + offsets[0];
253 pointer[1] -= this.offsetY + offsets[1];
254 this.event = event;
255 this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
256 if(this.initialized && this.options.onSlide)
257 this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
258 },
259 endDrag: function(event) {
260 if(this.active && this.dragging) {
261 this.finishDrag(event, true);
262 Event.stop(event);
263 }
264 this.active = false;
265 this.dragging = false;
266 },
267 finishDrag: function(event, success) {
268 this.active = false;
269 this.dragging = false;
270 this.updateFinished();
271 },
272 updateFinished: function() {
273 if(this.initialized && this.options.onChange)
274 this.options.onChange(this.values.length>1 ? this.values : this.value, this);
275 this.event = null;
276 }
277} \ No newline at end of file
diff --git a/docroot/lib/scriptaculous/sound.js b/docroot/lib/scriptaculous/sound.js
new file mode 100755
index 0000000..164c79a
--- /dev/null
+++ b/docroot/lib/scriptaculous/sound.js
@@ -0,0 +1,60 @@
1// script.aculo.us sound.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007
2
3// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4//
5// Based on code created by Jules Gravinese (http://www.webveteran.com/)
6//
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/
9
10Sound = {
11 tracks: {},
12 _enabled: true,
13 template:
14 new Template('<embed style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>'),
15 enable: function(){
16 Sound._enabled = true;
17 },
18 disable: function(){
19 Sound._enabled = false;
20 },
21 play: function(url){
22 if(!Sound._enabled) return;
23 var options = Object.extend({
24 track: 'global', url: url, replace: false
25 }, arguments[1] || {});
26
27 if(options.replace && this.tracks[options.track]) {
28 $R(0, this.tracks[options.track].id).each(function(id){
29 var sound = $('sound_'+options.track+'_'+id);
30 sound.Stop && sound.Stop();
31 sound.remove();
32 })
33 this.tracks[options.track] = null;
34 }
35
36 if(!this.tracks[options.track])
37 this.tracks[options.track] = { id: 0 }
38 else
39 this.tracks[options.track].id++;
40
41 options.id = this.tracks[options.track].id;
42 if (Prototype.Browser.IE) {
43 var sound = document.createElement('bgsound');
44 sound.setAttribute('id','sound_'+options.track+'_'+options.id);
45 sound.setAttribute('src',options.url);
46 sound.setAttribute('loop','1');
47 sound.setAttribute('autostart','true');
48 $$('body')[0].appendChild(sound);
49 }
50 else
51 new Insertion.Bottom($$('body')[0], Sound.template.evaluate(options));
52 }
53};
54
55if(Prototype.Browser.Gecko && navigator.userAgent.indexOf("Win") > 0){
56 if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 }))
57 Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>')
58 else
59 Sound.play = function(){}
60}
diff --git a/docroot/lib/scriptaculous/unittest.js b/docroot/lib/scriptaculous/unittest.js
new file mode 100755
index 0000000..d2dff8b
--- /dev/null
+++ b/docroot/lib/scriptaculous/unittest.js
@@ -0,0 +1,564 @@
1// script.aculo.us unittest.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007
2
3// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
5// (c) 2005-2007 Michael Schuerig (http://www.schuerig.de/michael/)
6//
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/
9
10// experimental, Firefox-only
11Event.simulateMouse = function(element, eventName) {
12 var options = Object.extend({
13 pointerX: 0,
14 pointerY: 0,
15 buttons: 0,
16 ctrlKey: false,
17 altKey: false,
18 shiftKey: false,
19 metaKey: false
20 }, arguments[2] || {});
21 var oEvent = document.createEvent("MouseEvents");
22 oEvent.initMouseEvent(eventName, true, true, document.defaultView,
23 options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
24 options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, $(element));
25
26 if(this.mark) Element.remove(this.mark);
27 this.mark = document.createElement('div');
28 this.mark.appendChild(document.createTextNode(" "));
29 document.body.appendChild(this.mark);
30 this.mark.style.position = 'absolute';
31 this.mark.style.top = options.pointerY + "px";
32 this.mark.style.left = options.pointerX + "px";
33 this.mark.style.width = "5px";
34 this.mark.style.height = "5px;";
35 this.mark.style.borderTop = "1px solid red;"
36 this.mark.style.borderLeft = "1px solid red;"
37
38 if(this.step)
39 alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
40
41 $(element).dispatchEvent(oEvent);
42};
43
44// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
45// You need to downgrade to 1.0.4 for now to get this working
46// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
47Event.simulateKey = function(element, eventName) {
48 var options = Object.extend({
49 ctrlKey: false,
50 altKey: false,
51 shiftKey: false,
52 metaKey: false,
53 keyCode: 0,
54 charCode: 0
55 }, arguments[2] || {});
56
57 var oEvent = document.createEvent("KeyEvents");
58 oEvent.initKeyEvent(eventName, true, true, window,
59 options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
60 options.keyCode, options.charCode );
61 $(element).dispatchEvent(oEvent);
62};
63
64Event.simulateKeys = function(element, command) {
65 for(var i=0; i<command.length; i++) {
66 Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
67 }
68};
69
70var Test = {}
71Test.Unit = {};
72
73// security exception workaround
74Test.Unit.inspect = Object.inspect;
75
76Test.Unit.Logger = Class.create();
77Test.Unit.Logger.prototype = {
78 initialize: function(log) {
79 this.log = $(log);
80 if (this.log) {
81 this._createLogTable();
82 }
83 },
84 start: function(testName) {
85 if (!this.log) return;
86 this.testName = testName;
87 this.lastLogLine = document.createElement('tr');
88 this.statusCell = document.createElement('td');
89 this.nameCell = document.createElement('td');
90 this.nameCell.className = "nameCell";
91 this.nameCell.appendChild(document.createTextNode(testName));
92 this.messageCell = document.createElement('td');
93 this.lastLogLine.appendChild(this.statusCell);
94 this.lastLogLine.appendChild(this.nameCell);
95 this.lastLogLine.appendChild(this.messageCell);
96 this.loglines.appendChild(this.lastLogLine);
97 },
98 finish: function(status, summary) {
99 if (!this.log) return;
100 this.lastLogLine.className = status;
101 this.statusCell.innerHTML = status;
102 this.messageCell.innerHTML = this._toHTML(summary);
103 this.addLinksToResults();
104 },
105 message: function(message) {
106 if (!this.log) return;
107 this.messageCell.innerHTML = this._toHTML(message);
108 },
109 summary: function(summary) {
110 if (!this.log) return;
111 this.logsummary.innerHTML = this._toHTML(summary);
112 },
113 _createLogTable: function() {
114 this.log.innerHTML =
115 '<div id="logsummary"></div>' +
116 '<table id="logtable">' +
117 '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
118 '<tbody id="loglines"></tbody>' +
119 '</table>';
120 this.logsummary = $('logsummary')
121 this.loglines = $('loglines');
122 },
123 _toHTML: function(txt) {
124 return txt.escapeHTML().replace(/\n/g,"<br/>");
125 },
126 addLinksToResults: function(){
127 $$("tr.failed .nameCell").each( function(td){ // todo: limit to children of this.log
128 td.title = "Run only this test"
129 Event.observe(td, 'click', function(){ window.location.search = "?tests=" + td.innerHTML;});
130 });
131 $$("tr.passed .nameCell").each( function(td){ // todo: limit to children of this.log
132 td.title = "Run all tests"
133 Event.observe(td, 'click', function(){ window.location.search = "";});
134 });
135 }
136}
137
138Test.Unit.Runner = Class.create();
139Test.Unit.Runner.prototype = {
140 initialize: function(testcases) {
141 this.options = Object.extend({
142 testLog: 'testlog'
143 }, arguments[1] || {});
144 this.options.resultsURL = this.parseResultsURLQueryParameter();
145 this.options.tests = this.parseTestsQueryParameter();
146 if (this.options.testLog) {
147 this.options.testLog = $(this.options.testLog) || null;
148 }
149 if(this.options.tests) {
150 this.tests = [];
151 for(var i = 0; i < this.options.tests.length; i++) {
152 if(/^test/.test(this.options.tests[i])) {
153 this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
154 }
155 }
156 } else {
157 if (this.options.test) {
158 this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
159 } else {
160 this.tests = [];
161 for(var testcase in testcases) {
162 if(/^test/.test(testcase)) {
163 this.tests.push(
164 new Test.Unit.Testcase(
165 this.options.context ? ' -> ' + this.options.titles[testcase] : testcase,
166 testcases[testcase], testcases["setup"], testcases["teardown"]
167 ));
168 }
169 }
170 }
171 }
172 this.currentTest = 0;
173 this.logger = new Test.Unit.Logger(this.options.testLog);
174 setTimeout(this.runTests.bind(this), 1000);
175 },
176 parseResultsURLQueryParameter: function() {
177 return window.location.search.parseQuery()["resultsURL"];
178 },
179 parseTestsQueryParameter: function(){
180 if (window.location.search.parseQuery()["tests"]){
181 return window.location.search.parseQuery()["tests"].split(',');
182 };
183 },
184 // Returns:
185 // "ERROR" if there was an error,
186 // "FAILURE" if there was a failure, or
187 // "SUCCESS" if there was neither
188 getResult: function() {
189 var hasFailure = false;
190 for(var i=0;i<this.tests.length;i++) {
191 if (this.tests[i].errors > 0) {
192 return "ERROR";
193 }
194 if (this.tests[i].failures > 0) {
195 hasFailure = true;
196 }
197 }
198 if (hasFailure) {
199 return "FAILURE";
200 } else {
201 return "SUCCESS";
202 }
203 },
204 postResults: function() {
205 if (this.options.resultsURL) {
206 new Ajax.Request(this.options.resultsURL,
207 { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
208 }
209 },
210 runTests: function() {
211 var test = this.tests[this.currentTest];
212 if (!test) {
213 // finished!
214 this.postResults();
215 this.logger.summary(this.summary());
216 return;
217 }
218 if(!test.isWaiting) {
219 this.logger.start(test.name);
220 }
221 test.run();
222 if(test.isWaiting) {
223 this.logger.message("Waiting for " + test.timeToWait + "ms");
224 setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
225 } else {
226 this.logger.finish(test.status(), test.summary());
227 this.currentTest++;
228 // tail recursive, hopefully the browser will skip the stackframe
229 this.runTests();
230 }
231 },
232 summary: function() {
233 var assertions = 0;
234 var failures = 0;
235 var errors = 0;
236 var messages = [];
237 for(var i=0;i<this.tests.length;i++) {
238 assertions += this.tests[i].assertions;
239 failures += this.tests[i].failures;
240 errors += this.tests[i].errors;
241 }
242 return (
243 (this.options.context ? this.options.context + ': ': '') +
244 this.tests.length + " tests, " +
245 assertions + " assertions, " +
246 failures + " failures, " +
247 errors + " errors");
248 }
249}
250
251Test.Unit.Assertions = Class.create();
252Test.Unit.Assertions.prototype = {
253 initialize: function() {
254 this.assertions = 0;
255 this.failures = 0;
256 this.errors = 0;
257 this.messages = [];
258 },
259 summary: function() {
260 return (
261 this.assertions + " assertions, " +
262 this.failures + " failures, " +
263 this.errors + " errors" + "\n" +
264 this.messages.join("\n"));
265 },
266 pass: function() {
267 this.assertions++;
268 },
269 fail: function(message) {
270 this.failures++;
271 this.messages.push("Failure: " + message);
272 },
273 info: function(message) {
274 this.messages.push("Info: " + message);
275 },
276 error: function(error) {
277 this.errors++;
278 this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
279 },
280 status: function() {
281 if (this.failures > 0) return 'failed';
282 if (this.errors > 0) return 'error';
283 return 'passed';
284 },
285 assert: function(expression) {
286 var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
287 try { expression ? this.pass() :
288 this.fail(message); }
289 catch(e) { this.error(e); }
290 },
291 assertEqual: function(expected, actual) {
292 var message = arguments[2] || "assertEqual";
293 try { (expected == actual) ? this.pass() :
294 this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
295 '", actual "' + Test.Unit.inspect(actual) + '"'); }
296 catch(e) { this.error(e); }
297 },
298 assertInspect: function(expected, actual) {
299 var message = arguments[2] || "assertInspect";
300 try { (expected == actual.inspect()) ? this.pass() :
301 this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
302 '", actual "' + Test.Unit.inspect(actual) + '"'); }
303 catch(e) { this.error(e); }
304 },
305 assertEnumEqual: function(expected, actual) {
306 var message = arguments[2] || "assertEnumEqual";
307 try { $A(expected).length == $A(actual).length &&
308 expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
309 this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) +
310 ', actual ' + Test.Unit.inspect(actual)); }
311 catch(e) { this.error(e); }
312 },
313 assertNotEqual: function(expected, actual) {
314 var message = arguments[2] || "assertNotEqual";
315 try { (expected != actual) ? this.pass() :
316 this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
317 catch(e) { this.error(e); }
318 },
319 assertIdentical: function(expected, actual) {
320 var message = arguments[2] || "assertIdentical";
321 try { (expected === actual) ? this.pass() :
322 this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
323 '", actual "' + Test.Unit.inspect(actual) + '"'); }
324 catch(e) { this.error(e); }
325 },
326 assertNotIdentical: function(expected, actual) {
327 var message = arguments[2] || "assertNotIdentical";
328 try { !(expected === actual) ? this.pass() :
329 this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
330 '", actual "' + Test.Unit.inspect(actual) + '"'); }
331 catch(e) { this.error(e); }
332 },
333 assertNull: function(obj) {
334 var message = arguments[1] || 'assertNull'
335 try { (obj==null) ? this.pass() :
336 this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
337 catch(e) { this.error(e); }
338 },
339 assertMatch: function(expected, actual) {
340 var message = arguments[2] || 'assertMatch';
341 var regex = new RegExp(expected);
342 try { (regex.exec(actual)) ? this.pass() :
343 this.fail(message + ' : regex: "' + Test.Unit.inspect(expected) + ' did not match: ' + Test.Unit.inspect(actual) + '"'); }
344 catch(e) { this.error(e); }
345 },
346 assertHidden: function(element) {
347 var message = arguments[1] || 'assertHidden';
348 this.assertEqual("none", element.style.display, message);
349 },
350 assertNotNull: function(object) {
351 var message = arguments[1] || 'assertNotNull';
352 this.assert(object != null, message);
353 },
354 assertType: function(expected, actual) {
355 var message = arguments[2] || 'assertType';
356 try {
357 (actual.constructor == expected) ? this.pass() :
358 this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
359 '", actual "' + (actual.constructor) + '"'); }
360 catch(e) { this.error(e); }
361 },
362 assertNotOfType: function(expected, actual) {
363 var message = arguments[2] || 'assertNotOfType';
364 try {
365 (actual.constructor != expected) ? this.pass() :
366 this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
367 '", actual "' + (actual.constructor) + '"'); }
368 catch(e) { this.error(e); }
369 },
370 assertInstanceOf: function(expected, actual) {
371 var message = arguments[2] || 'assertInstanceOf';
372 try {
373 (actual instanceof expected) ? this.pass() :
374 this.fail(message + ": object was not an instance of the expected type"); }
375 catch(e) { this.error(e); }
376 },
377 assertNotInstanceOf: function(expected, actual) {
378 var message = arguments[2] || 'assertNotInstanceOf';
379 try {
380 !(actual instanceof expected) ? this.pass() :
381 this.fail(message + ": object was an instance of the not expected type"); }
382 catch(e) { this.error(e); }
383 },
384 assertRespondsTo: function(method, obj) {
385 var message = arguments[2] || 'assertRespondsTo';
386 try {
387 (obj[method] && typeof obj[method] == 'function') ? this.pass() :
388 this.fail(message + ": object doesn't respond to [" + method + "]"); }
389 catch(e) { this.error(e); }
390 },
391 assertReturnsTrue: function(method, obj) {
392 var message = arguments[2] || 'assertReturnsTrue';
393 try {
394 var m = obj[method];
395 if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
396 m() ? this.pass() :
397 this.fail(message + ": method returned false"); }
398 catch(e) { this.error(e); }
399 },
400 assertReturnsFalse: function(method, obj) {
401 var message = arguments[2] || 'assertReturnsFalse';
402 try {
403 var m = obj[method];
404 if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
405 !m() ? this.pass() :
406 this.fail(message + ": method returned true"); }
407 catch(e) { this.error(e); }
408 },
409 assertRaise: function(exceptionName, method) {
410 var message = arguments[2] || 'assertRaise';
411 try {
412 method();
413 this.fail(message + ": exception expected but none was raised"); }
414 catch(e) {
415 ((exceptionName == null) || (e.name==exceptionName)) ? this.pass() : this.error(e);
416 }
417 },
418 assertElementsMatch: function() {
419 var expressions = $A(arguments), elements = $A(expressions.shift());
420 if (elements.length != expressions.length) {
421 this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions');
422 return false;
423 }
424 elements.zip(expressions).all(function(pair, index) {
425 var element = $(pair.first()), expression = pair.last();
426 if (element.match(expression)) return true;
427 this.fail('assertElementsMatch: (in index ' + index + ') expected ' + expression.inspect() + ' but got ' + element.inspect());
428 }.bind(this)) && this.pass();
429 },
430 assertElementMatches: function(element, expression) {
431 this.assertElementsMatch([element], expression);
432 },
433 benchmark: function(operation, iterations) {
434 var startAt = new Date();
435 (iterations || 1).times(operation);
436 var timeTaken = ((new Date())-startAt);
437 this.info((arguments[2] || 'Operation') + ' finished ' +
438 iterations + ' iterations in ' + (timeTaken/1000)+'s' );
439 return timeTaken;
440 },
441 _isVisible: function(element) {
442 element = $(element);
443 if(!element.parentNode) return true;
444 this.assertNotNull(element);
445 if(element.style && Element.getStyle(element, 'display') == 'none')
446 return false;
447
448 return this._isVisible(element.parentNode);
449 },
450 assertNotVisible: function(element) {
451 this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
452 },
453 assertVisible: function(element) {
454 this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
455 },
456 benchmark: function(operation, iterations) {
457 var startAt = new Date();
458 (iterations || 1).times(operation);
459 var timeTaken = ((new Date())-startAt);
460 this.info((arguments[2] || 'Operation') + ' finished ' +
461 iterations + ' iterations in ' + (timeTaken/1000)+'s' );
462 return timeTaken;
463 }
464}
465
466Test.Unit.Testcase = Class.create();
467Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
468 initialize: function(name, test, setup, teardown) {
469 Test.Unit.Assertions.prototype.initialize.bind(this)();
470 this.name = name;
471
472 if(typeof test == 'string') {
473 test = test.gsub(/(\.should[^\(]+\()/,'#{0}this,');
474 test = test.gsub(/(\.should[^\(]+)\(this,\)/,'#{1}(this)');
475 this.test = function() {
476 eval('with(this){'+test+'}');
477 }
478 } else {
479 this.test = test || function() {};
480 }
481
482 this.setup = setup || function() {};
483 this.teardown = teardown || function() {};
484 this.isWaiting = false;
485 this.timeToWait = 1000;
486 },
487 wait: function(time, nextPart) {
488 this.isWaiting = true;
489 this.test = nextPart;
490 this.timeToWait = time;
491 },
492 run: function() {
493 try {
494 try {
495 if (!this.isWaiting) this.setup.bind(this)();
496 this.isWaiting = false;
497 this.test.bind(this)();
498 } finally {
499 if(!this.isWaiting) {
500 this.teardown.bind(this)();
501 }
502 }
503 }
504 catch(e) { this.error(e); }
505 }
506});
507
508// *EXPERIMENTAL* BDD-style testing to please non-technical folk
509// This draws many ideas from RSpec http://rspec.rubyforge.org/
510
511Test.setupBDDExtensionMethods = function(){
512 var METHODMAP = {
513 shouldEqual: 'assertEqual',
514 shouldNotEqual: 'assertNotEqual',
515 shouldEqualEnum: 'assertEnumEqual',
516 shouldBeA: 'assertType',
517 shouldNotBeA: 'assertNotOfType',
518 shouldBeAn: 'assertType',
519 shouldNotBeAn: 'assertNotOfType',
520 shouldBeNull: 'assertNull',
521 shouldNotBeNull: 'assertNotNull',
522
523 shouldBe: 'assertReturnsTrue',
524 shouldNotBe: 'assertReturnsFalse',
525 shouldRespondTo: 'assertRespondsTo'
526 };
527 Test.BDDMethods = {};
528 for(m in METHODMAP) {
529 Test.BDDMethods[m] = eval(
530 'function(){'+
531 'var args = $A(arguments);'+
532 'var scope = args.shift();'+
533 'scope.'+METHODMAP[m]+'.apply(scope,(args || []).concat([this])); }');
534 }
535 [Array.prototype, String.prototype, Number.prototype].each(
536 function(p){ Object.extend(p, Test.BDDMethods) }
537 );
538}
539
540Test.context = function(name, spec, log){
541 Test.setupBDDExtensionMethods();
542
543 var compiledSpec = {};
544 var titles = {};
545 for(specName in spec) {
546 switch(specName){
547 case "setup":
548 case "teardown":
549 compiledSpec[specName] = spec[specName];
550 break;
551 default:
552 var testName = 'test'+specName.gsub(/\s+/,'-').camelize();
553 var body = spec[specName].toString().split('\n').slice(1);
554 if(/^\{/.test(body[0])) body = body.slice(1);
555 body.pop();
556 body = body.map(function(statement){
557 return statement.strip()
558 });
559 compiledSpec[testName] = body.join('\n');
560 titles[testName] = specName;
561 }
562 }
563 new Test.Unit.Runner(compiledSpec, { titles: titles, testLog: log || 'testlog', context: name });
564}; \ No newline at end of file
diff --git a/docroot/lib/swfobject/swfobject.js b/docroot/lib/swfobject/swfobject.js
new file mode 100755
index 0000000..caa256a
--- /dev/null
+++ b/docroot/lib/swfobject/swfobject.js
@@ -0,0 +1,233 @@
1/**
2 * SWFObject v1.5: Flash Player detection and embed - http://blog.deconcept.com/swfobject/
3 *
4 * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License:
5 * http://www.opensource.org/licenses/mit-license.php
6 *
7 */
8if(typeof deconcept == "undefined") var deconcept = new Object();
9if(typeof deconcept.util == "undefined") deconcept.util = new Object();
10if(typeof deconcept.SWFObjectUtil == "undefined") deconcept.SWFObjectUtil = new Object();
11deconcept.SWFObject = function(swf, id, w, h, ver, c, quality, xiRedirectUrl, redirectUrl, detectKey) {
12 if (!document.getElementById) { return; }
13 this.DETECT_KEY = detectKey ? detectKey : 'detectflash';
14 this.skipDetect = deconcept.util.getRequestParameter(this.DETECT_KEY);
15 this.params = new Object();
16 this.variables = new Object();
17 this.attributes = new Array();
18 if(swf) { this.setAttribute('swf', swf); }
19 if(id) { this.setAttribute('id', id); }
20 if(w) { this.setAttribute('width', w); }
21 if(h) { this.setAttribute('height', h); }
22 if(ver) { this.setAttribute('version', new deconcept.PlayerVersion(ver.toString().split("."))); }
23 this.installedVer = deconcept.SWFObjectUtil.getPlayerVersion();
24 if (!window.opera && document.all && this.installedVer.major > 7) {
25 // only add the onunload cleanup if the Flash Player version supports External Interface and we are in IE
26 deconcept.SWFObject.doPrepUnload = true;
27 }
28 if(c) { this.addParam('bgcolor', c); }
29 var q = quality ? quality : 'high';
30 this.addParam('quality', q);
31 this.setAttribute('useExpressInstall', false);
32 this.setAttribute('doExpressInstall', false);
33 var xir = (xiRedirectUrl) ? xiRedirectUrl : window.location;
34 this.setAttribute('xiRedirectUrl', xir);
35 this.setAttribute('redirectUrl', '');
36 if(redirectUrl) { this.setAttribute('redirectUrl', redirectUrl); }
37}
38deconcept.SWFObject.prototype = {
39 useExpressInstall: function(path) {
40 this.xiSWFPath = !path ? "expressinstall.swf" : path;
41 this.setAttribute('useExpressInstall', true);
42 },
43 setAttribute: function(name, value){
44 this.attributes[name] = value;
45 },
46 getAttribute: function(name){
47 return this.attributes[name];
48 },
49 addParam: function(name, value){
50 this.params[name] = value;
51 },
52 getParams: function(){
53 return this.params;
54 },
55 addVariable: function(name, value){
56 this.variables[name] = value;
57 },
58 getVariable: function(name){
59 return this.variables[name];
60 },
61 getVariables: function(){
62 return this.variables;
63 },
64 getVariablePairs: function(){
65 var variablePairs = new Array();
66 var key;
67 var variables = this.getVariables();
68 for(key in variables){
69 variablePairs[variablePairs.length] = key +"="+ variables[key];
70 }
71 return variablePairs;
72 },
73 getSWFHTML: function() {
74 var swfNode = "";
75 if (navigator.plugins && navigator.mimeTypes && navigator.mimeTypes.length) { // netscape plugin architecture
76 if (this.getAttribute("doExpressInstall")) {
77 this.addVariable("MMplayerType", "PlugIn");
78 this.setAttribute('swf', this.xiSWFPath);
79 }
80 swfNode = '<embed type="application/x-shockwave-flash" src="'+ this.getAttribute('swf') +'" width="'+ this.getAttribute('width') +'" height="'+ this.getAttribute('height') +'" style="'+ this.getAttribute('style') +'"';
81 swfNode += ' id="'+ this.getAttribute('id') +'" name="'+ this.getAttribute('id') +'" ';
82 var params = this.getParams();
83 for(var key in params){ swfNode += [key] +'="'+ params[key] +'" '; }
84 var pairs = this.getVariablePairs().join("&");
85 if (pairs.length > 0){ swfNode += 'flashvars="'+ pairs +'"'; }
86 swfNode += '/>';
87 } else { // PC IE
88 if (this.getAttribute("doExpressInstall")) {
89 this.addVariable("MMplayerType", "ActiveX");
90 this.setAttribute('swf', this.xiSWFPath);
91 }
92 swfNode = '<object id="'+ this.getAttribute('id') +'" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+ this.getAttribute('width') +'" height="'+ this.getAttribute('height') +'" style="'+ this.getAttribute('style') +'">';
93 swfNode += '<param name="movie" value="'+ this.getAttribute('swf') +'" />';
94 var params = this.getParams();
95 for(var key in params) {
96 swfNode += '<param name="'+ key +'" value="'+ params[key] +'" />';
97 }
98 var pairs = this.getVariablePairs().join("&");
99 if(pairs.length > 0) {swfNode += '<param name="flashvars" value="'+ pairs +'" />';}
100 swfNode += "</object>";
101 }
102 return swfNode;
103 },
104 write: function(elementId){
105 if(this.getAttribute('useExpressInstall')) {
106 // check to see if we need to do an express install
107 var expressInstallReqVer = new deconcept.PlayerVersion([6,0,65]);
108 if (this.installedVer.versionIsValid(expressInstallReqVer) && !this.installedVer.versionIsValid(this.getAttribute('version'))) {
109 this.setAttribute('doExpressInstall', true);
110 this.addVariable("MMredirectURL", escape(this.getAttribute('xiRedirectUrl')));
111 document.title = document.title.slice(0, 47) + " - Flash Player Installation";
112 this.addVariable("MMdoctitle", document.title);
113 }
114 }
115 if(this.skipDetect || this.getAttribute('doExpressInstall') || this.installedVer.versionIsValid(this.getAttribute('version'))){
116 var n = (typeof elementId == 'string') ? document.getElementById(elementId) : elementId;
117 n.innerHTML = this.getSWFHTML();
118 return true;
119 }else{
120 if(this.getAttribute('redirectUrl') != "") {
121 document.location.replace(this.getAttribute('redirectUrl'));
122 }
123 }
124 return false;
125 }
126}
127
128/* ---- detection functions ---- */
129deconcept.SWFObjectUtil.getPlayerVersion = function(){
130 var PlayerVersion = new deconcept.PlayerVersion([0,0,0]);
131 if(navigator.plugins && navigator.mimeTypes.length){
132 var x = navigator.plugins["Shockwave Flash"];
133 if(x && x.description) {
134 PlayerVersion = new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/, "").replace(/(\s+r|\s+b[0-9]+)/, ".").split("."));
135 }
136 }else if (navigator.userAgent && navigator.userAgent.indexOf("Windows CE") >= 0){ // if Windows CE
137 var axo = 1;
138 var counter = 3;
139 while(axo) {
140 try {
141 counter++;
142 axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+ counter);
143// document.write("player v: "+ counter);
144 PlayerVersion = new deconcept.PlayerVersion([counter,0,0]);
145 } catch (e) {
146 axo = null;
147 }
148 }
149 } else { // Win IE (non mobile)
150 // do minor version lookup in IE, but avoid fp6 crashing issues
151 // see http://blog.deconcept.com/2006/01/11/getvariable-setvariable-crash-internet-explorer-flash-6/
152 try{
153 var axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
154 }catch(e){
155 try {
156 var axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
157 PlayerVersion = new deconcept.PlayerVersion([6,0,21]);
158 axo.AllowScriptAccess = "always"; // error if player version < 6.0.47 (thanks to Michael Williams @ Adobe for this code)
159 } catch(e) {
160 if (PlayerVersion.major == 6) {
161 return PlayerVersion;
162 }
163 }
164 try {
165 axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
166 } catch(e) {}
167 }
168 if (axo != null) {
169 PlayerVersion = new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));
170 }
171 }
172 return PlayerVersion;
173}
174deconcept.PlayerVersion = function(arrVersion){
175 this.major = arrVersion[0] != null ? parseInt(arrVersion[0]) : 0;
176 this.minor = arrVersion[1] != null ? parseInt(arrVersion[1]) : 0;
177 this.rev = arrVersion[2] != null ? parseInt(arrVersion[2]) : 0;
178}
179deconcept.PlayerVersion.prototype.versionIsValid = function(fv){
180 if(this.major < fv.major) return false;
181 if(this.major > fv.major) return true;
182 if(this.minor < fv.minor) return false;
183 if(this.minor > fv.minor) return true;
184 if(this.rev < fv.rev) return false;
185 return true;
186}
187/* ---- get value of query string param ---- */
188deconcept.util = {
189 getRequestParameter: function(param) {
190 var q = document.location.search || document.location.hash;
191 if (param == null) { return q; }
192 if(q) {
193 var pairs = q.substring(1).split("&");
194 for (var i=0; i < pairs.length; i++) {
195 if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
196 return pairs[i].substring((pairs[i].indexOf("=")+1));
197 }
198 }
199 }
200 return "";
201 }
202}
203/* fix for video streaming bug */
204deconcept.SWFObjectUtil.cleanupSWFs = function() {
205 var objects = document.getElementsByTagName("OBJECT");
206 for (var i = objects.length - 1; i >= 0; i--) {
207 objects[i].style.display = 'none';
208 for (var x in objects[i]) {
209 if (typeof objects[i][x] == 'function') {
210 objects[i][x] = function(){};
211 }
212 }
213 }
214}
215// fixes bug in some fp9 versions see http://blog.deconcept.com/2006/07/28/swfobject-143-released/
216if (deconcept.SWFObject.doPrepUnload) {
217 if (!deconcept.unloadSet) {
218 deconcept.SWFObjectUtil.prepUnload = function() {
219 __flash_unloadHandler = function(){};
220 __flash_savedUnloadHandler = function(){};
221 window.attachEvent("onunload", deconcept.SWFObjectUtil.cleanupSWFs);
222 }
223 window.attachEvent("onbeforeunload", deconcept.SWFObjectUtil.prepUnload);
224 deconcept.unloadSet = true;
225 }
226}
227/* add document.getElementById if needed (mobile IE < 5) */
228if (!document.getElementById && document.all) { document.getElementById = function(id) { return document.all[id]; }}
229
230/* add some aliases for ease of use/backwards compatibility */
231var getQueryParamValue = deconcept.util.getRequestParameter;
232var FlashObject = deconcept.SWFObject; // for legacy support
233var SWFObject = deconcept.SWFObject;