docs/main/GettingStarted.rst | 9
docs/main/MindMap.rst | 12
docs/main/Widgets.rst | 1
moksha/widgets/mindmap/__init__.py | 1
moksha/widgets/mindmap/mindmap.py | 75
moksha/widgets/mindmap/static/css/js-mindmap.css | 42
moksha/widgets/mindmap/static/js/js-mindmap.js | 362 +
moksha/widgets/mindmap/static/js/lib/excanvas-compressed.js | 19
moksha/widgets/mindmap/static/js/lib/excanvas.js | 785 ++
moksha/widgets/mindmap/static/js/lib/mootools-1.2-core.js | 3816 ++++++++++++
moksha/widgets/mindmap/static/js/lib/mootools-1.2-more.js | 1491 ++++
11 files changed, 6609 insertions(+), 4 deletions(-)
New commits:
commit 67e3d389f11a47bf741ab8fe4567478ec719bc0b
Author: Luke Macken <lmacken(a)redhat.com>
Date: Wed Jul 1 01:21:22 2009 -0400
Create a slick little Mind Map widget :)
diff --git a/docs/main/MindMap.rst b/docs/main/MindMap.rst
new file mode 100644
index 0000000..c124abe
--- /dev/null
+++ b/docs/main/MindMap.rst
@@ -0,0 +1,12 @@
+Mind Maps
+=========
+
+Creating mind maps in Moksha
+----------------------------
+
+.. code-block:: python
+
+ from moksha.widgets.mindmap import MindMap
+
+.. automodule:: moksha.widgets.mindmap.mindmap
+
diff --git a/docs/main/Widgets.rst b/docs/main/Widgets.rst
index b7232d0..7bb455f 100644
--- a/docs/main/Widgets.rst
+++ b/docs/main/Widgets.rst
@@ -19,3 +19,4 @@ Moksha also provides a number of Widgets in its API, some of which
include:
Feeds
LiveWidget
MokshaContainerWidgets
+ MindMap
diff --git a/moksha/widgets/mindmap/__init__.py b/moksha/widgets/mindmap/__init__.py
new file mode 100644
index 0000000..58bf049
--- /dev/null
+++ b/moksha/widgets/mindmap/__init__.py
@@ -0,0 +1 @@
+from mindmap import *
diff --git a/moksha/widgets/mindmap/mindmap.py b/moksha/widgets/mindmap/mindmap.py
new file mode 100644
index 0000000..d7f78af
--- /dev/null
+++ b/moksha/widgets/mindmap/mindmap.py
@@ -0,0 +1,75 @@
+# This file is part of Moksha.
+#
+# Moksha is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Moksha is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Moksha. If not, see <
http://www.gnu.org/licenses/>.
+#
+# Copyright 2009, Moksha, Inc.
+
+"""
+:mod:`moksha.widgets.mindmap` -- A mind map widget
+==================================================
+
+.. widgetbrowser:: moksha.widgets.mindmap.MindMapDemo
+ :tabs: demo, source
+ :size: x-large
+
+.. moduleauthor:: Luke Macken <lmacken(a)redhat.com>
+"""
+
+from tw.api import Widget, JSLink, CSSLink
+from tw.jquery.flot import excanvas_js
+from tw.mootools.base import moo_core_js_compressed, moo_more_js_compressed
+from webhelpers.html import HTML
+
+class MindMap(Widget):
+ javascript = [JSLink(filename='static/js/js-mindmap.js', modname=__name__,
+ javascript=[moo_core_js_compressed,
+ moo_more_js_compressed,
+ excanvas_js],
+ location='bodybottom')]
+ css = [CSSLink(filename='static/css/js-mindmap.css', modname=__name__)]
+ template = """
+ <script>jQuery.noConflict();</script>
+ <div id="debug1"></div>
+ <canvas id="cv" width="900" height="600"
style="position:absolute;left:0;top:0;"></canvas>
+ <div id="js-mindmap">${root} ${tree}</div>
+ """
+ engine_name = 'mako'
+ params = ['data', 'root', 'tree']
+
+ def update_params(self, d):
+ super(MindMap, self).update_params(d)
+ d.tree = self.traverse_branch(d.data)
+
+ def traverse_branch(self, branch):
+ mindmap = "<ul>"
+ for value in branch:
+ if isinstance(value, (list, tuple)):
+ if len(value) != 2:
+ mindmap += self.traverse_branch(value)
+ continue
+ mindmap += '<li><a
href="%s">%s</a></li>' % (value[1], value[0])
+ mindmap += "</ul>"
+ return mindmap
+
+
+class MindMapDemo(MindMap):
+ id = 'MindMapDemo'
+ root = HTML.tag('a', href='http://moksha.fedorahosted.org',
c='Moksha')
+ data = [('Wiki', 'http://fedorahosted.org/moksha'),
+ ('Tickets', 'http://fedorahosted.org/moksha/tickets'),
+ ('Demo', 'http://bit.ly/RICf'),
+ ('Timeline', 'https://fedorahosted.org/moksha/timeline'),
+ ('Roadmap', 'https://fedorahosted.org/moksha/roadmap'),
+ ('Mailing List',
'https://fedorahosted.org/mailman/listinfo/moksha'),
+ ]
diff --git a/moksha/widgets/mindmap/static/css/js-mindmap.css
b/moksha/widgets/mindmap/static/css/js-mindmap.css
new file mode 100644
index 0000000..9e295ca
--- /dev/null
+++ b/moksha/widgets/mindmap/static/css/js-mindmap.css
@@ -0,0 +1,42 @@
+ body {
+ background:#ffffff;
+ }
+ #debug1 {
+ display:none;
+ }
+ .js-mindmap .node {
+ position:absolute;
+ top:0;
+ left:0;
+ font-family:verdana;
+ font-size:11px;
+ color:#003258;
+ background:#C1D1E0;
+ padding:15px;
+ -moz-border-radius:3px;
+ cursor:pointer;
+ cursor:hand;
+ z-index:100;
+ list-style:none;
+ }
+ .js-mindmap .node a{
+ color:#003258;
+ }
+ .js-mindmap .node.active{
+ background:#EAEFF5;
+ color:#003258;
+ font-weight:bold;
+ z-index:1000;
+ }
+ .js-mindmap .node.active a{
+ color:#003258;
+ }
+ .js-mindmap img.line {
+ position:absolute;
+ width:200px;
+ height:133px;
+ top:0;
+ left:0;
+ display:block;
+ z-index:0;
+ }
diff --git a/moksha/widgets/mindmap/static/js/js-mindmap.js
b/moksha/widgets/mindmap/static/js/js-mindmap.js
new file mode 100644
index 0000000..2281557
--- /dev/null
+++ b/moksha/widgets/mindmap/static/js/js-mindmap.js
@@ -0,0 +1,362 @@
+// js-mindmap
+// (c) Copyright Kenneth Kufluk 2008
+// Digitas London
+
+//requires MooTools
+//requires excanvas
+
+var timeperiod = 10;
+var damping = 0.55;
+var repulse = 2;
+var attract = 6;
+var wallrepulse = 0.2;
+var updateDisplayAfterNthIteration = 20;
+var activeNode = null;
+var mapArea = {x:900, y:600};
+var minSpeed = 0.05;
+var maxForce = 0.1;
+
+var showSublines = true;
+
+function SpiderNode(index, el, parent, active) {
+ this.el = el;
+ this.el.spiderObj = this;
+// this.originalPos = this.el.getPosition();
+ if (active) {
+ activeNode = this;
+ $(this.el).addClass('active');
+ }
+ this.parent = parent;
+ $(this.el).addClass('node');
+ this.index = index;
+ this.visible = true;
+ this.hasLayout = true;
+ this.x = Math.random()+(mapArea.x/2);
+ this.y = Math.random()+(mapArea.y/2);
+ this.el.style.left = this.x + "px";
+ this.el.style.top = this.y + "px";
+ this.dx = 0;
+ this.dy = 0;
+ this.count = 0;
+ var myDragInstance = new Drag(this.el,
+ {
+ snap: 1,
+ onSnap: function(el){
+ el.addClass('dragging');
+ },
+ onComplete: function(el){
+ el.removeClass('dragging');
+ }
+ }
+ );
+ if (this.el.childNodes[0].tagName=='A') {
+ this.el.href = this.el.childNodes[0].href;
+ }
+ this.el.onclick = function() {
+ if (activeNode) activeNode.el.removeClass('active');
+ activeNode = this.spiderObj;
+ activeNode.el.addClass('active');
+ return false;
+ }
+ this.el.ondblclick = function() {
+ location.href=this.href;
+ return false;
+ }
+}
+SpiderNode.prototype.normalizePosition = function() {
+ //move node to root (outside of parental positioning)
+ if (this.parent!=null) {
+ $$('#js-mindmap>ul')[1].appendChild(this.el);
+ }
+}
+// TODO WRITE METHOD
+SpiderNode.prototype.layOutChildren = function() {
+ //show my child nodes in an equally spaced group around myself, instead of placing them
randomly
+}
+SpiderNode.prototype.getForceVector = function() {
+ // for each item in nodes, calculate the force
+ // repulsive force is proportional to 1/distance
+ var fx = 0;
+ var fy = 0;
+ for (var i=0;i<nodes.length;i++) {
+ if (i==this.index) continue;
+ if ((showSublines && !nodes[i].hasLayout) || (!showSublines &&
!nodes[i].visible)) continue;
+ // Repulsive force (coulomb's law)
+ var x1 = (nodes[i].x - this.x);
+ var y1 = (nodes[i].y - this.y);
+// $('debug1').innerHTML = x1;
+ //adjust for variable node size
+ var nodewidths = ((nodes[i].el.getSize().x + this.el.getSize().x)/2)
+
+/*
+ if (x1<0) {
+ if (Math.abs(x1)<nodewidths) x1=0.01;
+ else x1 = x1 + nodewidths;
+ } else {
+ if (Math.abs(x1)<nodewidths) x1=0.01;
+ else x1 = x1 - nodewidths;
+ }
+*/
+
+ var xsign = x1/Math.abs(x1);
+ var ysign = y1/Math.abs(y1);
+ var dist = Math.sqrt((x1*x1) + (y1*y1));
+// x1 = x1 + (xsign*((nodes[i].el.getSize().x + this.el.getSize().x)/2));
+// y1 = y1 + (ysign*((nodes[i].el.getSize().y + this.el.getSize().y)/2));
+ var theta = Math.atan(y1/x1);
+ if (x1==0) {
+ theta = Math.PI/2;
+ xsign = 0;
+ }
+ // force is based on radial distance
+ var f = (repulse*500)/(dist*dist);
+ if (Math.abs(dist)<500) {
+ fx += -f * Math.cos(theta)*xsign;
+ fy += -f * Math.sin(theta)*xsign;
+ }
+ }
+ // add repulsive force of the "walls"
+ //left wall
+ var xdist = this.x+this.el.getSize().x;
+ var f = (wallrepulse*500)/(xdist*xdist);
+ fx += Math.min(2, f);
+ //right wall
+ var rightdist = (mapArea.x-xdist);
+ var f = -(wallrepulse*500)/(rightdist*rightdist);
+ fx += Math.max(-2, f);
+ //top wall
+ var f = (wallrepulse*500)/(this.y*this.y);
+ fy += Math.min(2, f);
+ //botttom wall
+ var bottomdist = (mapArea.y-this.y);
+ var f = -(wallrepulse*500)/(bottomdist*bottomdist);
+ fy += Math.max(-2, f);
+
+ // for each line, of which I'm a part, add an attractive force.
+ for (var i=0;i<lines.length;i++) {
+ var otherend = null;
+ if (lines[i].start.index == this.index) {
+ otherend = lines[i].end;
+ } else if (lines[i].end.index == this.index) {
+ otherend = lines[i].start;
+ } else continue;
+ // Attractive force (hooke's law)
+ var x1 = (otherend.x - this.x);
+ var y1 = (otherend.y - this.y);
+ var dist = Math.sqrt(x1*x1 + y1*y1);
+ var xsign = x1/Math.abs(x1);
+ var theta = Math.atan(y1/x1);
+ if (x1==0) {
+ theta = Math.PI/2;
+ xsign = 0;
+ }
+ // force is based on radial distance
+ var f = (attract*dist)/10000;
+ if (Math.abs(dist)>0) {
+ fx += f * Math.cos(theta)*xsign;
+ fy += f * Math.sin(theta)*xsign;
+ }
+
+// $('debug1').innerHTML = x1;
+ }
+
+ // if I'm active, attract me to the centre of the area
+ if (activeNode===this) {
+ // Attractive force (hooke's law)
+ var otherend = mapArea;
+ var x1 = ((otherend.x/2) - 100 - this.x);
+ var y1 = ((otherend.y/2) - this.y);
+ var dist = Math.sqrt(x1*x1 + y1*y1);
+ var xsign = x1/Math.abs(x1);
+ var theta = Math.atan(y1/x1);
+ if (x1==0) {
+ theta = Math.PI/2;
+ xsign = 0;
+ }
+ // force is based on radial distance
+ var f = (0.1*attract*dist)/10000;
+ if (Math.abs(dist)>0) {
+ fx += f * Math.cos(theta)*xsign;
+ fy += f * Math.sin(theta)*xsign;
+ }
+// $('debug1').innerHTML = x1;
+ }
+
+ // $('debug1').innerHTML = dist;
+ if (Math.abs(fx) > maxForce) fx = maxForce*(fx/Math.abs(fx));
+ if (Math.abs(fy) > maxForce) fy = maxForce*(fy/Math.abs(fy));
+ return {x:fx, y:fy};
+}
+SpiderNode.prototype.getSpeedVector = function() {
+ return {x:this.dx, y:this.dy};
+}
+SpiderNode.prototype.updatePosition = function() {
+
+
+ if (this.el.hasClass("dragging")) {
+ this.x = parseInt(this.el.style.left);
+ this.y = parseInt(this.el.style.top);
+ this.dx = 0;
+ this.dy = 0;
+ return;
+ }
+ //apply accelerations
+ var forces = this.getForceVector();
+// $('debug1').innerHTML = forces.x;
+ this.dx += forces.x*timeperiod;
+ this.dy += forces.y*timeperiod;
+
+ //TODO: CAP THE FORCES
+
+ // this.el.childNodes[0].innerHTML = parseInt(this.dx)+' '+parseInt(this.dy);
+ this.dx = this.dx*damping;
+ this.dy = this.dy*damping;
+
+ //TODO: ADD MINIMUM SPEEDS
+ if (Math.abs(this.dx)<minSpeed) this.dx=0;
+ if (Math.abs(this.dy)<minSpeed) this.dy=0;
+ //apply velocity vector
+ this.x += this.dx*timeperiod;
+ this.y += this.dy*timeperiod;
+ this.x = Math.min(mapArea.x,Math.max(1,this.x));
+ this.y = Math.min(mapArea.y,Math.max(1,this.y));
+ //only update the display after the thousanth iteration, so it's not too wild at the
start
+ this.count++;
+// if (this.count<updateDisplayAfterNthIteration) return;
+ // display
+ var showx = this.x - (this.el.getSize().x / 2);
+ var showy = this.y - (this.el.getSize().y / 2);
+ this.el.style.left = showx + "px";
+ this.el.style.top = showy + "px";
+
+}
+//////////////////////////////////////////////////////
+function SpiderLine(index, startSpiderNode, finSpiderNode) {
+ this.index = index;
+ this.start = startSpiderNode;
+ this.colour = "blue";
+ this.size = "thick";
+ this.end = finSpiderNode;
+ this.count=0;
+}
+SpiderLine.prototype.updatePosition = function() {
+ if (showSublines && (!this.start.hasLayout || !this.end.hasLayout)) return;
+ if (!showSublines && (!this.start.visible || !this.end.visible)) return;
+ if (this.start.visible && this.end.visible) this.size = "thick";
+ else this.size="thin";
+ if (activeNode.parent==this.start || activeNode.parent==this.end) this.colour =
"red";
+ else this.colour="blue";
+ switch (this.colour) {
+ case "red":
+ ctx.strokeStyle = "rgb(100, 0, 0)";
+ break;
+ case "blue":
+ ctx.strokeStyle = "rgb(0, 0, 100)";
+ break;
+ }
+ switch (this.size) {
+ case "thick":
+ ctx.lineWidth = "3";
+ break;
+ case "thin":
+ ctx.lineWidth = "1";
+ break;
+ }
+ ctx.beginPath();
+ ctx.moveTo(this.start.x, this.start.y);
+ ctx.quadraticCurveTo(((this.start.x + this.end.x)/1.8),((this.start.y +
this.end.y)/2.4), this.end.x, this.end.y);
+ ctx.stroke();
+ ctx.closePath();
+
+}
+
+//////////////////////////////////////////////////////
+function SpiderLoop() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ //update node positions
+ for (var i=0;i<nodes.length;i++) {
+ //TODO: replace this temporary idea
+ var childActive = false;
+ var currentNode = activeNode;
+ while (currentNode.parent && (currentNode=currentNode.parent)) {
+ if (currentNode == nodes[i]) childActive = true;
+ }
+ if (childActive || activeNode==nodes[i] || activeNode==nodes[i].parent) {
+ nodes[i].visible=true;
+ nodes[i].hasLayout = true;
+ } else {
+ nodes[i].visible=false;
+ if (nodes[i].parent && nodes[i].parent.parent &&
nodes[i].parent.parent==activeNode) {
+ nodes[i].hasLayout = true;
+ } else {
+ nodes[i].hasLayout = false;
+ }
+ }
+ if (nodes[i].visible) {
+ nodes[i].el.style.display="block";
+ } else {
+ nodes[i].el.style.display="none";
+ }
+ if ((showSublines && !nodes[i].hasLayout) || (!showSublines &&
!nodes[i].visible)) continue;
+ nodes[i].updatePosition();
+ }
+ //display lines
+ for (var i=0;i<lines.length;i++) {
+ lines[i].updatePosition();
+ }
+}
+//////////////////////////////////////////////////////
+function addList(ul, parent) {
+ var mylis = ul.childNodes;
+ var thislist = [];
+ var linecounter = 0;
+ for (var li=0;li<mylis.length;li++) {
+ if (mylis[li].tagName!='LI') continue;
+ var nodeno = nodes.length;
+ nodes[nodeno] = new SpiderNode(nodeno, mylis[li], parent);
+
+ thislist[thislist.length] = nodes[nodeno];
+ var mylicontent = mylis[li].childNodes;
+ for (var i=0;i<mylicontent.length;i++) {
+ if (mylicontent[i].tagName!='UL') continue;
+ addList(mylicontent[i], nodes[nodeno]);
+ }
+
+ if (parent!=null) {
+ var lineno = lines.length;
+ lines[lineno] = new SpiderLine(lineno, nodes[nodeno], parent);
+ }
+
+ }
+}
+
+
+var nodes = new Array();
+var lines = new Array();
+var activenode=null;
+onload = function() {
+ var myroot = $$('#js-mindmap>a')[0];
+
+ // create a misc UL to store flattened nodes
+ var miscUL = document.createElement("UL");
+ $('js-mindmap').appendChild(miscUL);
+
+ var nodeno = nodes.length;
+ nodes[nodeno] = new SpiderNode(nodeno, myroot, null, true);
+
+ var myul = $$('#js-mindmap>ul')[0];
+ addList(myul, nodes[nodeno]);
+ for (var i=0;i<nodes.length;i++) {
+ nodes[i].normalizePosition();
+ }
+
+ setInterval(SpiderLoop, 40);
+
+ $('js-mindmap').addClass('js-mindmap');
+
+ //CANVAS
+ canvas = document.getElementById("cv");
+ ctx = canvas.getContext("2d");
+ ctx.lineWidth = "2";
+ ctx.strokeStyle = "rgb(100, 100, 100)";
+}
diff --git a/moksha/widgets/mindmap/static/js/lib/excanvas-compressed.js
b/moksha/widgets/mindmap/static/js/lib/excanvas-compressed.js
new file mode 100644
index 0000000..9d71658
--- /dev/null
+++ b/moksha/widgets/mindmap/static/js/lib/excanvas-compressed.js
@@ -0,0 +1,19 @@
+if(!window.CanvasRenderingContext2D){(function(){var
I=Math,i=I.round,L=I.sin,M=I.cos,m=10,A=m/2,Q={init:function(a){var
b=a||document;if(/MSIE/.test(navigator.userAgent)&&!window.opera){var
c=this;b.attachEvent("onreadystatechange",function(){c.r(b)})}},r:function(a){if(a.readyState=="complete"){if(!a.namespaces["s"]){a.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml")}var
b=a.createStyleSheet();b.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}g_vml_\\:*{behavior:url(#default#VML)}";
+var c=a.getElementsByTagName("canvas");for(var
d=0;d<c.length;d++){if(!c[d].getContext){this.initElement(c[d])}}}},q:function(a){var
b=a.outerHTML,c=a.ownerDocument.createElement(b);if(b.slice(-2)!="/>"){var
d="/"+a.tagName,e;while((e=a.nextSibling)&&e.tagName!=d){e.removeNode()}if(e){e.removeNode()}}a.parentNode.replaceChild(c,a);return
c},initElement:function(a){a=this.q(a);a.getContext=function(){if(this.l){return
this.l}return this.l=new
K(this)};a.attachEvent("onpropertychange",V);a.attachEvent("onresize",
+W);var
b=a.attributes;if(b.width&&b.width.specified){a.style.width=b.width.nodeValue+"px"}else{a.width=a.clientWidth}if(b.height&&b.height.specified){a.style.height=b.height.nodeValue+"px"}else{a.height=a.clientHeight}return
a}};function V(a){var b=a.srcElement;switch(a.propertyName){case
"width":b.style.width=b.attributes.width.nodeValue+"px";b.getContext().clearRect();break;case
"height":b.style.height=b.attributes.height.nodeValue+"px";b.getContext().clearRect();break}}function
W(a){var b=a.srcElement;
+if(b.firstChild){b.firstChild.style.width=b.clientWidth+"px";b.firstChild.style.height=b.clientHeight+"px"}}Q.init();var
R=[];for(var E=0;E<16;E++){for(var
F=0;F<16;F++){R[E*16+F]=E.toString(16)+F.toString(16)}}function
J(){return[[1,0,0],[0,1,0],[0,0,1]]}function G(a,b){var c=J();for(var
d=0;d<3;d++){for(var e=0;e<3;e++){var g=0;for(var
h=0;h<3;h++){g+=a[d][h]*b[h][e]}c[d][e]=g}}return c}function
N(a,b){b.fillStyle=a.fillStyle;b.lineCap=a.lineCap;b.lineJoin=a.lineJoin;b.lineWidth=a.lineWidth;b.miterLimit=
+a.miterLimit;b.shadowBlur=a.shadowBlur;b.shadowColor=a.shadowColor;b.shadowOffsetX=a.shadowOffsetX;b.shadowOffsetY=a.shadowOffsetY;b.strokeStyle=a.strokeStyle;b.d=a.d;b.e=a.e}function
O(a){var b,c=1;a=String(a);if(a.substring(0,3)=="rgb"){var
d=a.indexOf("(",3),e=a.indexOf(")",d+1),g=a.substring(d+1,e).split(",");b="#";for(var
h=0;h<3;h++){b+=R[Number(g[h])]}if(g.length==4&&a.substr(3,1)=="a"){c=g[3]}}else{b=a}return[b,c]}function
S(a){switch(a){case "butt":return"flat";case
"round":return"round";
+case "square":default:return"square"}}function
K(a){this.a=J();this.m=[];this.k=[];this.c=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=m*1;this.globalAlpha=1;this.canvas=a;var
b=a.ownerDocument.createElement("div");b.style.width=a.clientWidth+"px";b.style.height=a.clientHeight+"px";b.style.overflow="hidden";b.style.position="absolute";a.appendChild(b);this.j=b;this.d=1;this.e=1}var
j=K.prototype;j.clearRect=function(){this.j.innerHTML=
+"";this.c=[]};j.beginPath=function(){this.c=[]};j.moveTo=function(a,b){this.c.push({type:"moveTo",x:a,y:b});this.f=a;this.g=b};j.lineTo=function(a,b){this.c.push({type:"lineTo",x:a,y:b});this.f=a;this.g=b};j.bezierCurveTo=function(a,b,c,d,e,g){this.c.push({type:"bezierCurveTo",cp1x:a,cp1y:b,cp2x:c,cp2y:d,x:e,y:g});this.f=e;this.g=g};j.quadraticCurveTo=function(a,b,c,d){var
e=this.f+0.6666666666666666*(a-this.f),g=this.g+0.6666666666666666*(b-this.g),h=e+(c-this.f)/3,l=g+(d-this.g)/3;this.bezierCurveTo(e,
+g,h,l,c,d)};j.arc=function(a,b,c,d,e,g){c*=m;var
h=g?"at":"wa",l=a+M(d)*c-A,n=b+L(d)*c-A,o=a+M(e)*c-A,f=b+L(e)*c-A;if(l==o&&!g){l+=0.125}this.c.push({type:h,x:a,y:b,radius:c,xStart:l,yStart:n,xEnd:o,yEnd:f})};j.rect=function(a,b,c,d){this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath()};j.strokeRect=function(a,b,c,d){this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath();this.stroke()};j.fillRect=function(a,
+b,c,d){this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath();this.fill()};j.createLinearGradient=function(a,b,c,d){var
e=new H("gradient");return e};j.createRadialGradient=function(a,b,c,d,e,g){var
h=new H("gradientradial");h.n=c;h.o=g;h.i.x=a;h.i.y=b;return
h};j.drawImage=function(a,b){var
c,d,e,g,h,l,n,o,f=a.runtimeStyle.width,k=a.runtimeStyle.height;a.runtimeStyle.width="auto";a.runtimeStyle.height="auto";var
q=a.width,r=a.height;a.runtimeStyle.width=
+f;a.runtimeStyle.height=k;if(arguments.length==3){c=arguments[1];d=arguments[2];h=(l=0);n=(e=q);o=(g=r)}else
if(arguments.length==5){c=arguments[1];d=arguments[2];e=arguments[3];g=arguments[4];h=(l=0);n=q;o=r}else
if(arguments.length==9){h=arguments[1];l=arguments[2];n=arguments[3];o=arguments[4];c=arguments[5];d=arguments[6];e=arguments[7];g=arguments[8]}else{throw"Invalid
number of arguments";}var s=this.b(c,d),t=[],v=10,w=10;t.push("
<g_vml_:group",'
coordsize="',m*v,",",m*w,'"','
coordorigin="0,0"',
+'
style="width:',v,";height:",w,";position:absolute;");if(this.a[0][0]!=1||this.a[0][1]){var
x=[];x.push("M11='",this.a[0][0],"',","M12='",this.a[1][0],"',","M21='",this.a[0][1],"',","M22='",this.a[1][1],"',","Dx='",i(s.x/m),"',","Dy='",i(s.y/m),"'");var
p=s,y=this.b(c+e,d),z=this.b(c,d+g),B=this.b(c+e,d+g);p.x=Math.max(p.x,y.x,z.x,B.x);p.y=Math.max(p.y,y.y,z.y,B.y);t.push("padding:0
",i(p.x/m),"px ",i(p.y/m),"px
0;filter:progid:DXImageTransform.Microsoft.Matrix(",x.join(""),",
sizingmethod='clip');")}else{t.push("top:",
+i(s.y/m),"px;left:",i(s.x/m),"px;")}t.push('
">','<g_vml_:image src="',a.src,'"','
style="width:',m*e,";"," height:",m*g,';"','
cropleft="',h/q,'"','
croptop="',l/r,'"','
cropright="',(q-h-n)/q,'"','
cropbottom="',(r-l-o)/r,'"',"
/>","</g_vml_:group>");this.j.insertAdjacentHTML("BeforeEnd",t.join(""))};j.stroke=function(a){var
b=[],c=O(a?this.fillStyle:this.strokeStyle),d=c[0],e=c[1]*this.globalAlpha,g=10,h=10;b.push("<g_vml_:shape",'
fillcolor="',d,'"','
filled="',Boolean(a),'"','
style="position:absolute;width:',
+g,";height:",h,';"',' coordorigin="0 0"
coordsize="',m*g," ",m*h,'"','
stroked="',!a,'"','
strokeweight="',this.lineWidth,'"','
strokecolor="',d,'"',' path="');var
l={x:null,y:null},n={x:null,y:null};for(var o=0;o<this.c.length;o++){var
f=this.c[o];if(f.type=="moveTo"){b.push(" m ");var
k=this.b(f.x,f.y);b.push(i(k.x),",",i(k.y))}else
if(f.type=="lineTo"){b.push(" l ");var
k=this.b(f.x,f.y);b.push(i(k.x),",",i(k.y))}else
if(f.type=="close"){b.push(" x ")}else
if(f.type=="bezierCurveTo"){b.push(" c ");
+var
k=this.b(f.x,f.y),q=this.b(f.cp1x,f.cp1y),r=this.b(f.cp2x,f.cp2y);b.push(i(q.x),",",i(q.y),",",i(r.x),",",i(r.y),",",i(k.x),",",i(k.y))}else
if(f.type=="at"||f.type=="wa"){b.push(" ",f.type,"
");var
k=this.b(f.x,f.y),s=this.b(f.xStart,f.yStart),t=this.b(f.xEnd,f.yEnd);b.push(i(k.x-this.d*f.radius),",",i(k.y-this.e*f.radius),"
",i(k.x+this.d*f.radius),",",i(k.y+this.e*f.radius),"
",i(s.x),",",i(s.y),"
",i(t.x),",",i(t.y))}if(k){if(l.x==null||k.x<l.x){l.x=k.x}if(n.x==null||k.x>n.x){n.x=k.x}if(l.y==
+null||k.y<l.y){l.y=k.y}if(n.y==null||k.y>n.y){n.y=k.y}}}b.push('
">');if(typeof this.fillStyle=="object"){var
v={x:"50%",y:"50%"},w=n.x-l.x,x=n.y-l.y,p=w>x?w:x;v.x=i(this.fillStyle.i.x/w*100+50)+"%";v.y=i(this.fillStyle.i.y/x*100+50)+"%";var
y=[];if(this.fillStyle.p=="gradientradial"){var
z=this.fillStyle.n/p*100,B=this.fillStyle.o/p*100-z}else{var z=0,B=100}var
C={offset:null,color:null},D={offset:null,color:null};this.fillStyle.h.sort(function(T,U){return
T.offset-U.offset});for(var o=0;o<this.fillStyle.h.length;o++){var u=
+this.fillStyle.h[o];y.push(u.offset*B+z,"%
",u.color,",");if(u.offset>C.offset||C.offset==null){C.offset=u.offset;C.color=u.color}if(u.offset<D.offset||D.offset==null){D.offset=u.offset;D.color=u.color}}y.pop();b.push("<g_vml_:fill",'
color="',D.color,'"','
color2="',C.color,'"','
type="',this.fillStyle.p,'"','
focusposition="',v.x,", ",v.y,'"','
colors="',y.join(""),'"','
opacity="',e,'" />')}else if(a){b.push('<g_vml_:fill
color="',d,'" opacity="',e,'"
/>')}else{b.push("<g_vml_:stroke",' opacity="',
+e,'"',' joinstyle="',this.lineJoin,'"','
miterlimit="',this.miterLimit,'"','
endcap="',S(this.lineCap),'"','
weight="',this.lineWidth,'px"',' color="',d,'"
/>')}b.push("</g_vml_:shape>");this.j.insertAdjacentHTML("beforeEnd",b.join(""));this.c=[]};j.fill=function(){this.stroke(true)};j.closePath=function(){this.c.push({type:"close"})};j.b=function(a,b){return{x:m*(a*this.a[0][0]+b*this.a[1][0]+this.a[2][0])-A,y:m*(a*this.a[0][1]+b*this.a[1][1]+this.a[2][1])-A}};j.save=function(){var
a={};N(this,a);
+this.k.push(a);this.m.push(this.a);this.a=G(J(),this.a)};j.restore=function(){N(this.k.pop(),this);this.a=this.m.pop()};j.translate=function(a,b){var
c=[[1,0,0],[0,1,0],[a,b,1]];this.a=G(c,this.a)};j.rotate=function(a){var
b=M(a),c=L(a),d=[[b,c,0],[-c,b,0],[0,0,1]];this.a=G(d,this.a)};j.scale=function(a,b){this.d*=a;this.e*=b;var
c=[[a,0,0],[0,b,0],[0,0,1]];this.a=G(c,this.a)};j.clip=function(){};j.arcTo=function(){};j.createPattern=function(){return
new P};function H(a){this.p=a;this.n=0;this.o=
+0;this.h=[];this.i={x:0,y:0}}H.prototype.addColorStop=function(a,b){b=O(b);this.h.push({offset:1-a,color:b})};function
P(){}G_vmlCanvasManager=Q;CanvasRenderingContext2D=K;CanvasGradient=H;CanvasPattern=P})()};
diff --git a/moksha/widgets/mindmap/static/js/lib/excanvas.js
b/moksha/widgets/mindmap/static/js/lib/excanvas.js
new file mode 100644
index 0000000..3e1aedf
--- /dev/null
+++ b/moksha/widgets/mindmap/static/js/lib/excanvas.js
@@ -0,0 +1,785 @@
+// Copyright 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//
http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+// Known Issues:
+//
+// * Patterns are not implemented.
+// * Radial gradient are not implemented. The VML version of these look very
+// different from the canvas one.
+// * Clipping paths are not implemented.
+// * Coordsize. The width and height attribute have higher priority than the
+// width and height style values which isn't correct.
+// * Painting mode isn't implemented.
+// * Canvas width/height should is using content-box by default. IE in
+// Quirks mode will draw the canvas using border-box. Either change your
+// doctype to HTML5
+// (
http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
+// or use Box Sizing Behavior from WebFX
+// (
http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
+// * Optimize. There is always room for speed improvements.
+
+// only add this code if we do not already have a canvas implementation
+if (!window.CanvasRenderingContext2D) {
+
+(function () {
+
+ // alias some functions to make (compiled) code shorter
+ var m = Math;
+ var mr = m.round;
+ var ms = m.sin;
+ var mc = m.cos;
+
+ // this is used for sub pixel precision
+ var Z = 10;
+ var Z2 = Z / 2;
+
+ var G_vmlCanvasManager_ = {
+ init: function (opt_doc) {
+ var doc = opt_doc || document;
+ if (/MSIE/.test(navigator.userAgent) && !window.opera) {
+ var self = this;
+ doc.attachEvent("onreadystatechange", function () {
+ self.init_(doc);
+ });
+ }
+ },
+
+ init_: function (doc) {
+ if (doc.readyState == "complete") {
+ // create xmlns
+ if (!doc.namespaces["g_vml_"]) {
+ doc.namespaces.add("g_vml_",
"urn:schemas-microsoft-com:vml");
+ }
+
+ // setup default css
+ var ss = doc.createStyleSheet();
+ ss.cssText = "canvas{display:inline-block;overflow:hidden;" +
+ // default size is 300x150 in Gecko and Opera
+ "text-align:left;width:300px;height:150px}" +
+ "g_vml_\\:*{behavior:url(#default#VML)}";
+
+ // find all canvas elements
+ var els = doc.getElementsByTagName("canvas");
+ for (var i = 0; i < els.length; i++) {
+ if (!els[i].getContext) {
+ this.initElement(els[i]);
+ }
+ }
+ }
+ },
+
+ fixElement_: function (el) {
+ // in IE before version 5.5 we would need to add HTML: to the tag name
+ // but we do not care about IE before version 6
+ var outerHTML = el.outerHTML;
+
+ var newEl = el.ownerDocument.createElement(outerHTML);
+ // if the tag is still open IE has created the children as siblings and
+ // it has also created a tag with the name "/FOO"
+ if (outerHTML.slice(-2) != "/>") {
+ var tagName = "/" + el.tagName;
+ var ns;
+ // remove content
+ while ((ns = el.nextSibling) && ns.tagName != tagName) {
+ ns.removeNode();
+ }
+ // remove the incorrect closing tag
+ if (ns) {
+ ns.removeNode();
+ }
+ }
+ el.parentNode.replaceChild(newEl, el);
+ return newEl;
+ },
+
+ /**
+ * Public initializes a canvas element so that it can be used as canvas
+ * element from now on. This is called automatically before the page is
+ * loaded but if you are creating elements using createElement you need to
+ * make sure this is called on the element.
+ * @param {HTMLElement} el The canvas element to initialize.
+ * @return {HTMLElement} the element that was created.
+ */
+ initElement: function (el) {
+ el = this.fixElement_(el);
+ el.getContext = function () {
+ if (this.context_) {
+ return this.context_;
+ }
+ return this.context_ = new CanvasRenderingContext2D_(this);
+ };
+
+ // do not use inline function because that will leak memory
+ el.attachEvent('onpropertychange', onPropertyChange);
+ el.attachEvent('onresize', onResize);
+
+ var attrs = el.attributes;
+ if (attrs.width && attrs.width.specified) {
+ // TODO: use runtimeStyle and coordsize
+ // el.getContext().setWidth_(attrs.width.nodeValue);
+ el.style.width = attrs.width.nodeValue + "px";
+ } else {
+ el.width = el.clientWidth;
+ }
+ if (attrs.height && attrs.height.specified) {
+ // TODO: use runtimeStyle and coordsize
+ // el.getContext().setHeight_(attrs.height.nodeValue);
+ el.style.height = attrs.height.nodeValue + "px";
+ } else {
+ el.height = el.clientHeight;
+ }
+ //el.getContext().setCoordsize_()
+ return el;
+ }
+ };
+
+ function onPropertyChange(e) {
+ var el = e.srcElement;
+
+ switch (e.propertyName) {
+ case 'width':
+ el.style.width = el.attributes.width.nodeValue + "px";
+ el.getContext().clearRect();
+ break;
+ case 'height':
+ el.style.height = el.attributes.height.nodeValue + "px";
+ el.getContext().clearRect();
+ break;
+ }
+ }
+
+ function onResize(e) {
+ var el = e.srcElement;
+ if (el.firstChild) {
+ el.firstChild.style.width = el.clientWidth + 'px';
+ el.firstChild.style.height = el.clientHeight + 'px';
+ }
+ }
+
+ G_vmlCanvasManager_.init();
+
+ // precompute "00" to "FF"
+ var dec2hex = [];
+ for (var i = 0; i < 16; i++) {
+ for (var j = 0; j < 16; j++) {
+ dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
+ }
+ }
+
+ function createMatrixIdentity() {
+ return [
+ [1, 0, 0],
+ [0, 1, 0],
+ [0, 0, 1]
+ ];
+ }
+
+ function matrixMultiply(m1, m2) {
+ var result = createMatrixIdentity();
+
+ for (var x = 0; x < 3; x++) {
+ for (var y = 0; y < 3; y++) {
+ var sum = 0;
+
+ for (var z = 0; z < 3; z++) {
+ sum += m1[x][z] * m2[z][y];
+ }
+
+ result[x][y] = sum;
+ }
+ }
+ return result;
+ }
+
+ function copyState(o1, o2) {
+ o2.fillStyle = o1.fillStyle;
+ o2.lineCap = o1.lineCap;
+ o2.lineJoin = o1.lineJoin;
+ o2.lineWidth = o1.lineWidth;
+ o2.miterLimit = o1.miterLimit;
+ o2.shadowBlur = o1.shadowBlur;
+ o2.shadowColor = o1.shadowColor;
+ o2.shadowOffsetX = o1.shadowOffsetX;
+ o2.shadowOffsetY = o1.shadowOffsetY;
+ o2.strokeStyle = o1.strokeStyle;
+ o2.arcScaleX_ = o1.arcScaleX_;
+ o2.arcScaleY_ = o1.arcScaleY_;
+ }
+
+ function processStyle(styleString) {
+ var str, alpha = 1;
+
+ styleString = String(styleString);
+ if (styleString.substring(0, 3) == "rgb") {
+ var start = styleString.indexOf("(", 3);
+ var end = styleString.indexOf(")", start + 1);
+ var guts = styleString.substring(start + 1, end).split(",");
+
+ str = "#";
+ for (var i = 0; i < 3; i++) {
+ str += dec2hex[Number(guts[i])];
+ }
+
+ if ((guts.length == 4) && (styleString.substr(3, 1) == "a")) {
+ alpha = guts[3];
+ }
+ } else {
+ str = styleString;
+ }
+
+ return [str, alpha];
+ }
+
+ function processLineCap(lineCap) {
+ switch (lineCap) {
+ case "butt":
+ return "flat";
+ case "round":
+ return "round";
+ case "square":
+ default:
+ return "square";
+ }
+ }
+
+ /**
+ * This class implements CanvasRenderingContext2D interface as described by
+ * the WHATWG.
+ * @param {HTMLElement} surfaceElement The element that the 2D context should
+ * be associated with
+ */
+ function CanvasRenderingContext2D_(surfaceElement) {
+ this.m_ = createMatrixIdentity();
+
+ this.mStack_ = [];
+ this.aStack_ = [];
+ this.currentPath_ = [];
+
+ // Canvas context properties
+ this.strokeStyle = "#000";
+ this.fillStyle = "#000";
+
+ this.lineWidth = 1;
+ this.lineJoin = "miter";
+ this.lineCap = "butt";
+ this.miterLimit = Z * 1;
+ this.globalAlpha = 1;
+ this.canvas = surfaceElement;
+
+ var el = surfaceElement.ownerDocument.createElement('div');
+ el.style.width = surfaceElement.clientWidth + 'px';
+ el.style.height = surfaceElement.clientHeight + 'px';
+ el.style.overflow = 'hidden';
+ el.style.position = 'absolute';
+ surfaceElement.appendChild(el);
+
+ this.element_ = el;
+ this.arcScaleX_ = 1;
+ this.arcScaleY_ = 1;
+ };
+
+ var contextPrototype = CanvasRenderingContext2D_.prototype;
+ contextPrototype.clearRect = function() {
+ this.element_.innerHTML = "";
+ this.currentPath_ = [];
+ };
+
+ contextPrototype.beginPath = function() {
+ // TODO: Branch current matrix so that save/restore has no effect
+ // as per safari docs.
+
+ this.currentPath_ = [];
+ };
+
+ contextPrototype.moveTo = function(aX, aY) {
+ this.currentPath_.push({type: "moveTo", x: aX, y: aY});
+ this.currentX_ = aX;
+ this.currentY_ = aY;
+ };
+
+ contextPrototype.lineTo = function(aX, aY) {
+ this.currentPath_.push({type: "lineTo", x: aX, y: aY});
+ this.currentX_ = aX;
+ this.currentY_ = aY;
+ };
+
+ contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
+ aCP2x, aCP2y,
+ aX, aY) {
+ this.currentPath_.push({type: "bezierCurveTo",
+ cp1x: aCP1x,
+ cp1y: aCP1y,
+ cp2x: aCP2x,
+ cp2y: aCP2y,
+ x: aX,
+ y: aY});
+ this.currentX_ = aX;
+ this.currentY_ = aY;
+ };
+
+ contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
+ // the following is lifted almost directly from
+ //
http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
+ var cp1x = this.currentX_ + 2.0 / 3.0 * (aCPx - this.currentX_);
+ var cp1y = this.currentY_ + 2.0 / 3.0 * (aCPy - this.currentY_);
+ var cp2x = cp1x + (aX - this.currentX_) / 3.0;
+ var cp2y = cp1y + (aY - this.currentY_) / 3.0;
+ this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, aX, aY);
+ };
+
+ contextPrototype.arc = function(aX, aY, aRadius,
+ aStartAngle, aEndAngle, aClockwise) {
+ aRadius *= Z;
+ var arcType = aClockwise ? "at" : "wa";
+
+ var xStart = aX + (mc(aStartAngle) * aRadius) - Z2;
+ var yStart = aY + (ms(aStartAngle) * aRadius) - Z2;
+
+ var xEnd = aX + (mc(aEndAngle) * aRadius) - Z2;
+ var yEnd = aY + (ms(aEndAngle) * aRadius) - Z2;
+
+ // IE won't render arches drawn counter clockwise if xStart == xEnd.
+ if (xStart == xEnd && !aClockwise) {
+ xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
+ // that can be represented in binary
+ }
+
+ this.currentPath_.push({type: arcType,
+ x: aX,
+ y: aY,
+ radius: aRadius,
+ xStart: xStart,
+ yStart: yStart,
+ xEnd: xEnd,
+ yEnd: yEnd});
+
+ };
+
+ contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
+ this.moveTo(aX, aY);
+ this.lineTo(aX + aWidth, aY);
+ this.lineTo(aX + aWidth, aY + aHeight);
+ this.lineTo(aX, aY + aHeight);
+ this.closePath();
+ };
+
+ contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
+ // Will destroy any existing path (same as FF behaviour)
+ this.beginPath();
+ this.moveTo(aX, aY);
+ this.lineTo(aX + aWidth, aY);
+ this.lineTo(aX + aWidth, aY + aHeight);
+ this.lineTo(aX, aY + aHeight);
+ this.closePath();
+ this.stroke();
+ };
+
+ contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
+ // Will destroy any existing path (same as FF behaviour)
+ this.beginPath();
+ this.moveTo(aX, aY);
+ this.lineTo(aX + aWidth, aY);
+ this.lineTo(aX + aWidth, aY + aHeight);
+ this.lineTo(aX, aY + aHeight);
+ this.closePath();
+ this.fill();
+ };
+
+ contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
+ var gradient = new CanvasGradient_("gradient");
+ return gradient;
+ };
+
+ contextPrototype.createRadialGradient = function(aX0, aY0,
+ aR0, aX1,
+ aY1, aR1) {
+ var gradient = new CanvasGradient_("gradientradial");
+ gradient.radius1_ = aR0;
+ gradient.radius2_ = aR1;
+ gradient.focus_.x = aX0;
+ gradient.focus_.y = aY0;
+ return gradient;
+ };
+
+ contextPrototype.drawImage = function (image, var_args) {
+ var dx, dy, dw, dh, sx, sy, sw, sh;
+
+ // to find the original width we overide the width and height
+ var oldRuntimeWidth = image.runtimeStyle.width;
+ var oldRuntimeHeight = image.runtimeStyle.height;
+ image.runtimeStyle.width = 'auto';
+ image.runtimeStyle.height = 'auto';
+
+ // get the original size
+ var w = image.width;
+ var h = image.height;
+
+ // and remove overides
+ image.runtimeStyle.width = oldRuntimeWidth;
+ image.runtimeStyle.height = oldRuntimeHeight;
+
+ if (arguments.length == 3) {
+ dx = arguments[1];
+ dy = arguments[2];
+ sx = sy = 0;
+ sw = dw = w;
+ sh = dh = h;
+ } else if (arguments.length == 5) {
+ dx = arguments[1];
+ dy = arguments[2];
+ dw = arguments[3];
+ dh = arguments[4];
+ sx = sy = 0;
+ sw = w;
+ sh = h;
+ } else if (arguments.length == 9) {
+ sx = arguments[1];
+ sy = arguments[2];
+ sw = arguments[3];
+ sh = arguments[4];
+ dx = arguments[5];
+ dy = arguments[6];
+ dw = arguments[7];
+ dh = arguments[8];
+ } else {
+ throw "Invalid number of arguments";
+ }
+
+ var d = this.getCoords_(dx, dy);
+
+ var w2 = sw / 2;
+ var h2 = sh / 2;
+
+ var vmlStr = [];
+
+ var W = 10;
+ var H = 10;
+
+ // For some reason that I've now forgotten, using divs didn't work
+ vmlStr.push(' <g_vml_:group',
+ ' coordsize="', Z * W, ',', Z * H,
'"',
+ ' coordorigin="0,0"' ,
+ ' style="width:', W, ';height:', H,
';position:absolute;');
+
+ // If filters are necessary (rotation exists), create them
+ // filters are bog-slow, so only create them if abbsolutely necessary
+ // The following check doesn't account for skews (which don't exist
+ // in the canvas spec (yet) anyway.
+
+ if (this.m_[0][0] != 1 || this.m_[0][1]) {
+ var filter = [];
+
+ // Note the 12/21 reversal
+ filter.push("M11='", this.m_[0][0], "',",
+ "M12='", this.m_[1][0], "',",
+ "M21='", this.m_[0][1], "',",
+ "M22='", this.m_[1][1], "',",
+ "Dx='", mr(d.x / Z), "',",
+ "Dy='", mr(d.y / Z), "'");
+
+ // Bounding box calculation (need to minimize displayed area so that
+ // filters don't waste time on unused pixels.
+ var max = d;
+ var c2 = this.getCoords_(dx + dw, dy);
+ var c3 = this.getCoords_(dx, dy + dh);
+ var c4 = this.getCoords_(dx + dw, dy + dh);
+
+ max.x = Math.max(max.x, c2.x, c3.x, c4.x);
+ max.y = Math.max(max.y, c2.y, c3.y, c4.y);
+
+ vmlStr.push("padding:0 ", mr(max.x / Z), "px ", mr(max.y / Z),
+ "px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",
+ filter.join(""), ",
sizingmethod='clip');")
+ } else {
+ vmlStr.push("top:", mr(d.y / Z), "px;left:", mr(d.x / Z),
"px;")
+ }
+
+ vmlStr.push(' ">' ,
+ '<g_vml_:image src="', image.src, '"',
+ ' style="width:', Z * dw, ';',
+ ' height:', Z * dh, ';"',
+ ' cropleft="', sx / w, '"',
+ ' croptop="', sy / h, '"',
+ ' cropright="', (w - sx - sw) / w, '"',
+ ' cropbottom="', (h - sy - sh) / h, '"',
+ ' />',
+ '</g_vml_:group>');
+
+ this.element_.insertAdjacentHTML("BeforeEnd",
+ vmlStr.join(""));
+ };
+
+ contextPrototype.stroke = function(aFill) {
+ var lineStr = [];
+ var lineOpen = false;
+ var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
+ var color = a[0];
+ var opacity = a[1] * this.globalAlpha;
+
+ var W = 10;
+ var H = 10;
+
+ lineStr.push('<g_vml_:shape',
+ ' fillcolor="', color, '"',
+ ' filled="', Boolean(aFill), '"',
+ ' style="position:absolute;width:', W, ';height:',
H, ';"',
+ ' coordorigin="0 0" coordsize="', Z * W, '
', Z * H, '"',
+ ' stroked="', !aFill, '"',
+ ' strokeweight="', this.lineWidth, '"',
+ ' strokecolor="', color, '"',
+ ' path="');
+
+ var newSeq = false;
+ var min = {x: null, y: null};
+ var max = {x: null, y: null};
+
+ for (var i = 0; i < this.currentPath_.length; i++) {
+ var p = this.currentPath_[i];
+
+ if (p.type == "moveTo") {
+ lineStr.push(" m ");
+ var c = this.getCoords_(p.x, p.y);
+ lineStr.push(mr(c.x), ",", mr(c.y));
+ } else if (p.type == "lineTo") {
+ lineStr.push(" l ");
+ var c = this.getCoords_(p.x, p.y);
+ lineStr.push(mr(c.x), ",", mr(c.y));
+ } else if (p.type == "close") {
+ lineStr.push(" x ");
+ } else if (p.type == "bezierCurveTo") {
+ lineStr.push(" c ");
+ var c = this.getCoords_(p.x, p.y);
+ var c1 = this.getCoords_(p.cp1x, p.cp1y);
+ var c2 = this.getCoords_(p.cp2x, p.cp2y);
+ lineStr.push(mr(c1.x), ",", mr(c1.y), ",",
+ mr(c2.x), ",", mr(c2.y), ",",
+ mr(c.x), ",", mr(c.y));
+ } else if (p.type == "at" || p.type == "wa") {
+ lineStr.push(" ", p.type, " ");
+ var c = this.getCoords_(p.x, p.y);
+ var cStart = this.getCoords_(p.xStart, p.yStart);
+ var cEnd = this.getCoords_(p.xEnd, p.yEnd);
+
+ lineStr.push(mr(c.x - this.arcScaleX_ * p.radius), ",",
+ mr(c.y - this.arcScaleY_ * p.radius), " ",
+ mr(c.x + this.arcScaleX_ * p.radius), ",",
+ mr(c.y + this.arcScaleY_ * p.radius), " ",
+ mr(cStart.x), ",", mr(cStart.y), " ",
+ mr(cEnd.x), ",", mr(cEnd.y));
+ }
+
+
+ // TODO: Following is broken for curves due to
+ // move to proper paths.
+
+ // Figure out dimensions so we can do gradient fills
+ // properly
+ if(c) {
+ if (min.x == null || c.x < min.x) {
+ min.x = c.x;
+ }
+ if (max.x == null || c.x > max.x) {
+ max.x = c.x;
+ }
+ if (min.y == null || c.y < min.y) {
+ min.y = c.y;
+ }
+ if (max.y == null || c.y > max.y) {
+ max.y = c.y;
+ }
+ }
+ }
+ lineStr.push(' ">');
+
+ if (typeof this.fillStyle == "object") {
+ var focus = {x: "50%", y: "50%"};
+ var width = (max.x - min.x);
+ var height = (max.y - min.y);
+ var dimension = (width > height) ? width : height;
+
+ focus.x = mr((this.fillStyle.focus_.x / width) * 100 + 50) + "%";
+ focus.y = mr((this.fillStyle.focus_.y / height) * 100 + 50) + "%";
+
+ var colors = [];
+
+ // inside radius (%)
+ if (this.fillStyle.type_ == "gradientradial") {
+ var inside = (this.fillStyle.radius1_ / dimension * 100);
+
+ // percentage that outside radius exceeds inside radius
+ var expansion = (this.fillStyle.radius2_ / dimension * 100) - inside;
+ } else {
+ var inside = 0;
+ var expansion = 100;
+ }
+
+ var insidecolor = {offset: null, color: null};
+ var outsidecolor = {offset: null, color: null};
+
+ // We need to sort 'colors' by percentage, from 0 > 100 otherwise ie
+ // won't interpret it correctly
+ this.fillStyle.colors_.sort(function (cs1, cs2) {
+ return cs1.offset - cs2.offset;
+ });
+
+ for (var i = 0; i < this.fillStyle.colors_.length; i++) {
+ var fs = this.fillStyle.colors_[i];
+
+ colors.push( (fs.offset * expansion) + inside, "% ", fs.color,
",");
+
+ if (fs.offset > insidecolor.offset || insidecolor.offset == null) {
+ insidecolor.offset = fs.offset;
+ insidecolor.color = fs.color;
+ }
+
+ if (fs.offset < outsidecolor.offset || outsidecolor.offset == null) {
+ outsidecolor.offset = fs.offset;
+ outsidecolor.color = fs.color;
+ }
+ }
+ colors.pop();
+
+ lineStr.push('<g_vml_:fill',
+ ' color="', outsidecolor.color, '"',
+ ' color2="', insidecolor.color, '"',
+ ' type="', this.fillStyle.type_, '"',
+ ' focusposition="', focus.x, ', ', focus.y,
'"',
+ ' colors="', colors.join(""),
'"',
+ ' opacity="', opacity, '" />');
+ } else if (aFill) {
+ lineStr.push('<g_vml_:fill color="', color, '"
opacity="', opacity, '" />');
+ } else {
+ lineStr.push(
+ '<g_vml_:stroke',
+ ' opacity="', opacity,'"',
+ ' joinstyle="', this.lineJoin, '"',
+ ' miterlimit="', this.miterLimit, '"',
+ ' endcap="', processLineCap(this.lineCap) ,'"',
+ ' weight="', this.lineWidth, 'px"',
+ ' color="', color,'" />'
+ );
+ }
+
+ lineStr.push("</g_vml_:shape>");
+
+ this.element_.insertAdjacentHTML("beforeEnd", lineStr.join(""));
+
+ this.currentPath_ = [];
+ };
+
+ contextPrototype.fill = function() {
+ this.stroke(true);
+ }
+
+ contextPrototype.closePath = function() {
+ this.currentPath_.push({type: "close"});
+ };
+
+ /**
+ * @private
+ */
+ contextPrototype.getCoords_ = function(aX, aY) {
+ return {
+ x: Z * (aX * this.m_[0][0] + aY * this.m_[1][0] + this.m_[2][0]) - Z2,
+ y: Z * (aX * this.m_[0][1] + aY * this.m_[1][1] + this.m_[2][1]) - Z2
+ }
+ };
+
+ contextPrototype.save = function() {
+ var o = {};
+ copyState(this, o);
+ this.aStack_.push(o);
+ this.mStack_.push(this.m_);
+ this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
+ };
+
+ contextPrototype.restore = function() {
+ copyState(this.aStack_.pop(), this);
+ this.m_ = this.mStack_.pop();
+ };
+
+ contextPrototype.translate = function(aX, aY) {
+ var m1 = [
+ [1, 0, 0],
+ [0, 1, 0],
+ [aX, aY, 1]
+ ];
+
+ this.m_ = matrixMultiply(m1, this.m_);
+ };
+
+ contextPrototype.rotate = function(aRot) {
+ var c = mc(aRot);
+ var s = ms(aRot);
+
+ var m1 = [
+ [c, s, 0],
+ [-s, c, 0],
+ [0, 0, 1]
+ ];
+
+ this.m_ = matrixMultiply(m1, this.m_);
+ };
+
+ contextPrototype.scale = function(aX, aY) {
+ this.arcScaleX_ *= aX;
+ this.arcScaleY_ *= aY;
+ var m1 = [
+ [aX, 0, 0],
+ [0, aY, 0],
+ [0, 0, 1]
+ ];
+
+ this.m_ = matrixMultiply(m1, this.m_);
+ };
+
+ /******** STUBS ********/
+ contextPrototype.clip = function() {
+ // TODO: Implement
+ };
+
+ contextPrototype.arcTo = function() {
+ // TODO: Implement
+ };
+
+ contextPrototype.createPattern = function() {
+ return new CanvasPattern_;
+ };
+
+ // Gradient / Pattern Stubs
+ function CanvasGradient_(aType) {
+ this.type_ = aType;
+ this.radius1_ = 0;
+ this.radius2_ = 0;
+ this.colors_ = [];
+ this.focus_ = {x: 0, y: 0};
+ }
+
+ CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
+ aColor = processStyle(aColor);
+ this.colors_.push({offset: 1-aOffset, color: aColor});
+ };
+
+ function CanvasPattern_() {}
+
+ // set up externs
+ G_vmlCanvasManager = G_vmlCanvasManager_;
+ CanvasRenderingContext2D = CanvasRenderingContext2D_;
+ CanvasGradient = CanvasGradient_;
+ CanvasPattern = CanvasPattern_;
+
+})();
+
+} // if
diff --git a/moksha/widgets/mindmap/static/js/lib/mootools-1.2-core.js
b/moksha/widgets/mindmap/static/js/lib/mootools-1.2-core.js
new file mode 100644
index 0000000..b120ec5
--- /dev/null
+++ b/moksha/widgets/mindmap/static/js/lib/mootools-1.2-core.js
@@ -0,0 +1,3816 @@
+/*
+Script: Core.js
+ MooTools - My Object Oriented JavaScript Tools.
+
+License:
+ MIT-style license.
+
+Copyright:
+ Copyright (c) 2006-2007 [Valerio
Proietti](http://mad4milk.net/).
+
+Code & Documentation:
+ [The MooTools production
team](http://mootools.net/developers/).
+
+Inspiration:
+ - Class implementation inspired by
[Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards,
[GNU Lesser General Public
License](http://opensource.org/licenses/lgpl-license.php)
+ - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c)
2005-2007 Sam Stephenson, [MIT
License](http://opensource.org/licenses/mit-license.php)
+*/
+
+var MooTools = {
+ 'version': '1.2.0',
+ 'build': ''
+};
+
+var Native = function(options){
+ options = options || {};
+
+ var afterImplement = options.afterImplement || function(){};
+ var generics = options.generics;
+ generics = (generics !== false);
+ var legacy = options.legacy;
+ var initialize = options.initialize;
+ var protect = options.protect;
+ var name = options.name;
+
+ var object = initialize || legacy;
+
+ object.constructor = Native;
+ object.$family = {name: 'native'};
+ if (legacy && initialize) object.prototype = legacy.prototype;
+ object.prototype.constructor = object;
+
+ if (name){
+ var family = name.toLowerCase();
+ object.prototype.$family = {name: family};
+ Native.typize(object, family);
+ }
+
+ var add = function(obj, name, method, force){
+ if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
+ if (generics) Native.genericize(obj, name, protect);
+ afterImplement.call(obj, name, method);
+ return obj;
+ };
+
+ object.implement = function(a1, a2, a3){
+ if (typeof a1 == 'string') return add(this, a1, a2, a3);
+ for (var p in a1) add(this, p, a1[p], a2);
+ return this;
+ };
+
+ object.alias = function(a1, a2, a3){
+ if (typeof a1 == 'string'){
+ a1 = this.prototype[a1];
+ if (a1) add(this, a2, a1, a3);
+ } else {
+ for (var a in a1) this.alias(a, a1[a], a2);
+ }
+ return this;
+ };
+
+ return object;
+};
+
+Native.implement = function(objects, properties){
+ for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
+};
+
+Native.genericize = function(object, property, check){
+ if ((!check || !object[property]) && typeof object.prototype[property] ==
'function') object[property] = function(){
+ var args = Array.prototype.slice.call(arguments);
+ return object.prototype[property].apply(args.shift(), args);
+ };
+};
+
+Native.typize = function(object, family){
+ if (!object.type) object.type = function(item){
+ return ($type(item) === family);
+ };
+};
+
+Native.alias = function(objects, a1, a2, a3){
+ for (var i = 0, j = objects.length; i < j; i++) objects[i].alias(a1, a2, a3);
+};
+
+(function(objects){
+ for (var name in objects) Native.typize(objects[name], name);
+})({'boolean': Boolean, 'native': Native, 'object': Object});
+
+(function(objects){
+ for (var name in objects) new Native({name: name, initialize: objects[name], protect:
true});
+})({'String': String, 'Function': Function, 'Number': Number,
'Array': Array, 'RegExp': RegExp, 'Date': Date});
+
+(function(object, methods){
+ for (var i = methods.length; i--; i) Native.genericize(object, methods[i], true);
+ return arguments.callee;
+})
+(Array, ['pop', 'push', 'reverse', 'shift',
'sort', 'splice', 'unshift', 'concat', 'join',
'slice', 'toString', 'valueOf', 'indexOf',
'lastIndexOf'])
+(String, ['charAt', 'charCodeAt', 'concat', 'indexOf',
'lastIndexOf', 'match', 'replace', 'search',
'slice', 'split', 'substr', 'substring',
'toLowerCase', 'toUpperCase', 'valueOf']);
+
+function $chk(obj){
+ return !!(obj || obj === 0);
+};
+
+function $clear(timer){
+ clearTimeout(timer);
+ clearInterval(timer);
+ return null;
+};
+
+function $defined(obj){
+ return (obj != undefined);
+};
+
+function $empty(){};
+
+function $arguments(i){
+ return function(){
+ return arguments[i];
+ };
+};
+
+function $lambda(value){
+ return (typeof value == 'function') ? value : function(){
+ return value;
+ };
+};
+
+function $extend(original, extended){
+ for (var key in (extended || {})) original[key] = extended[key];
+ return original;
+};
+
+function $unlink(object){
+ var unlinked;
+
+ switch ($type(object)){
+ case 'object':
+ unlinked = {};
+ for (var p in object) unlinked[p] = $unlink(object[p]);
+ break;
+ case 'hash':
+ unlinked = $unlink(object.getClean());
+ break;
+ case 'array':
+ unlinked = [];
+ for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
+ break;
+ default: return object;
+ }
+
+ return unlinked;
+};
+
+function $merge(){
+ var mix = {};
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var object = arguments[i];
+ if ($type(object) != 'object') continue;
+ for (var key in object){
+ var op = object[key], mp = mix[key];
+ mix[key] = (mp && $type(op) == 'object' && $type(mp) ==
'object') ? $merge(mp, op) : $unlink(op);
+ }
+ }
+ return mix;
+};
+
+function $pick(){
+ for (var i = 0, l = arguments.length; i < l; i++){
+ if (arguments[i] != undefined) return arguments[i];
+ }
+ return null;
+};
+
+function $random(min, max){
+ return Math.floor(Math.random() * (max - min + 1) + min);
+};
+
+function $splat(obj){
+ var type = $type(obj);
+ return (type) ? ((type != 'array' && type != 'arguments') ?
[obj] : obj) : [];
+};
+
+var $time = Date.now || function(){
+ return new Date().getTime();
+};
+
+function $try(){
+ for (var i = 0, l = arguments.length; i < l; i++){
+ try {
+ return arguments[i]();
+ } catch(e){}
+ }
+ return null;
+};
+
+function $type(obj){
+ if (obj == undefined) return false;
+ if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj))
? false : obj.$family.name;
+ if (obj.nodeName){
+ switch (obj.nodeType){
+ case 1: return 'element';
+ case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' :
'whitespace';
+ }
+ } else if (typeof obj.length == 'number'){
+ if (obj.callee) return 'arguments';
+ else if (obj.item) return 'collection';
+ }
+ return typeof obj;
+};
+
+var Hash = new Native({
+
+ name: 'Hash',
+
+ initialize: function(object){
+ if ($type(object) == 'hash') object = $unlink(object.getClean());
+ for (var key in object) this[key] = object[key];
+ return this;
+ }
+
+});
+
+Hash.implement({
+
+ getLength: function(){
+ var length = 0;
+ for (var key in this){
+ if (this.hasOwnProperty(key)) length++;
+ }
+ return length;
+ },
+
+ forEach: function(fn, bind){
+ for (var key in this){
+ if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
+ }
+ },
+
+ getClean: function(){
+ var clean = {};
+ for (var key in this){
+ if (this.hasOwnProperty(key)) clean[key] = this[key];
+ }
+ return clean;
+ }
+
+});
+
+Hash.alias('forEach', 'each');
+
+function $H(object){
+ return new Hash(object);
+};
+
+Array.implement({
+
+ forEach: function(fn, bind){
+ for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
+ }
+
+});
+
+Array.alias('forEach', 'each');
+
+function $A(iterable){
+ if (iterable.item){
+ var array = [];
+ for (var i = 0, l = iterable.length; i < l; i++) array[i] = iterable[i];
+ return array;
+ }
+ return Array.prototype.slice.call(iterable);
+};
+
+function $each(iterable, fn, bind){
+ var type = $type(iterable);
+ ((type == 'arguments' || type == 'collection' || type ==
'array') ? Array : Hash).each(iterable, fn, bind);
+};
+
+
+/*
+Script: Browser.js
+ The Browser Core. Contains Browser initialization, Window and Document, and the Browser
Hash.
+
+License:
+ MIT-style license.
+*/
+
+var Browser = new Hash({
+ Engine: {name: 'unknown', version: ''},
+ Platform: {name: (navigator.platform.match(/mac|win|linux/i) ||
['other'])[0].toLowerCase()},
+ Features: {xpath: !!(document.evaluate), air: !!(window.runtime)},
+ Plugins: {}
+});
+
+if (window.opera) Browser.Engine = {name: 'presto', version:
(document.getElementsByClassName) ? 950 : 925};
+else if (window.ActiveXObject) Browser.Engine = {name: 'trident', version:
(window.XMLHttpRequest) ? 5 : 4};
+else if (!navigator.taintEnabled) Browser.Engine = {name: 'webkit', version:
(Browser.Features.xpath) ? 420 : 419};
+else if (document.getBoxObjectFor != null) Browser.Engine = {name: 'gecko',
version: (document.getElementsByClassName) ? 19 : 18};
+Browser.Engine[Browser.Engine.name] = Browser.Engine[Browser.Engine.name +
Browser.Engine.version] = true;
+
+if (window.orientation != undefined) Browser.Platform.name = 'ipod';
+
+Browser.Platform[Browser.Platform.name] = true;
+
+Browser.Request = function(){
+ return $try(function(){
+ return new XMLHttpRequest();
+ }, function(){
+ return new ActiveXObject('MSXML2.XMLHTTP');
+ });
+};
+
+Browser.Features.xhr = !!(Browser.Request());
+
+Browser.Plugins.Flash = (function(){
+ var version = ($try(function(){
+ return navigator.plugins['Shockwave Flash'].description;
+ }, function(){
+ return new
ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
+ }) || '0 r0').match(/\d+/g);
+ return {version: parseInt(version[0] || 0 + '.' + version[1] || 0), build:
parseInt(version[2] || 0)};
+})();
+
+function $exec(text){
+ if (!text) return text;
+ if (window.execScript){
+ window.execScript(text);
+ } else {
+ var script = document.createElement('script');
+ script.setAttribute('type', 'text/javascript');
+ script.text = text;
+ document.head.appendChild(script);
+ document.head.removeChild(script);
+ }
+ return text;
+};
+
+Native.UID = 1;
+
+var $uid = (Browser.Engine.trident) ? function(item){
+ return (item.uid || (item.uid = [Native.UID++]))[0];
+} : function(item){
+ return item.uid || (item.uid = Native.UID++);
+};
+
+var Window = new Native({
+
+ name: 'Window',
+
+ legacy: (Browser.Engine.trident) ? null: window.Window,
+
+ initialize: function(win){
+ $uid(win);
+ if (!win.Element){
+ win.Element = $empty;
+ if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes
safari 2
+ win.Element.prototype = (Browser.Engine.webkit) ?
window["[[DOMElement.prototype]]"] : {};
+ }
+ return $extend(win, Window.Prototype);
+ },
+
+ afterImplement: function(property, value){
+ window[property] = Window.Prototype[property] = value;
+ }
+
+});
+
+Window.Prototype = {$family: {name: 'window'}};
+
+new Window(window);
+
+var Document = new Native({
+
+ name: 'Document',
+
+ legacy: (Browser.Engine.trident) ? null: window.Document,
+
+ initialize: function(doc){
+ $uid(doc);
+ doc.head = doc.getElementsByTagName('head')[0];
+ doc.html = doc.getElementsByTagName('html')[0];
+ doc.window = doc.defaultView || doc.parentWindow;
+ if (Browser.Engine.trident4) $try(function(){
+ doc.execCommand("BackgroundImageCache", false, true);
+ });
+ return $extend(doc, Document.Prototype);
+ },
+
+ afterImplement: function(property, value){
+ document[property] = Document.Prototype[property] = value;
+ }
+
+});
+
+Document.Prototype = {$family: {name: 'document'}};
+
+new Document(document);
+
+/*
+Script: Array.js
+ Contains Array Prototypes like copy, each, contains, and remove.
+
+License:
+ MIT-style license.
+*/
+
+Array.implement({
+
+ every: function(fn, bind){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (!fn.call(bind, this[i], i, this)) return false;
+ }
+ return true;
+ },
+
+ filter: function(fn, bind){
+ var results = [];
+ for (var i = 0, l = this.length; i < l; i++){
+ if (fn.call(bind, this[i], i, this)) results.push(this[i]);
+ }
+ return results;
+ },
+
+ clean: function() {
+ return this.filter($defined);
+ },
+
+ indexOf: function(item, from){
+ var len = this.length;
+ for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
+ if (this[i] === item) return i;
+ }
+ return -1;
+ },
+
+ map: function(fn, bind){
+ var results = [];
+ for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i,
this);
+ return results;
+ },
+
+ some: function(fn, bind){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (fn.call(bind, this[i], i, this)) return true;
+ }
+ return false;
+ },
+
+ associate: function(keys){
+ var obj = {}, length = Math.min(this.length, keys.length);
+ for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
+ return obj;
+ },
+
+ link: function(object){
+ var result = {};
+ for (var i = 0, l = this.length; i < l; i++){
+ for (var key in object){
+ if (object[key](this[i])){
+ result[key] = this[i];
+ delete object[key];
+ break;
+ }
+ }
+ }
+ return result;
+ },
+
+ contains: function(item, from){
+ return this.indexOf(item, from) != -1;
+ },
+
+ extend: function(array){
+ for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
+ return this;
+ },
+
+ getLast: function(){
+ return (this.length) ? this[this.length - 1] : null;
+ },
+
+ getRandom: function(){
+ return (this.length) ? this[$random(0, this.length - 1)] : null;
+ },
+
+ include: function(item){
+ if (!this.contains(item)) this.push(item);
+ return this;
+ },
+
+ combine: function(array){
+ for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
+ return this;
+ },
+
+ erase: function(item){
+ for (var i = this.length; i--; i){
+ if (this[i] === item) this.splice(i, 1);
+ }
+ return this;
+ },
+
+ empty: function(){
+ this.length = 0;
+ return this;
+ },
+
+ flatten: function(){
+ var array = [];
+ for (var i = 0, l = this.length; i < l; i++){
+ var type = $type(this[i]);
+ if (!type) continue;
+ array = array.concat((type == 'array' || type == 'collection' || type
== 'arguments') ? Array.flatten(this[i]) : this[i]);
+ }
+ return array;
+ },
+
+ hexToRgb: function(array){
+ if (this.length != 3) return null;
+ var rgb = this.map(function(value){
+ if (value.length == 1) value += value;
+ return value.toInt(16);
+ });
+ return (array) ? rgb : 'rgb(' + rgb + ')';
+ },
+
+ rgbToHex: function(array){
+ if (this.length < 3) return null;
+ if (this.length == 4 && this[3] == 0 && !array) return
'transparent';
+ var hex = [];
+ for (var i = 0; i < 3; i++){
+ var bit = (this[i] - 0).toString(16);
+ hex.push((bit.length == 1) ? '0' + bit : bit);
+ }
+ return (array) ? hex : '#' + hex.join('');
+ }
+
+});
+
+/*
+Script: Function.js
+ Contains Function Prototypes like create, bind, pass, and delay.
+
+License:
+ MIT-style license.
+*/
+
+Function.implement({
+
+ extend: function(properties){
+ for (var property in properties) this[property] = properties[property];
+ return this;
+ },
+
+ create: function(options){
+ var self = this;
+ options = options || {};
+ return function(event){
+ var args = options.arguments;
+ args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1
: 0);
+ if (options.event) args = [event || window.event].extend(args);
+ var returns = function(){
+ return self.apply(options.bind || null, args);
+ };
+ if (options.delay) return setTimeout(returns, options.delay);
+ if (options.periodical) return setInterval(returns, options.periodical);
+ if (options.attempt) return $try(returns);
+ return returns();
+ };
+ },
+
+ pass: function(args, bind){
+ return this.create({arguments: args, bind: bind});
+ },
+
+ attempt: function(args, bind){
+ return this.create({arguments: args, bind: bind, attempt: true})();
+ },
+
+ bind: function(bind, args){
+ return this.create({bind: bind, arguments: args});
+ },
+
+ bindWithEvent: function(bind, args){
+ return this.create({bind: bind, event: true, arguments: args});
+ },
+
+ delay: function(delay, bind, args){
+ return this.create({delay: delay, bind: bind, arguments: args})();
+ },
+
+ periodical: function(interval, bind, args){
+ return this.create({periodical: interval, bind: bind, arguments: args})();
+ },
+
+ run: function(args, bind){
+ return this.apply(bind, $splat(args));
+ }
+
+});
+
+/*
+Script: Number.js
+ Contains Number Prototypes like limit, round, times, and ceil.
+
+License:
+ MIT-style license.
+*/
+
+Number.implement({
+
+ limit: function(min, max){
+ return Math.min(max, Math.max(min, this));
+ },
+
+ round: function(precision){
+ precision = Math.pow(10, precision || 0);
+ return Math.round(this * precision) / precision;
+ },
+
+ times: function(fn, bind){
+ for (var i = 0; i < this; i++) fn.call(bind, i, this);
+ },
+
+ toFloat: function(){
+ return parseFloat(this);
+ },
+
+ toInt: function(base){
+ return parseInt(this, base || 10);
+ }
+
+});
+
+Number.alias('times', 'each');
+
+(function(math){
+ var methods = {};
+ math.each(function(name){
+ if (!Number[name]) methods[name] = function(){
+ return Math[name].apply(null, [this].concat($A(arguments)));
+ };
+ });
+ Number.implement(methods);
+})(['abs', 'acos', 'asin', 'atan', 'atan2',
'ceil', 'cos', 'exp', 'floor', 'log',
'max', 'min', 'pow', 'sin', 'sqrt',
'tan']);
+
+/*
+Script: String.js
+ Contains String Prototypes like camelCase, capitalize, test, and toInt.
+
+License:
+ MIT-style license.
+*/
+
+String.implement({
+
+ test: function(regex, params){
+ return ((typeof regex == 'string') ? new RegExp(regex, params) :
regex).test(this);
+ },
+
+ contains: function(string, separator){
+ return (separator) ? (separator + this + separator).indexOf(separator + string +
separator) > -1 : this.indexOf(string) > -1;
+ },
+
+ trim: function(){
+ return this.replace(/^\s+|\s+$/g, '');
+ },
+
+ clean: function(){
+ return this.replace(/\s+/g, ' ').trim();
+ },
+
+ camelCase: function(){
+ return this.replace(/-\D/g, function(match){
+ return match.charAt(1).toUpperCase();
+ });
+ },
+
+ hyphenate: function(){
+ return this.replace(/[A-Z]/g, function(match){
+ return ('-' + match.charAt(0).toLowerCase());
+ });
+ },
+
+ capitalize: function(){
+ return this.replace(/\b[a-z]/g, function(match){
+ return match.toUpperCase();
+ });
+ },
+
+ escapeRegExp: function(){
+ return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
+ },
+
+ toInt: function(base){
+ return parseInt(this, base || 10);
+ },
+
+ toFloat: function(){
+ return parseFloat(this);
+ },
+
+ hexToRgb: function(array){
+ var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
+ return (hex) ? hex.slice(1).hexToRgb(array) : null;
+ },
+
+ rgbToHex: function(array){
+ var rgb = this.match(/\d{1,3}/g);
+ return (rgb) ? rgb.rgbToHex(array) : null;
+ },
+
+ stripScripts: function(option){
+ var scripts = '';
+ var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi,
function(){
+ scripts += arguments[1] + '\n';
+ return '';
+ });
+ if (option === true) $exec(scripts);
+ else if ($type(option) == 'function') option(scripts, text);
+ return text;
+ },
+
+ substitute: function(object, regexp){
+ return this.replace(regexp || (/\\?\{([^}]+)\}/g), function(match, name){
+ if (match.charAt(0) == '\\') return match.slice(1);
+ return (object[name] != undefined) ? object[name] : '';
+ });
+ }
+
+});
+
+/*
+Script: Hash.js
+ Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical
impossibility of extending native Objects.
+
+License:
+ MIT-style license.
+*/
+
+Hash.implement({
+
+ has: Object.prototype.hasOwnProperty,
+
+ keyOf: function(value){
+ for (var key in this){
+ if (this.hasOwnProperty(key) && this[key] === value) return key;
+ }
+ return null;
+ },
+
+ hasValue: function(value){
+ return (Hash.keyOf(this, value) !== null);
+ },
+
+ extend: function(properties){
+ Hash.each(properties, function(value, key){
+ Hash.set(this, key, value);
+ }, this);
+ return this;
+ },
+
+ combine: function(properties){
+ Hash.each(properties, function(value, key){
+ Hash.include(this, key, value);
+ }, this);
+ return this;
+ },
+
+ erase: function(key){
+ if (this.hasOwnProperty(key)) delete this[key];
+ return this;
+ },
+
+ get: function(key){
+ return (this.hasOwnProperty(key)) ? this[key] : null;
+ },
+
+ set: function(key, value){
+ if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
+ return this;
+ },
+
+ empty: function(){
+ Hash.each(this, function(value, key){
+ delete this[key];
+ }, this);
+ return this;
+ },
+
+ include: function(key, value){
+ var k = this[key];
+ if (k == undefined) this[key] = value;
+ return this;
+ },
+
+ map: function(fn, bind){
+ var results = new Hash;
+ Hash.each(this, function(value, key){
+ results.set(key, fn.call(bind, value, key, this));
+ }, this);
+ return results;
+ },
+
+ filter: function(fn, bind){
+ var results = new Hash;
+ Hash.each(this, function(value, key){
+ if (fn.call(bind, value, key, this)) results.set(key, value);
+ }, this);
+ return results;
+ },
+
+ every: function(fn, bind){
+ for (var key in this){
+ if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
+ }
+ return true;
+ },
+
+ some: function(fn, bind){
+ for (var key in this){
+ if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
+ }
+ return false;
+ },
+
+ getKeys: function(){
+ var keys = [];
+ Hash.each(this, function(value, key){
+ keys.push(key);
+ });
+ return keys;
+ },
+
+ getValues: function(){
+ var values = [];
+ Hash.each(this, function(value){
+ values.push(value);
+ });
+ return values;
+ },
+
+ toQueryString: function(base){
+ var queryString = [];
+ Hash.each(this, function(value, key){
+ if (base) key = base + '[' + key + ']';
+ var result;
+ switch ($type(value)){
+ case 'object': result = Hash.toQueryString(value, key); break;
+ case 'array':
+ var qs = {};
+ value.each(function(val, i){
+ qs[i] = val;
+ });
+ result = Hash.toQueryString(qs, key);
+ break;
+ default: result = key + '=' + encodeURIComponent(value);
+ }
+ if (value != undefined) queryString.push(result);
+ });
+
+ return queryString.join('&');
+ }
+
+});
+
+Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});
+
+/*
+Script: Event.js
+ Contains the Event Native, to make the event object completely crossbrowser.
+
+License:
+ MIT-style license.
+*/
+
+var Event = new Native({
+
+ name: 'Event',
+
+ initialize: function(event, win){
+ win = win || window;
+ var doc = win.document;
+ event = event || win.event;
+ if (event.$extended) return event;
+ this.$extended = true;
+ var type = event.type;
+ var target = event.target || event.srcElement;
+ while (target && target.nodeType == 3) target = target.parentNode;
+
+ if (type.test(/key/)){
+ var code = event.which || event.keyCode;
+ var key = Event.Keys.keyOf(code);
+ if (type == 'keydown'){
+ var fKey = code - 111;
+ if (fKey > 0 && fKey < 13) key = 'f' + fKey;
+ }
+ key = key || String.fromCharCode(code).toLowerCase();
+ } else if (type.match(/(click|mouse|menu)/i)){
+ doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html :
doc.body;
+ var page = {
+ x: event.pageX || event.clientX + doc.scrollLeft,
+ y: event.pageY || event.clientY + doc.scrollTop
+ };
+ var client = {
+ x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
+ y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
+ };
+ if (type.match(/DOMMouseScroll|mousewheel/)){
+ var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
+ }
+ var rightClick = (event.which == 3) || (event.button == 2);
+ var related = null;
+ if (type.match(/over|out/)){
+ switch (type){
+ case 'mouseover': related = event.relatedTarget || event.fromElement;
break;
+ case 'mouseout': related = event.relatedTarget || event.toElement;
+ }
+ if (!(function(){
+ while (related && related.nodeType == 3) related = related.parentNode;
+ return true;
+ }).create({attempt: Browser.Engine.gecko})()) related = false;
+ }
+ }
+
+ return $extend(this, {
+ event: event,
+ type: type,
+
+ page: page,
+ client: client,
+ rightClick: rightClick,
+
+ wheel: wheel,
+
+ relatedTarget: related,
+ target: target,
+
+ code: code,
+ key: key,
+
+ shift: event.shiftKey,
+ control: event.ctrlKey,
+ alt: event.altKey,
+ meta: event.metaKey
+ });
+ }
+
+});
+
+Event.Keys = new Hash({
+ 'enter': 13,
+ 'up': 38,
+ 'down': 40,
+ 'left': 37,
+ 'right': 39,
+ 'esc': 27,
+ 'space': 32,
+ 'backspace': 8,
+ 'tab': 9,
+ 'delete': 46
+});
+
+Event.implement({
+
+ stop: function(){
+ return this.stopPropagation().preventDefault();
+ },
+
+ stopPropagation: function(){
+ if (this.event.stopPropagation) this.event.stopPropagation();
+ else this.event.cancelBubble = true;
+ return this;
+ },
+
+ preventDefault: function(){
+ if (this.event.preventDefault) this.event.preventDefault();
+ else this.event.returnValue = false;
+ return this;
+ }
+
+});
+
+/*
+Script: Class.js
+ Contains the Class Function for easily creating, extending, and implementing reusable
Classes.
+
+License:
+ MIT-style license.
+*/
+
+var Class = new Native({
+
+ name: 'Class',
+
+ initialize: function(properties){
+ properties = properties || {};
+ var klass = function(empty){
+ for (var key in this) this[key] = $unlink(this[key]);
+ for (var mutator in Class.Mutators){
+ if (!this[mutator]) continue;
+ Class.Mutators[mutator](this, this[mutator]);
+ delete this[mutator];
+ }
+
+ this.constructor = klass;
+ if (empty === $empty) return this;
+
+ var self = (this.initialize) ? this.initialize.apply(this, arguments) : this;
+ if (this.options && this.options.initialize)
this.options.initialize.call(this);
+ return self;
+ };
+
+ $extend(klass, this);
+ klass.constructor = Class;
+ klass.prototype = properties;
+ return klass;
+ }
+
+});
+
+Class.implement({
+
+ implement: function(){
+ Class.Mutators.Implements(this.prototype, Array.slice(arguments));
+ return this;
+ }
+
+});
+
+Class.Mutators = {
+
+ Implements: function(self, klasses){
+ $splat(klasses).each(function(klass){
+ $extend(self, ($type(klass) == 'class') ? new klass($empty) : klass);
+ });
+ },
+
+ Extends: function(self, klass){
+ var instance = new klass($empty);
+ delete instance.parent;
+ delete instance.parentOf;
+
+ for (var key in instance){
+ var current = self[key], previous = instance[key];
+ if (current == undefined){
+ self[key] = previous;
+ continue;
+ }
+
+ var ctype = $type(current), ptype = $type(previous);
+ if (ctype != ptype) continue;
+
+ switch (ctype){
+ case 'function':
+ // this code will be only executed if the current browser does not support
function.caller (currently only opera).
+ // we replace the function code with brute force. Not pretty, but it will only be
executed if function.caller is not supported.
+
+ if (!arguments.callee.caller) self[key] = eval('(' +
String(current).replace(/\bthis\.parent\(\s*(\))?/g, function(full, close){
+ return 'arguments.callee._parent_.call(this' + (close || ', ');
+ }) + ')');
+
+ // end "opera" code
+ self[key]._parent_ = previous;
+ break;
+ case 'object': self[key] = $merge(previous, current);
+ }
+
+ }
+
+ self.parent = function(){
+ return arguments.callee.caller._parent_.apply(this, arguments);
+ };
+
+ self.parentOf = function(descendant){
+ return descendant._parent_.apply(this, Array.slice(arguments, 1));
+ };
+ }
+
+};
+
+
+/*
+Script: Class.Extras.js
+ Contains Utility Classes that can be implemented into your own Classes to ease the
execution of many common tasks.
+
+License:
+ MIT-style license.
+*/
+
+var Chain = new Class({
+
+ chain: function(){
+ this.$chain = (this.$chain || []).extend(arguments);
+ return this;
+ },
+
+ callChain: function(){
+ return (this.$chain && this.$chain.length) ? this.$chain.shift().apply(this,
arguments) : false;
+ },
+
+ clearChain: function(){
+ if (this.$chain) this.$chain.empty();
+ return this;
+ }
+
+});
+
+var Events = new Class({
+
+ addEvent: function(type, fn, internal){
+ type = Events.removeOn(type);
+ if (fn != $empty){
+ this.$events = this.$events || {};
+ this.$events[type] = this.$events[type] || [];
+ this.$events[type].include(fn);
+ if (internal) fn.internal = true;
+ }
+ return this;
+ },
+
+ addEvents: function(events){
+ for (var type in events) this.addEvent(type, events[type]);
+ return this;
+ },
+
+ fireEvent: function(type, args, delay){
+ type = Events.removeOn(type);
+ if (!this.$events || !this.$events[type]) return this;
+ this.$events[type].each(function(fn){
+ fn.create({'bind': this, 'delay': delay, 'arguments':
args})();
+ }, this);
+ return this;
+ },
+
+ removeEvent: function(type, fn){
+ type = Events.removeOn(type);
+ if (!this.$events || !this.$events[type]) return this;
+ if (!fn.internal) this.$events[type].erase(fn);
+ return this;
+ },
+
+ removeEvents: function(type){
+ for (var e in this.$events){
+ if (type && type != e) continue;
+ var fns = this.$events[e];
+ for (var i = fns.length; i--; i) this.removeEvent(e, fns[i]);
+ }
+ return this;
+ }
+
+});
+
+Events.removeOn = function(string){
+ return string.replace(/^on([A-Z])/, function(full, first) {
+ return first.toLowerCase();
+ });
+};
+
+var Options = new Class({
+
+ setOptions: function(){
+ this.options = $merge.run([this.options].extend(arguments));
+ if (!this.addEvent) return this;
+ for (var option in this.options){
+ if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option))
continue;
+ this.addEvent(option, this.options[option]);
+ delete this.options[option];
+ }
+ return this;
+ }
+
+});
+
+/*
+Script: Element.js
+ One of the most important items in MooTools. Contains the dollar function, the dollars
function, and an handful of cross-browser,
+ time-saver methods to let you easily work with HTML Elements.
+
+License:
+ MIT-style license.
+*/
+
+Document.implement({
+
+ newElement: function(tag, props){
+ if (Browser.Engine.trident && props){
+ ['name', 'type', 'checked'].each(function(attribute){
+ if (!props[attribute]) return;
+ tag += ' ' + attribute + '="' + props[attribute] +
'"';
+ if (attribute != 'checked') delete props[attribute];
+ });
+ tag = '<' + tag + '>';
+ }
+ return $.element(this.createElement(tag)).set(props);
+ },
+
+ newTextNode: function(text){
+ return this.createTextNode(text);
+ },
+
+ getDocument: function(){
+ return this;
+ },
+
+ getWindow: function(){
+ return this.defaultView || this.parentWindow;
+ },
+
+ purge: function(){
+ var elements = this.getElementsByTagName('*');
+ for (var i = 0, l = elements.length; i < l; i++) Browser.freeMem(elements[i]);
+ }
+
+});
+
+var Element = new Native({
+
+ name: 'Element',
+
+ legacy: window.Element,
+
+ initialize: function(tag, props){
+ var konstructor = Element.Constructors.get(tag);
+ if (konstructor) return konstructor(props);
+ if (typeof tag == 'string') return document.newElement(tag, props);
+ return $(tag).set(props);
+ },
+
+ afterImplement: function(key, value){
+ if (!Array[key]) Elements.implement(key, Elements.multi(key));
+ Element.Prototype[key] = value;
+ }
+
+});
+
+Element.Prototype = {$family: {name: 'element'}};
+
+Element.Constructors = new Hash;
+
+var IFrame = new Native({
+
+ name: 'IFrame',
+
+ generics: false,
+
+ initialize: function(){
+ var params = Array.link(arguments, {properties: Object.type, iframe: $defined});
+ var props = params.properties || {};
+ var iframe = $(params.iframe) || false;
+ var onload = props.onload || $empty;
+ delete props.onload;
+ props.id = props.name = $pick(props.id, props.name, iframe.id, iframe.name,
'IFrame_' + $time());
+ iframe = new Element(iframe || 'iframe', props);
+ var onFrameLoad = function(){
+ var host = $try(function(){
+ return iframe.contentWindow.location.host;
+ });
+ if (host && host == window.location.host){
+ var win = new Window(iframe.contentWindow);
+ var doc = new Document(iframe.contentWindow.document);
+ $extend(win.Element.prototype, Element.Prototype);
+ }
+ onload.call(iframe.contentWindow, iframe.contentWindow.document);
+ };
+ (!window.frames[props.id]) ? iframe.addListener('load', onFrameLoad) :
onFrameLoad();
+ return iframe;
+ }
+
+});
+
+var Elements = new Native({
+
+ initialize: function(elements, options){
+ options = $extend({ddup: true, cash: true}, options);
+ elements = elements || [];
+ if (options.ddup || options.cash){
+ var uniques = {}, returned = [];
+ for (var i = 0, l = elements.length; i < l; i++){
+ var el = $.element(elements[i], !options.cash);
+ if (options.ddup){
+ if (uniques[el.uid]) continue;
+ uniques[el.uid] = true;
+ }
+ returned.push(el);
+ }
+ elements = returned;
+ }
+ return (options.cash) ? $extend(elements, this) : elements;
+ }
+
+});
+
+Elements.implement({
+
+ filter: function(filter, bind){
+ if (!filter) return this;
+ return new Elements(Array.filter(this, (typeof filter == 'string') ?
function(item){
+ return item.match(filter);
+ } : filter, bind));
+ }
+
+});
+
+Elements.multi = function(property){
+ return function(){
+ var items = [];
+ var elements = true;
+ for (var i = 0, j = this.length; i < j; i++){
+ var returns = this[i][property].apply(this[i], arguments);
+ items.push(returns);
+ if (elements) elements = ($type(returns) == 'element');
+ }
+ return (elements) ? new Elements(items) : items;
+ };
+};
+
+Window.implement({
+
+ $: function(el, nocash){
+ if (el && el.$family && el.uid) return el;
+ var type = $type(el);
+ return ($[type]) ? $[type](el, nocash, this.document) : null;
+ },
+
+ $$: function(selector){
+ if (arguments.length == 1 && typeof selector == 'string') return
this.document.getElements(selector);
+ var elements = [];
+ var args = Array.flatten(arguments);
+ for (var i = 0, l = args.length; i < l; i++){
+ var item = args[i];
+ switch ($type(item)){
+ case 'element': item = [item]; break;
+ case 'string': item = this.document.getElements(item, true); break;
+ default: item = false;
+ }
+ if (item) elements.extend(item);
+ }
+ return new Elements(elements);
+ },
+
+ getDocument: function(){
+ return this.document;
+ },
+
+ getWindow: function(){
+ return this;
+ }
+
+});
+
+$.string = function(id, nocash, doc){
+ id = doc.getElementById(id);
+ return (id) ? $.element(id, nocash) : null;
+};
+
+$.element = function(el, nocash){
+ $uid(el);
+ if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
+ var proto = Element.Prototype;
+ for (var p in proto) el[p] = proto[p];
+ };
+ return el;
+};
+
+$.object = function(obj, nocash, doc){
+ if (obj.toElement) return $.element(obj.toElement(doc), nocash);
+ return null;
+};
+
+$.textnode = $.whitespace = $.window = $.document = $arguments(0);
+
+Native.implement([Element, Document], {
+
+ getElement: function(selector, nocash){
+ return $(this.getElements(selector, true)[0] || null, nocash);
+ },
+
+ getElements: function(tags, nocash){
+ tags = tags.split(',');
+ var elements = [];
+ var ddup = (tags.length > 1);
+ tags.each(function(tag){
+ var partial = this.getElementsByTagName(tag.trim());
+ (ddup) ? elements.extend(partial) : elements = partial;
+ }, this);
+ return new Elements(elements, {ddup: ddup, cash: !nocash});
+ }
+
+});
+
+Element.Storage = {
+
+ get: function(uid){
+ return (this[uid] || (this[uid] = {}));
+ }
+
+};
+
+Element.Inserters = new Hash({
+
+ before: function(context, element){
+ if (element.parentNode) element.parentNode.insertBefore(context, element);
+ },
+
+ after: function(context, element){
+ if (!element.parentNode) return;
+ var next = element.nextSibling;
+ (next) ? element.parentNode.insertBefore(context, next) :
element.parentNode.appendChild(context);
+ },
+
+ bottom: function(context, element){
+ element.appendChild(context);
+ },
+
+ top: function(context, element){
+ var first = element.firstChild;
+ (first) ? element.insertBefore(context, first) : element.appendChild(context);
+ }
+
+});
+
+Element.Inserters.inside = Element.Inserters.bottom;
+
+Element.Inserters.each(function(value, key){
+
+ var Key = key.capitalize();
+
+ Element.implement('inject' + Key, function(el){
+ value(this, $(el, true));
+ return this;
+ });
+
+ Element.implement('grab' + Key, function(el){
+ value($(el, true), this);
+ return this;
+ });
+
+});
+
+Element.implement({
+
+ getDocument: function(){
+ return this.ownerDocument;
+ },
+
+ getWindow: function(){
+ return this.ownerDocument.getWindow();
+ },
+
+ getElementById: function(id, nocash){
+ var el = this.ownerDocument.getElementById(id);
+ if (!el) return null;
+ for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
+ if (!parent) return null;
+ }
+ return $.element(el, nocash);
+ },
+
+ set: function(prop, value){
+ switch ($type(prop)){
+ case 'object':
+ for (var p in prop) this.set(p, prop[p]);
+ break;
+ case 'string':
+ var property = Element.Properties.get(prop);
+ (property && property.set) ? property.set.apply(this, Array.slice(arguments,
1)) : this.setProperty(prop, value);
+ }
+ return this;
+ },
+
+ get: function(prop){
+ var property = Element.Properties.get(prop);
+ return (property && property.get) ? property.get.apply(this,
Array.slice(arguments, 1)) : this.getProperty(prop);
+ },
+
+ erase: function(prop){
+ var property = Element.Properties.get(prop);
+ (property && property.erase) ? property.erase.apply(this,
Array.slice(arguments, 1)) : this.removeProperty(prop);
+ return this;
+ },
+
+ match: function(tag){
+ return (!tag || Element.get(this, 'tag') == tag);
+ },
+
+ inject: function(el, where){
+ Element.Inserters.get(where || 'bottom')(this, $(el, true));
+ return this;
+ },
+
+ wraps: function(el, where){
+ el = $(el, true);
+ return this.replaces(el).grab(el, where);
+ },
+
+ grab: function(el, where){
+ Element.Inserters.get(where || 'bottom')($(el, true), this);
+ return this;
+ },
+
+ appendText: function(text, where){
+ return this.grab(this.getDocument().newTextNode(text), where);
+ },
+
+ adopt: function(){
+ Array.flatten(arguments).each(function(element){
+ element = $(element, true);
+ if (element) this.appendChild(element);
+ }, this);
+ return this;
+ },
+
+ dispose: function(){
+ return (this.parentNode) ? this.parentNode.removeChild(this) : this;
+ },
+
+ clone: function(contents, keepid){
+ switch ($type(this)){
+ case 'element':
+ var attributes = {};
+ for (var j = 0, l = this.attributes.length; j < l; j++){
+ var attribute = this.attributes[j], key = attribute.nodeName.toLowerCase();
+ if (Browser.Engine.trident && (/input/i).test(this.tagName) &&
(/width|height/).test(key)) continue;
+ var value = (key == 'style' && this.style) ? this.style.cssText :
attribute.nodeValue;
+ if (!$chk(value) || key == 'uid' || (key == 'id' &&
!keepid)) continue;
+ if (value != 'inherit' && ['string',
'number'].contains($type(value))) attributes[key] = value;
+ }
+ var element = new Element(this.nodeName.toLowerCase(), attributes);
+ if (contents !== false){
+ for (var i = 0, k = this.childNodes.length; i < k; i++){
+ var child = Element.clone(this.childNodes[i], true, keepid);
+ if (child) element.grab(child);
+ }
+ }
+ return element;
+ case 'textnode': return document.newTextNode(this.nodeValue);
+ }
+ return null;
+ },
+
+ replaces: function(el){
+ el = $(el, true);
+ el.parentNode.replaceChild(this, el);
+ return this;
+ },
+
+ hasClass: function(className){
+ return this.className.contains(className, ' ');
+ },
+
+ addClass: function(className){
+ if (!this.hasClass(className)) this.className = (this.className + ' ' +
className).clean();
+ return this;
+ },
+
+ removeClass: function(className){
+ this.className = this.className.replace(new RegExp('(^|\\s)' + className +
'(?:\\s|$)'), '$1').clean();
+ return this;
+ },
+
+ toggleClass: function(className){
+ return this.hasClass(className) ? this.removeClass(className) :
this.addClass(className);
+ },
+
+ getComputedStyle: function(property){
+ if (this.currentStyle) return this.currentStyle[property.camelCase()];
+ var computed = this.getWindow().getComputedStyle(this, null);
+ return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
+ },
+
+ empty: function(){
+ $A(this.childNodes).each(function(node){
+ Browser.freeMem(node);
+ Element.empty(node);
+ Element.dispose(node);
+ }, this);
+ return this;
+ },
+
+ destroy: function(){
+ Browser.freeMem(this.empty().dispose());
+ return null;
+ },
+
+ getSelected: function(){
+ return new Elements($A(this.options).filter(function(option){
+ return option.selected;
+ }));
+ },
+
+ toQueryString: function(){
+ var queryString = [];
+ this.getElements('input, select, textarea').each(function(el){
+ if (!el.name || el.disabled) return;
+ var value = (el.tagName.toLowerCase() == 'select') ?
Element.getSelected(el).map(function(opt){
+ return opt.value;
+ }) : ((el.type == 'radio' || el.type == 'checkbox') &&
!el.checked) ? null : el.value;
+ $splat(value).each(function(val){
+ if (val) queryString.push(el.name + '=' + encodeURIComponent(val));
+ });
+ });
+ return queryString.join('&');
+ },
+
+ getProperty: function(attribute){
+ var EA = Element.Attributes, key = EA.Props[attribute];
+ var value = (key) ? this[key] : this.getAttribute(attribute, 2);
+ return (EA.Bools[attribute]) ? !!value : (key) ? value : value || null;
+ },
+
+ getProperties: function(){
+ var args = $A(arguments);
+ return args.map(function(attr){
+ return this.getProperty(attr);
+ }, this).associate(args);
+ },
+
+ setProperty: function(attribute, value){
+ var EA = Element.Attributes, key = EA.Props[attribute], hasValue = $defined(value);
+ if (key && EA.Bools[attribute]) value = (value || !hasValue) ? true : false;
+ else if (!hasValue) return this.removeProperty(attribute);
+ (key) ? this[key] = value : this.setAttribute(attribute, value);
+ return this;
+ },
+
+ setProperties: function(attributes){
+ for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
+ return this;
+ },
+
+ removeProperty: function(attribute){
+ var EA = Element.Attributes, key = EA.Props[attribute], isBool = (key &&
EA.Bools[attribute]);
+ (key) ? this[key] = (isBool) ? false : '' : this.removeAttribute(attribute);
+ return this;
+ },
+
+ removeProperties: function(){
+ Array.each(arguments, this.removeProperty, this);
+ return this;
+ }
+
+});
+
+(function(){
+
+var walk = function(element, walk, start, match, all, nocash){
+ var el = element[start || walk];
+ var elements = [];
+ while (el){
+ if (el.nodeType == 1 && (!match || Element.match(el, match))){
+ elements.push(el);
+ if (!all) break;
+ }
+ el = el[walk];
+ }
+ return (all) ? new Elements(elements, {ddup: false, cash: !nocash}) : $(elements[0],
nocash);
+};
+
+Element.implement({
+
+ getPrevious: function(match, nocash){
+ return walk(this, 'previousSibling', null, match, false, nocash);
+ },
+
+ getAllPrevious: function(match, nocash){
+ return walk(this, 'previousSibling', null, match, true, nocash);
+ },
+
+ getNext: function(match, nocash){
+ return walk(this, 'nextSibling', null, match, false, nocash);
+ },
+
+ getAllNext: function(match, nocash){
+ return walk(this, 'nextSibling', null, match, true, nocash);
+ },
+
+ getFirst: function(match, nocash){
+ return walk(this, 'nextSibling', 'firstChild', match, false, nocash);
+ },
+
+ getLast: function(match, nocash){
+ return walk(this, 'previousSibling', 'lastChild', match, false,
nocash);
+ },
+
+ getParent: function(match, nocash){
+ return walk(this, 'parentNode', null, match, false, nocash);
+ },
+
+ getParents: function(match, nocash){
+ return walk(this, 'parentNode', null, match, true, nocash);
+ },
+
+ getChildren: function(match, nocash){
+ return walk(this, 'nextSibling', 'firstChild', match, true, nocash);
+ },
+
+ hasChild: function(el){
+ el = $(el, true);
+ return (!!el && $A(this.getElementsByTagName(el.tagName)).contains(el));
+ }
+
+});
+
+})();
+
+Element.Properties = new Hash;
+
+Element.Properties.style = {
+
+ set: function(style){
+ this.style.cssText = style;
+ },
+
+ get: function(){
+ return this.style.cssText;
+ },
+
+ erase: function(){
+ this.style.cssText = '';
+ }
+
+};
+
+Element.Properties.tag = {get: function(){
+ return this.tagName.toLowerCase();
+}};
+
+Element.Properties.href = {get: function(){
+ return (!this.href) ? null : this.href.replace(new RegExp('^' +
document.location.protocol + '\/\/' + document.location.host), '');
+}};
+
+Element.Properties.html = {set: function(){
+ return this.innerHTML = Array.flatten(arguments).join('');
+}};
+
+Native.implement([Element, Window, Document], {
+
+ addListener: function(type, fn){
+ if (this.addEventListener) this.addEventListener(type, fn, false);
+ else this.attachEvent('on' + type, fn);
+ return this;
+ },
+
+ removeListener: function(type, fn){
+ if (this.removeEventListener) this.removeEventListener(type, fn, false);
+ else this.detachEvent('on' + type, fn);
+ return this;
+ },
+
+ retrieve: function(property, dflt){
+ var storage = Element.Storage.get(this.uid);
+ var prop = storage[property];
+ if ($defined(dflt) && !$defined(prop)) prop = storage[property] = dflt;
+ return $pick(prop);
+ },
+
+ store: function(property, value){
+ var storage = Element.Storage.get(this.uid);
+ storage[property] = value;
+ return this;
+ },
+
+ eliminate: function(property){
+ var storage = Element.Storage.get(this.uid);
+ delete storage[property];
+ return this;
+ }
+
+});
+
+Element.Attributes = new Hash({
+ Props: {'html': 'innerHTML', 'class': 'className',
'for': 'htmlFor', 'text': (Browser.Engine.trident) ?
'innerText' : 'textContent'},
+ Bools: ['compact', 'nowrap', 'ismap', 'declare',
'noshade', 'checked', 'disabled', 'readonly',
'multiple', 'selected', 'noresize', 'defer'],
+ Camels: ['value', 'accessKey', 'cellPadding',
'cellSpacing', 'colSpan', 'frameBorder', 'maxLength',
'readOnly', 'rowSpan', 'tabIndex', 'useMap']
+});
+
+Browser.freeMem = function(item){
+ if (!item) return;
+ if (Browser.Engine.trident && (/object/i).test(item.tagName)){
+ for (var p in item){
+ if (typeof item[p] == 'function') item[p] = $empty;
+ }
+ Element.dispose(item);
+ }
+ if (item.uid && item.removeEvents) item.removeEvents();
+};
+
+(function(EA){
+
+ var EAB = EA.Bools, EAC = EA.Camels;
+ EA.Bools = EAB = EAB.associate(EAB);
+ Hash.extend(Hash.combine(EA.Props, EAB), EAC.associate(EAC.map(function(v){
+ return v.toLowerCase();
+ })));
+ EA.erase('Camels');
+
+})(Element.Attributes);
+
+window.addListener('unload', function(){
+ window.removeListener('unload', arguments.callee);
+ document.purge();
+ if (Browser.Engine.trident) CollectGarbage();
+});
+
+/*
+Script: Element.Event.js
+ Contains Element methods for dealing with events, and custom Events.
+
+License:
+ MIT-style license.
+*/
+
+Element.Properties.events = {set: function(events){
+ this.addEvents(events);
+}};
+
+Native.implement([Element, Window, Document], {
+
+ addEvent: function(type, fn){
+ var events = this.retrieve('events', {});
+ events[type] = events[type] || {'keys': [], 'values': []};
+ if (events[type].keys.contains(fn)) return this;
+ events[type].keys.push(fn);
+ var realType = type, custom = Element.Events.get(type), condition = fn, self = this;
+ if (custom){
+ if (custom.onAdd) custom.onAdd.call(this, fn);
+ if (custom.condition){
+ condition = function(event){
+ if (custom.condition.call(this, event)) return fn.call(this, event);
+ return false;
+ };
+ }
+ realType = custom.base || realType;
+ }
+ var defn = function(){
+ return fn.call(self);
+ };
+ var nativeEvent = Element.NativeEvents[realType] || 0;
+ if (nativeEvent){
+ if (nativeEvent == 2){
+ defn = function(event){
+ event = new Event(event, self.getWindow());
+ if (condition.call(self, event) === false) event.stop();
+ };
+ }
+ this.addListener(realType, defn);
+ }
+ events[type].values.push(defn);
+ return this;
+ },
+
+ removeEvent: function(type, fn){
+ var events = this.retrieve('events');
+ if (!events || !events[type]) return this;
+ var pos = events[type].keys.indexOf(fn);
+ if (pos == -1) return this;
+ var key = events[type].keys.splice(pos, 1)[0];
+ var value = events[type].values.splice(pos, 1)[0];
+ var custom = Element.Events.get(type);
+ if (custom){
+ if (custom.onRemove) custom.onRemove.call(this, fn);
+ type = custom.base || type;
+ }
+ return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this;
+ },
+
+ addEvents: function(events){
+ for (var event in events) this.addEvent(event, events[event]);
+ return this;
+ },
+
+ removeEvents: function(type){
+ var events = this.retrieve('events');
+ if (!events) return this;
+ if (!type){
+ for (var evType in events) this.removeEvents(evType);
+ events = null;
+ } else if (events[type]){
+ while (events[type].keys[0]) this.removeEvent(type, events[type].keys[0]);
+ events[type] = null;
+ }
+ return this;
+ },
+
+ fireEvent: function(type, args, delay){
+ var events = this.retrieve('events');
+ if (!events || !events[type]) return this;
+ events[type].keys.each(function(fn){
+ fn.create({'bind': this, 'delay': delay, 'arguments':
args})();
+ }, this);
+ return this;
+ },
+
+ cloneEvents: function(from, type){
+ from = $(from);
+ var fevents = from.retrieve('events');
+ if (!fevents) return this;
+ if (!type){
+ for (var evType in fevents) this.cloneEvents(from, evType);
+ } else if (fevents[type]){
+ fevents[type].keys.each(function(fn){
+ this.addEvent(type, fn);
+ }, this);
+ }
+ return this;
+ }
+
+});
+
+Element.NativeEvents = {
+ click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
+ mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
+ mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
+ keydown: 2, keypress: 2, keyup: 2, //keyboard
+ focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements
+ load: 1, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1,
readystatechange: 1, //window
+ error: 1, abort: 1, scroll: 1 //misc
+};
+
+(function(){
+
+var $check = function(event){
+ var related = event.relatedTarget;
+ if (related == undefined) return true;
+ if (related === false) return false;
+ return ($type(this) != 'document' && related != this &&
related.prefix != 'xul' && !this.hasChild(related));
+};
+
+Element.Events = new Hash({
+
+ mouseenter: {
+ base: 'mouseover',
+ condition: $check
+ },
+
+ mouseleave: {
+ base: 'mouseout',
+ condition: $check
+ },
+
+ mousewheel: {
+ base: (Browser.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel'
+ }
+
+});
+
+})();
+
+/*
+Script: Element.Style.js
+ Contains methods for interacting with the styles of Elements in a fashionable way.
+
+License:
+ MIT-style license.
+*/
+
+Element.Properties.styles = {set: function(styles){
+ this.setStyles(styles);
+}};
+
+Element.Properties.opacity = {
+
+ set: function(opacity, novisibility){
+ if (!novisibility){
+ if (opacity == 0){
+ if (this.style.visibility != 'hidden') this.style.visibility =
'hidden';
+ } else {
+ if (this.style.visibility != 'visible') this.style.visibility =
'visible';
+ }
+ }
+ if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1;
+ if (Browser.Engine.trident) this.style.filter = (opacity == 1) ? '' :
'alpha(opacity=' + opacity * 100 + ')';
+ this.style.opacity = opacity;
+ this.store('opacity', opacity);
+ },
+
+ get: function(){
+ return this.retrieve('opacity', 1);
+ }
+
+};
+
+Element.implement({
+
+ setOpacity: function(value){
+ return this.set('opacity', value, true);
+ },
+
+ getOpacity: function(){
+ return this.get('opacity');
+ },
+
+ setStyle: function(property, value){
+ switch (property){
+ case 'opacity': return this.set('opacity', parseFloat(value));
+ case 'float': property = (Browser.Engine.trident) ? 'styleFloat' :
'cssFloat';
+ }
+ property = property.camelCase();
+ if ($type(value) != 'string'){
+ var map = (Element.Styles.get(property) || '(a)').split(' ');
+ value = $splat(value).map(function(val, i){
+ if (!map[i]) return '';
+ return ($type(val) == 'number') ? map[i].replace('@',
Math.round(val)) : val;
+ }).join(' ');
+ } else if (value == String(Number(value))){
+ value = Math.round(value);
+ }
+ this.style[property] = value;
+ return this;
+ },
+
+ getStyle: function(property){
+ switch (property){
+ case 'opacity': return this.get('opacity');
+ case 'float': property = (Browser.Engine.trident) ? 'styleFloat' :
'cssFloat';
+ }
+ property = property.camelCase();
+ var result = this.style[property];
+ if (!$chk(result)){
+ result = [];
+ for (var style in Element.ShortStyles){
+ if (property != style) continue;
+ for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
+ return result.join(' ');
+ }
+ result = this.getComputedStyle(property);
+ }
+ if (result){
+ result = String(result);
+ var color = result.match(/rgba?\([\d\s,]+\)/);
+ if (color) result = result.replace(color[0], color[0].rgbToHex());
+ }
+ if (Browser.Engine.presto || (Browser.Engine.trident &&
!$chk(parseInt(result)))){
+ if (property.test(/^(height|width)$/)){
+ var values = (property == 'width') ? ['left', 'right'] :
['top', 'bottom'], size = 0;
+ values.each(function(value){
+ size += this.getStyle('border-' + value + '-width').toInt() +
this.getStyle('padding-' + value).toInt();
+ }, this);
+ return this['offset' + property.capitalize()] - size + 'px';
+ }
+ if (Browser.Engine.presto && String(result).test('px')) return
result;
+ if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
+ }
+ return result;
+ },
+
+ setStyles: function(styles){
+ for (var style in styles) this.setStyle(style, styles[style]);
+ return this;
+ },
+
+ getStyles: function(){
+ var result = {};
+ Array.each(arguments, function(key){
+ result[key] = this.getStyle(key);
+ }, this);
+ return result;
+ }
+
+});
+
+Element.Styles = new Hash({
+ left: '@px', top: '@px', bottom: '@px', right: '@px',
+ width: '@px', height: '@px', maxWidth: '@px', maxHeight:
'@px', minWidth: '@px', minHeight: '@px',
+ backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color:
'rgb(@, @, @)',
+ fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip:
'rect(@px @px @px @px)',
+ margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px
@ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
+ borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor:
'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
+ zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent:
'@px', opacity: '@'
+});
+
+Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle:
{}, borderColor: {}};
+
+['Top', 'Right', 'Bottom',
'Left'].each(function(direction){
+ var Short = Element.ShortStyles;
+ var All = Element.Styles;
+ ['margin', 'padding'].each(function(style){
+ var sd = style + direction;
+ Short[style][sd] = All[sd] = '@px';
+ });
+ var bd = 'border' + direction;
+ Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
+ var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
+ Short[bd] = {};
+ Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
+ Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
+ Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
+});
+
+
+/*
+Script: Element.Dimensions.js
+ Contains methods to work with size, scroll, or positioning of Elements and the window
object.
+
+License:
+ MIT-style license.
+
+Credits:
+ - Element positioning based on the [
qooxdoo](http://qooxdoo.org/) code and smart browser
fixes, [LGPL
License](http://www.gnu.org/licenses/lgpl.html).
+ - Viewport dimensions based on [
YUI](http://developer.yahoo.com/yui/) code, [BSD
License](http://developer.yahoo.com/yui/license.html).
+*/
+
+(function(){
+
+Element.implement({
+
+ scrollTo: function(x, y){
+ if (isBody(this)){
+ this.getWindow().scrollTo(x, y);
+ } else {
+ this.scrollLeft = x;
+ this.scrollTop = y;
+ }
+ return this;
+ },
+
+ getSize: function(){
+ if (isBody(this)) return this.getWindow().getSize();
+ return {x: this.offsetWidth, y: this.offsetHeight};
+ },
+
+ getScrollSize: function(){
+ if (isBody(this)) return this.getWindow().getScrollSize();
+ return {x: this.scrollWidth, y: this.scrollHeight};
+ },
+
+ getScroll: function(){
+ if (isBody(this)) return this.getWindow().getScroll();
+ return {x: this.scrollLeft, y: this.scrollTop};
+ },
+
+ getScrolls: function(){
+ var element = this, position = {x: 0, y: 0};
+ while (element && !isBody(element)){
+ position.x += element.scrollLeft;
+ position.y += element.scrollTop;
+ element = element.parentNode;
+ }
+ return position;
+ },
+
+ getOffsetParent: function(){
+ var element = this;
+ if (isBody(element)) return null;
+ if (!Browser.Engine.trident) return element.offsetParent;
+ while ((element = element.parentNode) && !isBody(element)){
+ if (styleString(element, 'position') != 'static') return element;
+ }
+ return null;
+ },
+
+ getOffsets: function(){
+ var element = this, position = {x: 0, y: 0};
+ if (isBody(this)) return position;
+
+ while (element && !isBody(element)){
+ position.x += element.offsetLeft;
+ position.y += element.offsetTop;
+
+ if (Browser.Engine.gecko){
+ if (!borderBox(element)){
+ position.x += leftBorder(element);
+ position.y += topBorder(element);
+ }
+ var parent = element.parentNode;
+ if (parent && styleString(parent, 'overflow') != 'visible'){
+ position.x += leftBorder(parent);
+ position.y += topBorder(parent);
+ }
+ } else if (element != this && (Browser.Engine.trident ||
Browser.Engine.webkit)){
+ position.x += leftBorder(element);
+ position.y += topBorder(element);
+ }
+
+ element = element.offsetParent;
+ if (Browser.Engine.trident){
+ while (element && !element.currentStyle.hasLayout) element =
element.offsetParent;
+ }
+ }
+ if (Browser.Engine.gecko && !borderBox(this)){
+ position.x -= leftBorder(this);
+ position.y -= topBorder(this);
+ }
+ return position;
+ },
+
+ getPosition: function(relative){
+ if (isBody(this)) return {x: 0, y: 0};
+ var offset = this.getOffsets(), scroll = this.getScrolls();
+ var position = {x: offset.x - scroll.x, y: offset.y - scroll.y};
+ var relativePosition = (relative && (relative = $(relative))) ?
relative.getPosition() : {x: 0, y: 0};
+ return {x: position.x - relativePosition.x, y: position.y - relativePosition.y};
+ },
+
+ getCoordinates: function(element){
+ if (isBody(this)) return this.getWindow().getCoordinates();
+ var position = this.getPosition(element), size = this.getSize();
+ var obj = {left: position.x, top: position.y, width: size.x, height: size.y};
+ obj.right = obj.left + obj.width;
+ obj.bottom = obj.top + obj.height;
+ return obj;
+ },
+
+ computePosition: function(obj){
+ return {left: obj.x - styleNumber(this, 'margin-left'), top: obj.y -
styleNumber(this, 'margin-top')};
+ },
+
+ position: function(obj){
+ return this.setStyles(this.computePosition(obj));
+ }
+
+});
+
+Native.implement([Document, Window], {
+
+ getSize: function(){
+ var win = this.getWindow();
+ if (Browser.Engine.presto || Browser.Engine.webkit) return {x: win.innerWidth, y:
win.innerHeight};
+ var doc = getCompatElement(this);
+ return {x: doc.clientWidth, y: doc.clientHeight};
+ },
+
+ getScroll: function(){
+ var win = this.getWindow();
+ var doc = getCompatElement(this);
+ return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
+ },
+
+ getScrollSize: function(){
+ var doc = getCompatElement(this);
+ var min = this.getSize();
+ return {x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y)};
+ },
+
+ getPosition: function(){
+ return {x: 0, y: 0};
+ },
+
+ getCoordinates: function(){
+ var size = this.getSize();
+ return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width:
size.x};
+ }
+
+});
+
+// private methods
+
+var styleString = Element.getComputedStyle;
+
+function styleNumber(element, style){
+ return styleString(element, style).toInt() || 0;
+};
+
+function borderBox(element){
+ return styleString(element, '-moz-box-sizing') == 'border-box';
+};
+
+function topBorder(element){
+ return styleNumber(element, 'border-top-width');
+};
+
+function leftBorder(element){
+ return styleNumber(element, 'border-left-width');
+};
+
+function isBody(element){
+ return (/^(?:body|html)$/i).test(element.tagName);
+};
+
+function getCompatElement(element){
+ var doc = element.getDocument();
+ return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html :
doc.body;
+};
+
+})();
+
+//aliases
+
+Native.implement([Window, Document, Element], {
+
+ getHeight: function(){
+ return this.getSize().y;
+ },
+
+ getWidth: function(){
+ return this.getSize().x;
+ },
+
+ getScrollTop: function(){
+ return this.getScroll().y;
+ },
+
+ getScrollLeft: function(){
+ return this.getScroll().x;
+ },
+
+ getScrollHeight: function(){
+ return this.getScrollSize().y;
+ },
+
+ getScrollWidth: function(){
+ return this.getScrollSize().x;
+ },
+
+ getTop: function(){
+ return this.getPosition().y;
+ },
+
+ getLeft: function(){
+ return this.getPosition().x;
+ }
+
+});
+
+/*
+Script: Selectors.js
+ Adds advanced CSS Querying capabilities for targeting elements. Also includes
pseudoselectors support.
+
+License:
+ MIT-style license.
+*/
+
+Native.implement([Document, Element], {
+
+ getElements: function(expression, nocash){
+ expression = expression.split(',');
+ var items, local = {};
+ for (var i = 0, l = expression.length; i < l; i++){
+ var selector = expression[i], elements = Selectors.Utils.search(this, selector,
local);
+ if (i != 0 && elements.item) elements = $A(elements);
+ items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) :
items.concat(elements);
+ }
+ return new Elements(items, {ddup: (expression.length > 1), cash: !nocash});
+ }
+
+});
+
+Element.implement({
+
+ match: function(selector){
+ if (!selector) return true;
+ var tagid = Selectors.Utils.parseTagAndID(selector);
+ var tag = tagid[0], id = tagid[1];
+ if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return
false;
+ var parsed = Selectors.Utils.parseSelector(selector);
+ return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
+ }
+
+});
+
+var Selectors = {Cache: {nth: {}, parsed: {}}};
+
+Selectors.RegExps = {
+ id: (/#([\w-]+)/),
+ tag: (/^(\w+|\*)/),
+ quick: (/^(\w+|\*)$/),
+ splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),
+ combined:
(/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)["']?(.*?)["']?)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)
+};
+
+Selectors.Utils = {
+
+ chk: function(item, uniques){
+ if (!uniques) return true;
+ var uid = $uid(item);
+ if (!uniques[uid]) return uniques[uid] = true;
+ return false;
+ },
+
+ parseNthArgument: function(argument){
+ if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument];
+ var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);
+ if (!parsed) return false;
+ var inta = parseInt(parsed[1]);
+ var a = (inta || inta === 0) ? inta : 1;
+ var special = parsed[2] || false;
+ var b = parseInt(parsed[3]) || 0;
+ if (a != 0){
+ b--;
+ while (b < 1) b += a;
+ while (b >= a) b -= a;
+ } else {
+ a = b;
+ special = 'index';
+ }
+ switch (special){
+ case 'n': parsed = {a: a, b: b, special: 'n'}; break;
+ case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break;
+ case 'even': parsed = {a: 2, b: 1, special: 'n'}; break;
+ case 'first': parsed = {a: 0, special: 'index'}; break;
+ case 'last': parsed = {special: 'last-child'}; break;
+ case 'only': parsed = {special: 'only-child'}; break;
+ default: parsed = {a: (a - 1), special: 'index'};
+ }
+
+ return Selectors.Cache.nth[argument] = parsed;
+ },
+
+ parseSelector: function(selector){
+ if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector];
+ var m, parsed = {classes: [], pseudos: [], attributes: []};
+ while ((m = Selectors.RegExps.combined.exec(selector))){
+ var cn = m[1], an = m[2], ao = m[3], av = m[4], pn = m[5], pa = m[6];
+ if (cn){
+ parsed.classes.push(cn);
+ } else if (pn){
+ var parser = Selectors.Pseudo.get(pn);
+ if (parser) parsed.pseudos.push({parser: parser, argument: pa});
+ else parsed.attributes.push({name: pn, operator: '=', value: pa});
+ } else if (an){
+ parsed.attributes.push({name: an, operator: ao, value: av});
+ }
+ }
+ if (!parsed.classes.length) delete parsed.classes;
+ if (!parsed.attributes.length) delete parsed.attributes;
+ if (!parsed.pseudos.length) delete parsed.pseudos;
+ if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed =
null;
+ return Selectors.Cache.parsed[selector] = parsed;
+ },
+
+ parseTagAndID: function(selector){
+ var tag = selector.match(Selectors.RegExps.tag);
+ var id = selector.match(Selectors.RegExps.id);
+ return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
+ },
+
+ filter: function(item, parsed, local){
+ var i;
+ if (parsed.classes){
+ for (i = parsed.classes.length; i--; i){
+ var cn = parsed.classes[i];
+ if (!Selectors.Filters.byClass(item, cn)) return false;
+ }
+ }
+ if (parsed.attributes){
+ for (i = parsed.attributes.length; i--; i){
+ var att = parsed.attributes[i];
+ if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return
false;
+ }
+ }
+ if (parsed.pseudos){
+ for (i = parsed.pseudos.length; i--; i){
+ var psd = parsed.pseudos[i];
+ if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return
false;
+ }
+ }
+ return true;
+ },
+
+ getByTagAndID: function(ctx, tag, id){
+ if (id){
+ var item = (ctx.getElementById) ? ctx.getElementById(id, true) :
Element.getElementById(ctx, id, true);
+ return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
+ } else {
+ return ctx.getElementsByTagName(tag);
+ }
+ },
+
+ search: function(self, expression, local){
+ var splitters = [];
+
+ var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1,
m2){
+ splitters.push(m1);
+ return ':)' + m2;
+ }).split(':)');
+
+ var items, match, filtered, item;
+
+ for (var i = 0, l = selectors.length; i < l; i++){
+
+ var selector = selectors[i];
+
+ if (i == 0 && Selectors.RegExps.quick.test(selector)){
+ items = self.getElementsByTagName(selector);
+ continue;
+ }
+
+ var splitter = splitters[i - 1];
+
+ var tagid = Selectors.Utils.parseTagAndID(selector);
+ var tag = tagid[0], id = tagid[1];
+
+ if (i == 0){
+ items = Selectors.Utils.getByTagAndID(self, tag, id);
+ } else {
+ var uniques = {}, found = [];
+ for (var j = 0, k = items.length; j < k; j++) found =
Selectors.Getters[splitter](found, items[j], tag, id, uniques);
+ items = found;
+ }
+
+ var parsed = Selectors.Utils.parseSelector(selector);
+
+ if (parsed){
+ filtered = [];
+ for (var m = 0, n = items.length; m < n; m++){
+ item = items[m];
+ if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
+ }
+ items = filtered;
+ }
+
+ }
+
+ return items;
+
+ }
+
+};
+
+Selectors.Getters = {
+
+ ' ': function(found, self, tag, id, uniques){
+ var items = Selectors.Utils.getByTagAndID(self, tag, id);
+ for (var i = 0, l = items.length; i < l; i++){
+ var item = items[i];
+ if (Selectors.Utils.chk(item, uniques)) found.push(item);
+ }
+ return found;
+ },
+
+ '>': function(found, self, tag, id, uniques){
+ var children = Selectors.Utils.getByTagAndID(self, tag, id);
+ for (var i = 0, l = children.length; i < l; i++){
+ var child = children[i];
+ if (child.parentNode == self && Selectors.Utils.chk(child, uniques))
found.push(child);
+ }
+ return found;
+ },
+
+ '+': function(found, self, tag, id, uniques){
+ while ((self = self.nextSibling)){
+ if (self.nodeType == 1){
+ if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag)
&& Selectors.Filters.byID(self, id)) found.push(self);
+ break;
+ }
+ }
+ return found;
+ },
+
+ '~': function(found, self, tag, id, uniques){
+
+ while ((self = self.nextSibling)){
+ if (self.nodeType == 1){
+ if (!Selectors.Utils.chk(self, uniques)) break;
+ if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id))
found.push(self);
+ }
+ }
+ return found;
+ }
+
+};
+
+Selectors.Filters = {
+
+ byTag: function(self, tag){
+ return (tag == '*' || (self.tagName && self.tagName.toLowerCase() ==
tag));
+ },
+
+ byID: function(self, id){
+ return (!id || (self.id && self.id == id));
+ },
+
+ byClass: function(self, klass){
+ return (self.className && self.className.contains(klass, ' '));
+ },
+
+ byPseudo: function(self, parser, argument, local){
+ return parser.call(self, argument, local);
+ },
+
+ byAttribute: function(self, name, operator, value){
+ var result = Element.prototype.getProperty.call(self, name);
+ if (!result) return false;
+ if (!operator || value == undefined) return true;
+ switch (operator){
+ case '=': return (result == value);
+ case '*=': return (result.contains(value));
+ case '^=': return (result.substr(0, value.length) == value);
+ case '$=': return (result.substr(result.length - value.length) == value);
+ case '!=': return (result != value);
+ case '~=': return result.contains(value, ' ');
+ case '|=': return result.contains(value, '-');
+ }
+ return false;
+ }
+
+};
+
+Selectors.Pseudo = new Hash({
+
+ // w3c pseudo selectors
+
+ empty: function(){
+ return !(this.innerText || this.textContent || '').length;
+ },
+
+ not: function(selector){
+ return !Element.match(this, selector);
+ },
+
+ contains: function(text){
+ return (this.innerText || this.textContent || '').contains(text);
+ },
+
+ 'first-child': function(){
+ return Selectors.Pseudo.index.call(this, 0);
+ },
+
+ 'last-child': function(){
+ var element = this;
+ while ((element = element.nextSibling)){
+ if (element.nodeType == 1) return false;
+ }
+ return true;
+ },
+
+ 'only-child': function(){
+ var prev = this;
+ while ((prev = prev.previousSibling)){
+ if (prev.nodeType == 1) return false;
+ }
+ var next = this;
+ while ((next = next.nextSibling)){
+ if (next.nodeType == 1) return false;
+ }
+ return true;
+ },
+
+ 'nth-child': function(argument, local){
+ argument = (argument == undefined) ? 'n' : argument;
+ var parsed = Selectors.Utils.parseNthArgument(argument);
+ if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this,
parsed.a, local);
+ var count = 0;
+ local.positions = local.positions || {};
+ var uid = $uid(this);
+ if (!local.positions[uid]){
+ var self = this;
+ while ((self = self.previousSibling)){
+ if (self.nodeType != 1) continue;
+ count ++;
+ var position = local.positions[$uid(self)];
+ if (position != undefined){
+ count = position + count;
+ break;
+ }
+ }
+ local.positions[uid] = count;
+ }
+ return (local.positions[uid] % parsed.a == parsed.b);
+ },
+
+ // custom pseudo selectors
+
+ index: function(index){
+ var element = this, count = 0;
+ while ((element = element.previousSibling)){
+ if (element.nodeType == 1 && ++count > index) return false;
+ }
+ return (count == index);
+ },
+
+ even: function(argument, local){
+ return Selectors.Pseudo['nth-child'].call(this, '2n+1', local);
+ },
+
+ odd: function(argument, local){
+ return Selectors.Pseudo['nth-child'].call(this, '2n', local);
+ }
+
+});
+
+/*
+Script: Domready.js
+ Contains the domready custom event.
+
+License:
+ MIT-style license.
+*/
+
+Element.Events.domready = {
+
+ onAdd: function(fn){
+ if (Browser.loaded) fn.call(this);
+ }
+
+};
+
+(function(){
+
+ var domready = function(){
+ if (Browser.loaded) return;
+ Browser.loaded = true;
+ window.fireEvent('domready');
+ document.fireEvent('domready');
+ };
+
+ switch (Browser.Engine.name){
+
+ case 'webkit': (function(){
+ (['loaded', 'complete'].contains(document.readyState)) ? domready() :
arguments.callee.delay(50);
+ })(); break;
+
+ case 'trident':
+ var temp = document.createElement('div');
+ (function(){
+ ($try(function(){
+ temp.doScroll('left');
+ return $(temp).inject(document.body).set('html', 'temp').dispose();
+ })) ? domready() : arguments.callee.delay(50);
+ })();
+ break;
+
+ default:
+ window.addEvent('load', domready);
+ document.addEvent('DOMContentLoaded', domready);
+
+ }
+
+})();
+
+/*
+Script: JSON.js
+ JSON encoder and decoder.
+
+License:
+ MIT-style license.
+
+See Also:
+ <
http://www.json.org/>
+*/
+
+var JSON = new Hash({
+
+ encode: function(obj){
+ switch ($type(obj)){
+ case 'string':
+ return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) +
'"';
+ case 'array':
+ return '[' + String(obj.map(JSON.encode).filter($defined)) + ']';
+ case 'object': case 'hash':
+ var string = [];
+ Hash.each(obj, function(value, key){
+ var json = JSON.encode(value);
+ if (json) string.push(JSON.encode(key) + ':' + json);
+ });
+ return '{' + string + '}';
+ case 'number': case 'boolean': return String(obj);
+ case false: return 'null';
+ }
+ return null;
+ },
+
+ $specialChars: {'\b': '\\b', '\t': '\\t', '\n':
'\\n', '\f': '\\f', '\r': '\\r', '"'
: '\\"', '\\': '\\\\'},
+
+ $replaceChars: function(chr){
+ return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() /
16).toString(16) + (chr.charCodeAt() % 16).toString(16);
+ },
+
+ decode: function(string, secure){
+ if ($type(string) != 'string' || !string.length) return null;
+ if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u
\n\r\t]*$/).test(string.replace(/\\./g,
'(a)').replace(/"[^"\\\n\r]*"/g, ''))) return null;
+ return eval('(' + string + ')');
+ }
+
+});
+
+Native.implement([Hash, Array, String, Number], {
+
+ toJSON: function(){
+ return JSON.encode(this);
+ }
+
+});
+
+
+/*
+Script: Cookie.js
+ Class for creating, loading, and saving browser Cookies.
+
+License:
+ MIT-style license.
+
+Credits:
+ Based on the functions by Peter-Paul Koch (
http://quirksmode.org).
+*/
+
+var Cookie = new Class({
+
+ Implements: Options,
+
+ options: {
+ path: false,
+ domain: false,
+ duration: false,
+ secure: false,
+ document: document
+ },
+
+ initialize: function(key, options){
+ this.key = key;
+ this.setOptions(options);
+ },
+
+ write: function(value){
+ value = encodeURIComponent(value);
+ if (this.options.domain) value += '; domain=' + this.options.domain;
+ if (this.options.path) value += '; path=' + this.options.path;
+ if (this.options.duration){
+ var date = new Date();
+ date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
+ value += '; expires=' + date.toGMTString();
+ }
+ if (this.options.secure) value += '; secure';
+ this.options.document.cookie = this.key + '=' + value;
+ return this;
+ },
+
+ read: function(){
+ var value = this.options.document.cookie.match('(?:^|;)\\s*' +
this.key.escapeRegExp() + '=([^;]*)');
+ return (value) ? decodeURIComponent(value[1]) : null;
+ },
+
+ dispose: function(){
+ new Cookie(this.key, $merge(this.options, {duration: -1})).write('');
+ return this;
+ }
+
+});
+
+Cookie.write = function(key, value, options){
+ return new Cookie(key, options).write(value);
+};
+
+Cookie.read = function(key){
+ return new Cookie(key).read();
+};
+
+Cookie.dispose = function(key, options){
+ return new Cookie(key, options).dispose();
+};
+
+/*
+Script: Swiff.js
+ Wrapper for embedding SWF movies. Supports (and fixes) External Interface
Communication.
+
+License:
+ MIT-style license.
+
+Credits:
+ Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
+*/
+
+var Swiff = new Class({
+
+ Implements: [Options],
+
+ options: {
+ id: null,
+ height: 1,
+ width: 1,
+ container: null,
+ properties: {},
+ params: {
+ quality: 'high',
+ allowScriptAccess: 'always',
+ wMode: 'transparent',
+ swLiveConnect: true
+ },
+ callBacks: {},
+ vars: {}
+ },
+
+ toElement: function(){
+ return this.object;
+ },
+
+ initialize: function(path, options){
+ this.instance = 'Swiff_' + $time();
+
+ this.setOptions(options);
+ options = this.options;
+ var id = this.id = options.id || this.instance;
+ var container = $(options.container);
+
+ Swiff.CallBacks[this.instance] = {};
+
+ var params = options.params, vars = options.vars, callBacks = options.callBacks;
+ var properties = $extend({height: options.height, width: options.width},
options.properties);
+
+ var self = this;
+
+ for (var callBack in callBacks){
+ Swiff.CallBacks[this.instance][callBack] = (function(option){
+ return function(){
+ return option.apply(self.object, arguments);
+ };
+ })(callBacks[callBack]);
+ vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
+ }
+
+ params.flashVars = Hash.toQueryString(vars);
+ if (Browser.Engine.trident){
+ properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
+ params.movie = path;
+ } else {
+ properties.type = 'application/x-shockwave-flash';
+ properties.data = path;
+ }
+ var build = '<object id="' + id + '"';
+ for (var property in properties) build += ' ' + property + '="' +
properties[property] + '"';
+ build += '>';
+ for (var param in params){
+ if (params[param]) build += '<param name="' + param + '"
value="' + params[param] + '" />';
+ }
+ build += '</object>';
+ this.object = ((container) ? container.empty() : new
Element('div')).set('html', build).firstChild;
+ },
+
+ replaces: function(element){
+ element = $(element, true);
+ element.parentNode.replaceChild(this.toElement(), element);
+ return this;
+ },
+
+ inject: function(element){
+ $(element, true).appendChild(this.toElement());
+ return this;
+ },
+
+ remote: function(){
+ return Swiff.remote.apply(Swiff, [this.toElement()].extend(arguments));
+ }
+
+});
+
+Swiff.CallBacks = {};
+
+Swiff.remote = function(obj, fn){
+ var rs = obj.CallFunction('<invoke name="' + fn + '"
returntype="javascript">' + __flash__argumentsToXML(arguments, 2) +
'</invoke>');
+ return eval(rs);
+};
+
+/*
+Script: Fx.js
+ Contains the basic animation logic to be extended by all other Fx Classes.
+
+License:
+ MIT-style license.
+*/
+
+var Fx = new Class({
+
+ Implements: [Chain, Events, Options],
+
+ options: {
+ /*
+ onStart: $empty,
+ onCancel: $empty,
+ onComplete: $empty,
+ */
+ fps: 50,
+ unit: false,
+ duration: 500,
+ link: 'ignore',
+ transition: function(p){
+ return -(Math.cos(Math.PI * p) - 1) / 2;
+ }
+ },
+
+ initialize: function(options){
+ this.subject = this.subject || this;
+ this.setOptions(options);
+ this.options.duration = Fx.Durations[this.options.duration] ||
this.options.duration.toInt();
+ var wait = this.options.wait;
+ if (wait === false) this.options.link = 'cancel';
+ },
+
+ step: function(){
+ var time = $time();
+ if (time < this.time + this.options.duration){
+ var delta = this.options.transition((time - this.time) / this.options.duration);
+ this.set(this.compute(this.from, this.to, delta));
+ } else {
+ this.set(this.compute(this.from, this.to, 1));
+ this.complete();
+ }
+ },
+
+ set: function(now){
+ return now;
+ },
+
+ compute: function(from, to, delta){
+ return Fx.compute(from, to, delta);
+ },
+
+ check: function(caller){
+ if (!this.timer) return true;
+ switch (this.options.link){
+ case 'cancel': this.cancel(); return true;
+ case 'chain': this.chain(caller.bind(this, Array.slice(arguments, 1))); return
false;
+ }
+ return false;
+ },
+
+ start: function(from, to){
+ if (!this.check(arguments.callee, from, to)) return this;
+ this.from = from;
+ this.to = to;
+ this.time = 0;
+ this.startTimer();
+ this.onStart();
+ return this;
+ },
+
+ complete: function(){
+ if (this.stopTimer()) this.onComplete();
+ return this;
+ },
+
+ cancel: function(){
+ if (this.stopTimer()) this.onCancel();
+ return this;
+ },
+
+ onStart: function(){
+ this.fireEvent('start', this.subject);
+ },
+
+ onComplete: function(){
+ this.fireEvent('complete', this.subject);
+ if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
+ },
+
+ onCancel: function(){
+ this.fireEvent('cancel', this.subject).clearChain();
+ },
+
+ pause: function(){
+ this.stopTimer();
+ return this;
+ },
+
+ resume: function(){
+ this.startTimer();
+ return this;
+ },
+
+ stopTimer: function(){
+ if (!this.timer) return false;
+ this.time = $time() - this.time;
+ this.timer = $clear(this.timer);
+ return true;
+ },
+
+ startTimer: function(){
+ if (this.timer) return false;
+ this.time = $time() - this.time;
+ this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);
+ return true;
+ }
+
+});
+
+Fx.compute = function(from, to, delta){
+ return (to - from) * delta + from;
+};
+
+Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
+
+
+/*
+Script: Fx.CSS.js
+ Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
+
+License:
+ MIT-style license.
+*/
+
+Fx.CSS = new Class({
+
+ Extends: Fx,
+
+ //prepares the base from/to object
+
+ prepare: function(element, property, values){
+ values = $splat(values);
+ var values1 = values[1];
+ if (!$chk(values1)){
+ values[1] = values[0];
+ values[0] = element.getStyle(property);
+ }
+ var parsed = values.map(this.parse);
+ return {from: parsed[0], to: parsed[1]};
+ },
+
+ //parses a value into an array
+
+ parse: function(value){
+ value = $lambda(value)();
+ value = (typeof value == 'string') ? value.split(' ') : $splat(value);
+ return value.map(function(val){
+ val = String(val);
+ var found = false;
+ Fx.CSS.Parsers.each(function(parser, key){
+ if (found) return;
+ var parsed = parser.parse(val);
+ if ($chk(parsed)) found = {value: parsed, parser: parser};
+ });
+ found = found || {value: val, parser: Fx.CSS.Parsers.String};
+ return found;
+ });
+ },
+
+ //computes by a from and to prepared objects, using their parsers.
+
+ compute: function(from, to, delta){
+ var computed = [];
+ (Math.min(from.length, to.length)).times(function(i){
+ computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta),
parser: from[i].parser});
+ });
+ computed.$family = {name: 'fx:css:value'};
+ return computed;
+ },
+
+ //serves the value as settable
+
+ serve: function(value, unit){
+ if ($type(value) != 'fx:css:value') value = this.parse(value);
+ var returned = [];
+ value.each(function(bit){
+ returned = returned.concat(bit.parser.serve(bit.value, unit));
+ });
+ return returned;
+ },
+
+ //renders the change to an element
+
+ render: function(element, property, value, unit){
+ element.setStyle(property, this.serve(value, unit));
+ },
+
+ //searches inside the page css to find the values for a selector
+
+ search: function(selector){
+ if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
+ var to = {};
+ Array.each(document.styleSheets, function(sheet, j){
+ var href = sheet.href;
+ if (href && href.contains('://') &&
!href.contains(document.domain)) return;
+ var rules = sheet.rules || sheet.cssRules;
+ Array.each(rules, function(rule, i){
+ if (!rule.style) return;
+ var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/,
function(m){
+ return m.toLowerCase();
+ }) : null;
+ if (!selectorText || !selectorText.test('^' + selector + '$'))
return;
+ Element.Styles.each(function(value, style){
+ if (!rule.style[style] || Element.ShortStyles[style]) return;
+ value = String(rule.style[style]);
+ to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value;
+ });
+ });
+ });
+ return Fx.CSS.Cache[selector] = to;
+ }
+
+});
+
+Fx.CSS.Cache = {};
+
+Fx.CSS.Parsers = new Hash({
+
+ Color: {
+ parse: function(value){
+ if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
+ return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2],
value[3]] : false;
+ },
+ compute: function(from, to, delta){
+ return from.map(function(value, i){
+ return Math.round(Fx.compute(from[i], to[i], delta));
+ });
+ },
+ serve: function(value){
+ return value.map(Number);
+ }
+ },
+
+ Number: {
+ parse: parseFloat,
+ compute: Fx.compute,
+ serve: function(value, unit){
+ return (unit) ? value + unit : value;
+ }
+ },
+
+ String: {
+ parse: $lambda(false),
+ compute: $arguments(1),
+ serve: $arguments(0)
+ }
+
+});
+
+
+/*
+Script: Fx.Tween.js
+ Formerly Fx.Style, effect to transition any CSS property for an element.
+
+License:
+ MIT-style license.
+*/
+
+Fx.Tween = new Class({
+
+ Extends: Fx.CSS,
+
+ initialize: function(element, options){
+ this.element = this.subject = $(element);
+ this.parent(options);
+ },
+
+ set: function(property, now){
+ if (arguments.length == 1){
+ now = property;
+ property = this.property || this.options.property;
+ }
+ this.render(this.element, property, now, this.options.unit);
+ return this;
+ },
+
+ start: function(property, from, to){
+ if (!this.check(arguments.callee, property, from, to)) return this;
+ var args = Array.flatten(arguments);
+ this.property = this.options.property || args.shift();
+ var parsed = this.prepare(this.element, this.property, args);
+ return this.parent(parsed.from, parsed.to);
+ }
+
+});
+
+Element.Properties.tween = {
+
+ set: function(options){
+ var tween = this.retrieve('tween');
+ if (tween) tween.cancel();
+ return this.eliminate('tween').store('tween:options', $extend({link:
'cancel'}, options));
+ },
+
+ get: function(options){
+ if (options || !this.retrieve('tween')){
+ if (options || !this.retrieve('tween:options')) this.set('tween',
options);
+ this.store('tween', new Fx.Tween(this,
this.retrieve('tween:options')));
+ }
+ return this.retrieve('tween');
+ }
+
+};
+
+Element.implement({
+
+ tween: function(property, from, to){
+ this.get('tween').start(arguments);
+ return this;
+ },
+
+ fade: function(how){
+ var fade = this.get('tween'), o = 'opacity', toggle;
+ how = $pick(how, 'toggle');
+ switch (how){
+ case 'in': fade.start(o, 1); break;
+ case 'out': fade.start(o, 0); break;
+ case 'show': fade.set(o, 1); break;
+ case 'hide': fade.set(o, 0); break;
+ case 'toggle':
+ var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
+ fade.start(o, (flag) ? 0 : 1);
+ this.store('fade:flag', !flag);
+ toggle = true;
+ break;
+ default: fade.start(o, arguments);
+ }
+ if (!toggle) this.eliminate('fade:flag');
+ return this;
+ },
+
+ highlight: function(start, end){
+ if (!end){
+ end = this.retrieve('highlight:original',
this.getStyle('background-color'));
+ end = (end == 'transparent') ? '#fff' : end;
+ }
+ var tween = this.get('tween');
+ tween.start('background-color', start || '#ffff88',
end).chain(function(){
+ this.setStyle('background-color',
this.retrieve('highlight:original'));
+ tween.callChain();
+ }.bind(this));
+ return this;
+ }
+
+});
+
+
+/*
+Script: Fx.Morph.js
+ Formerly Fx.Styles, effect to transition any number of CSS properties for an element
using an object of rules, or CSS based selector rules.
+
+License:
+ MIT-style license.
+*/
+
+Fx.Morph = new Class({
+
+ Extends: Fx.CSS,
+
+ initialize: function(element, options){
+ this.element = this.subject = $(element);
+ this.parent(options);
+ },
+
+ set: function(now){
+ if (typeof now == 'string') now = this.search(now);
+ for (var p in now) this.render(this.element, p, now[p], this.options.unit);
+ return this;
+ },
+
+ compute: function(from, to, delta){
+ var now = {};
+ for (var p in from) now[p] = this.parent(from[p], to[p], delta);
+ return now;
+ },
+
+ start: function(properties){
+ if (!this.check(arguments.callee, properties)) return this;
+ if (typeof properties == 'string') properties = this.search(properties);
+ var from = {}, to = {};
+ for (var p in properties){
+ var parsed = this.prepare(this.element, p, properties[p]);
+ from[p] = parsed.from;
+ to[p] = parsed.to;
+ }
+ return this.parent(from, to);
+ }
+
+});
+
+Element.Properties.morph = {
+
+ set: function(options){
+ var morph = this.retrieve('morph');
+ if (morph) morph.cancel();
+ return this.eliminate('morph').store('morph:options', $extend({link:
'cancel'}, options));
+ },
+
+ get: function(options){
+ if (options || !this.retrieve('morph')){
+ if (options || !this.retrieve('morph:options')) this.set('morph',
options);
+ this.store('morph', new Fx.Morph(this,
this.retrieve('morph:options')));
+ }
+ return this.retrieve('morph');
+ }
+
+};
+
+Element.implement({
+
+ morph: function(props){
+ this.get('morph').start(props);
+ return this;
+ }
+
+});
+
+/*
+Script: Fx.Transitions.js
+ Contains a set of advanced transitions to be used with any of the Fx Classes.
+
+License:
+ MIT-style license.
+
+Credits:
+ Easing Equations by Robert Penner, <
http://www.robertpenner.com/easing/>, modified
and optimized to be used with MooTools.
+*/
+
+(function(){
+
+ var old = Fx.prototype.initialize;
+
+ Fx.prototype.initialize = function(options){
+ old.call(this, options);
+ var trans = this.options.transition;
+ if (typeof trans == 'string' && (trans = trans.split(':'))){
+ var base = Fx.Transitions;
+ base = base[trans[0]] || base[trans[0].capitalize()];
+ if (trans[1]) base = base['ease' + trans[1].capitalize() + (trans[2] ?
trans[2].capitalize() : '')];
+ this.options.transition = base;
+ }
+ };
+
+})();
+
+Fx.Transition = function(transition, params){
+ params = $splat(params);
+ return $extend(transition, {
+ easeIn: function(pos){
+ return transition(pos, params);
+ },
+ easeOut: function(pos){
+ return 1 - transition(1 - pos, params);
+ },
+ easeInOut: function(pos){
+ return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 -
pos), params)) / 2;
+ }
+ });
+};
+
+Fx.Transitions = new Hash({
+
+ linear: $arguments(0)
+
+});
+
+Fx.Transitions.extend = function(transitions){
+ for (var transition in transitions) Fx.Transitions[transition] = new
Fx.Transition(transitions[transition]);
+};
+
+Fx.Transitions.extend({
+
+ Pow: function(p, x){
+ return Math.pow(p, x[0] || 6);
+ },
+
+ Expo: function(p){
+ return Math.pow(2, 8 * (p - 1));
+ },
+
+ Circ: function(p){
+ return 1 - Math.sin(Math.acos(p));
+ },
+
+ Sine: function(p){
+ return 1 - Math.sin((1 - p) * Math.PI / 2);
+ },
+
+ Back: function(p, x){
+ x = x[0] || 1.618;
+ return Math.pow(p, 2) * ((x + 1) * p - x);
+ },
+
+ Bounce: function(p){
+ var value;
+ for (var a = 0, b = 1; 1; a += b, b /= 2){
+ if (p >= (7 - 4 * a) / 11){
+ value = - Math.pow((11 - 6 * a - 11 * p) / 4, 2) + b * b;
+ break;
+ }
+ }
+ return value;
+ },
+
+ Elastic: function(p, x){
+ return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
+ }
+
+});
+
+['Quad', 'Cubic', 'Quart',
'Quint'].each(function(transition, i){
+ Fx.Transitions[transition] = new Fx.Transition(function(p){
+ return Math.pow(p, [i + 2]);
+ });
+});
+
+
+/*
+Script: Request.js
+ Powerful all purpose Request Class. Uses XMLHTTPRequest.
+
+License:
+ MIT-style license.
+*/
+
+var Request = new Class({
+
+ Implements: [Chain, Events, Options],
+
+ options: {
+ /*onRequest: $empty,
+ onSuccess: $empty,
+ onFailure: $empty,
+ onException: $empty,*/
+ url: '',
+ data: '',
+ headers: {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml,
*/*'
+ },
+ async: true,
+ format: false,
+ method: 'post',
+ link: 'ignore',
+ isSuccess: null,
+ emulation: true,
+ urlEncoded: true,
+ encoding: 'utf-8',
+ evalScripts: false,
+ evalResponse: false
+ },
+
+ initialize: function(options){
+ this.xhr = new Browser.Request();
+ this.setOptions(options);
+ this.options.isSuccess = this.options.isSuccess || this.isSuccess;
+ this.headers = new Hash(this.options.headers);
+ },
+
+ onStateChange: function(){
+ if (this.xhr.readyState != 4 || !this.running) return;
+ this.running = false;
+ this.status = 0;
+ $try(function(){
+ this.status = this.xhr.status;
+ }.bind(this));
+ if (this.options.isSuccess.call(this, this.status)){
+ this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
+ this.success(this.response.text, this.response.xml);
+ } else {
+ this.response = {text: null, xml: null};
+ this.failure();
+ }
+ this.xhr.onreadystatechange = $empty;
+ },
+
+ isSuccess: function(){
+ return ((this.status >= 200) && (this.status < 300));
+ },
+
+ processScripts: function(text){
+ if (this.options.evalResponse ||
(/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
+ return text.stripScripts(this.options.evalScripts);
+ },
+
+ success: function(text, xml){
+ this.onSuccess(this.processScripts(text), xml);
+ },
+
+ onSuccess: function(){
+ this.fireEvent('complete', arguments).fireEvent('success',
arguments).callChain();
+ },
+
+ failure: function(){
+ this.onFailure();
+ },
+
+ onFailure: function(){
+ this.fireEvent('complete').fireEvent('failure', this.xhr);
+ },
+
+ setHeader: function(name, value){
+ this.headers.set(name, value);
+ return this;
+ },
+
+ getHeader: function(name){
+ return $try(function(){
+ return this.xhr.getResponseHeader(name);
+ }.bind(this));
+ },
+
+ check: function(caller){
+ if (!this.running) return true;
+ switch (this.options.link){
+ case 'cancel': this.cancel(); return true;
+ case 'chain': this.chain(caller.bind(this, Array.slice(arguments, 1))); return
false;
+ }
+ return false;
+ },
+
+ send: function(options){
+ if (!this.check(arguments.callee, options)) return this;
+ this.running = true;
+
+ var type = $type(options);
+ if (type == 'string' || type == 'element') options = {data: options};
+
+ var old = this.options;
+ options = $extend({data: old.data, url: old.url, method: old.method}, options);
+ var data = options.data, url = options.url, method = options.method;
+
+ switch ($type(data)){
+ case 'element': data = $(data).toQueryString(); break;
+ case 'object': case 'hash': data = Hash.toQueryString(data);
+ }
+
+ if (this.options.format){
+ var format = 'format=' + this.options.format;
+ data = (data) ? format + '&' + data : format;
+ }
+
+ if (this.options.emulation && ['put',
'delete'].contains(method)){
+ var _method = '_method=' + method;
+ data = (data) ? _method + '&' + data : _method;
+ method = 'post';
+ }
+
+ if (this.options.urlEncoded && method == 'post'){
+ var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding
: '';
+ this.headers.set('Content-type', 'application/x-www-form-urlencoded' +
encoding);
+ }
+
+ if (data && method == 'get'){
+ url = url + (url.contains('?') ? '&' : '?') + data;
+ data = null;
+ }
+
+ this.xhr.open(method.toUpperCase(), url, this.options.async);
+
+ this.xhr.onreadystatechange = this.onStateChange.bind(this);
+
+ this.headers.each(function(value, key){
+ if (!$try(function(){
+ this.xhr.setRequestHeader(key, value);
+ return true;
+ }.bind(this))) this.fireEvent('exception', [key, value]);
+ }, this);
+
+ this.fireEvent('request');
+ this.xhr.send(data);
+ if (!this.options.async) this.onStateChange();
+ return this;
+ },
+
+ cancel: function(){
+ if (!this.running) return this;
+ this.running = false;
+ this.xhr.abort();
+ this.xhr.onreadystatechange = $empty;
+ this.xhr = new Browser.Request();
+ this.fireEvent('cancel');
+ return this;
+ }
+
+});
+
+(function(){
+
+var methods = {};
+['get', 'post', 'put', 'delete', 'GET',
'POST', 'PUT', 'DELETE'].each(function(method){
+ methods[method] = function(){
+ var params = Array.link(arguments, {url: String.type, data: $defined});
+ return this.send($extend(params, {method: method.toLowerCase()}));
+ };
+});
+
+Request.implement(methods);
+
+})();
+
+Element.Properties.send = {
+
+ set: function(options){
+ var send = this.retrieve('send');
+ if (send) send.cancel();
+ return this.eliminate('send').store('send:options', $extend({
+ data: this, link: 'cancel', method: this.get('method') ||
'post', url: this.get('action')
+ }, options));
+ },
+
+ get: function(options){
+ if (options || !this.retrieve('send')){
+ if (options || !this.retrieve('send:options')) this.set('send',
options);
+ this.store('send', new Request(this.retrieve('send:options')));
+ }
+ return this.retrieve('send');
+ }
+
+};
+
+Element.implement({
+
+ send: function(url){
+ var sender = this.get('send');
+ sender.send({data: this, url: url || sender.options.url});
+ return this;
+ }
+
+});
+
+
+/*
+Script: Request.HTML.js
+ Extends the basic Request Class with additional methods for interacting with HTML
responses.
+
+License:
+ MIT-style license.
+*/
+
+Request.HTML = new Class({
+
+ Extends: Request,
+
+ options: {
+ update: false,
+ evalScripts: true,
+ filter: false
+ },
+
+ processHTML: function(text){
+ var match = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
+ text = (match) ? match[1] : text;
+
+ var container = new Element('div');
+
+ return $try(function(){
+ var root = '<root>' + text + '</root>', doc;
+ if (Browser.Engine.trident){
+ doc = new ActiveXObject('Microsoft.XMLDOM');
+ doc.async = false;
+ doc.loadXML(root);
+ } else {
+ doc = new DOMParser().parseFromString(root, 'text/xml');
+ }
+ root = doc.getElementsByTagName('root')[0];
+ for (var i = 0, k = root.childNodes.length; i < k; i++){
+ var child = Element.clone(root.childNodes[i], true, true);
+ if (child) container.grab(child);
+ }
+ return container;
+ }) || container.set('html', text);
+ },
+
+ success: function(text){
+ var options = this.options, response = this.response;
+
+ response.html = text.stripScripts(function(script){
+ response.javascript = script;
+ });
+
+ var temp = this.processHTML(response.html);
+
+ response.tree = temp.childNodes;
+ response.elements = temp.getElements('*');
+
+ if (options.filter) response.tree = response.elements.filter(options.filter);
+ if (options.update) $(options.update).empty().adopt(response.tree);
+ if (options.evalScripts) $exec(response.javascript);
+
+ this.onSuccess(response.tree, response.elements, response.html, response.javascript);
+ }
+
+});
+
+Element.Properties.load = {
+
+ set: function(options){
+ var load = this.retrieve('load');
+ if (load) send.cancel();
+ return this.eliminate('load').store('load:options', $extend({data:
this, link: 'cancel', update: this, method: 'get'}, options));
+ },
+
+ get: function(options){
+ if (options || ! this.retrieve('load')){
+ if (options || !this.retrieve('load:options')) this.set('load',
options);
+ this.store('load', new Request.HTML(this.retrieve('load:options')));
+ }
+ return this.retrieve('load');
+ }
+
+};
+
+Element.implement({
+
+ load: function(){
+ this.get('load').send(Array.link(arguments, {data: Object.type, url:
String.type}));
+ return this;
+ }
+
+});
+
+
+/*
+Script: Request.JSON.js
+ Extends the basic Request Class with additional methods for sending and receiving JSON
data.
+
+License:
+ MIT-style license.
+*/
+
+Request.JSON = new Class({
+
+ Extends: Request,
+
+ options: {
+ secure: true
+ },
+
+ initialize: function(options){
+ this.parent(options);
+ this.headers.extend({'Accept': 'application/json', 'X-Request':
'JSON'});
+ },
+
+ success: function(text){
+ this.response.json = JSON.decode(text, this.options.secure);
+ this.onSuccess(this.response.json, text);
+ }
+
+});
\ No newline at end of file
diff --git a/moksha/widgets/mindmap/static/js/lib/mootools-1.2-more.js
b/moksha/widgets/mindmap/static/js/lib/mootools-1.2-more.js
new file mode 100644
index 0000000..d0fd9f2
--- /dev/null
+++ b/moksha/widgets/mindmap/static/js/lib/mootools-1.2-more.js
@@ -0,0 +1,1491 @@
+//MooTools More, <
http://mootools.net/more>. Copyright (c) 2006-2008 Valerio
Proietti, <
http://mad4milk.net>, MIT Style License.
+
+/*
+Script: Fx.Slide.js
+ Effect to slide an element in and out of view.
+
+License:
+ MIT-style license.
+*/
+
+Fx.Slide = new Class({
+
+ Extends: Fx,
+
+ options: {
+ mode: 'vertical'
+ },
+
+ initialize: function(element, options){
+ this.addEvent('complete', function(){
+ this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);
+ if (this.open && Browser.Engine.webkit419)
this.element.dispose().inject(this.wrapper);
+ }, true);
+ this.element = this.subject = $(element);
+ this.parent(options);
+ var wrapper = this.element.retrieve('wrapper');
+ this.wrapper = wrapper || new Element('div', {
+ styles: $extend(this.element.getStyles('margin', 'position'),
{'overflow': 'hidden'})
+ }).wraps(this.element);
+ this.element.store('wrapper', this.wrapper).setStyle('margin', 0);
+ this.now = [];
+ this.open = true;
+ },
+
+ vertical: function(){
+ this.margin = 'margin-top';
+ this.layout = 'height';
+ this.offset = this.element.offsetHeight;
+ },
+
+ horizontal: function(){
+ this.margin = 'margin-left';
+ this.layout = 'width';
+ this.offset = this.element.offsetWidth;
+ },
+
+ set: function(now){
+ this.element.setStyle(this.margin, now[0]);
+ this.wrapper.setStyle(this.layout, now[1]);
+ return this;
+ },
+
+ compute: function(from, to, delta){
+ var now = [];
+ var x = 2;
+ x.times(function(i){
+ now[i] = Fx.compute(from[i], to[i], delta);
+ });
+ return now;
+ },
+
+ start: function(how, mode){
+ if (!this.check(arguments.callee, how, mode)) return this;
+ this[mode || this.options.mode]();
+ var margin = this.element.getStyle(this.margin).toInt();
+ var layout = this.wrapper.getStyle(this.layout).toInt();
+ var caseIn = [[margin, layout], [0, this.offset]];
+ var caseOut = [[margin, layout], [-this.offset, 0]];
+ var start;
+ switch (how){
+ case 'in': start = caseIn; break;
+ case 'out': start = caseOut; break;
+ case 'toggle': start = (this.wrapper['offset' +
this.layout.capitalize()] == 0) ? caseIn : caseOut;
+ }
+ return this.parent(start[0], start[1]);
+ },
+
+ slideIn: function(mode){
+ return this.start('in', mode);
+ },
+
+ slideOut: function(mode){
+ return this.start('out', mode);
+ },
+
+ hide: function(mode){
+ this[mode || this.options.mode]();
+ this.open = false;
+ return this.set([-this.offset, 0]);
+ },
+
+ show: function(mode){
+ this[mode || this.options.mode]();
+ this.open = true;
+ return this.set([0, this.offset]);
+ },
+
+ toggle: function(mode){
+ return this.start('toggle', mode);
+ }
+
+});
+
+Element.Properties.slide = {
+
+ set: function(options){
+ var slide = this.retrieve('slide');
+ if (slide) slide.cancel();
+ return this.eliminate('slide').store('slide:options', $extend({link:
'cancel'}, options));
+ },
+
+ get: function(options){
+ if (options || !this.retrieve('slide')){
+ if (options || !this.retrieve('slide:options')) this.set('slide',
options);
+ this.store('slide', new Fx.Slide(this,
this.retrieve('slide:options')));
+ }
+ return this.retrieve('slide');
+ }
+
+};
+
+Element.implement({
+
+ slide: function(how, mode){
+ how = how || 'toggle';
+ var slide = this.get('slide'), toggle;
+ switch (how){
+ case 'hide': slide.hide(mode); break;
+ case 'show': slide.show(mode); break;
+ case 'toggle':
+ var flag = this.retrieve('slide:flag', slide.open);
+ slide[(flag) ? 'slideOut' : 'slideIn'](mode);
+ this.store('slide:flag', !flag);
+ toggle = true;
+ break;
+ default: slide.start(how, mode);
+ }
+ if (!toggle) this.eliminate('slide:flag');
+ return this;
+ }
+
+});
+
+
+/*
+Script: Fx.Scroll.js
+ Effect to smoothly scroll any element, including the window.
+
+License:
+ MIT-style license.
+*/
+
+Fx.Scroll = new Class({
+
+ Extends: Fx,
+
+ options: {
+ offset: {'x': 0, 'y': 0},
+ wheelStops: true
+ },
+
+ initialize: function(element, options){
+ this.element = this.subject = $(element);
+ this.parent(options);
+ var cancel = this.cancel.bind(this, false);
+
+ if ($type(this.element) != 'element') this.element =
$(this.element.getDocument().body);
+
+ var stopper = this.element;
+
+ if (this.options.wheelStops){
+ this.addEvent('start', function(){
+ stopper.addEvent('mousewheel', cancel);
+ }, true);
+ this.addEvent('complete', function(){
+ stopper.removeEvent('mousewheel', cancel);
+ }, true);
+ }
+ },
+
+ set: function(){
+ var now = Array.flatten(arguments);
+ this.element.scrollTo(now[0], now[1]);
+ },
+
+ compute: function(from, to, delta){
+ var now = [];
+ var x = 2;
+ x.times(function(i){
+ now.push(Fx.compute(from[i], to[i], delta));
+ });
+ return now;
+ },
+
+ start: function(x, y){
+ if (!this.check(arguments.callee, x, y)) return this;
+ var offsetSize = this.element.getSize(), scrollSize = this.element.getScrollSize();
+ var scroll = this.element.getScroll(), values = {x: x, y: y};
+ for (var z in values){
+ var max = scrollSize[z] - offsetSize[z];
+ if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ?
values[z].limit(0, max) : max;
+ else values[z] = scroll[z];
+ values[z] += this.options.offset[z];
+ }
+ return this.parent([scroll.x, scroll.y], [values.x, values.y]);
+ },
+
+ toTop: function(){
+ return this.start(false, 0);
+ },
+
+ toLeft: function(){
+ return this.start(0, false);
+ },
+
+ toRight: function(){
+ return this.start('right', false);
+ },
+
+ toBottom: function(){
+ return this.start(false, 'bottom');
+ },
+
+ toElement: function(el){
+ var position = $(el).getPosition(this.element);
+ return this.start(position.x, position.y);
+ }
+
+});
+
+
+/*
+Script: Fx.Elements.js
+ Effect to change any number of CSS properties of any number of Elements.
+
+License:
+ MIT-style license.
+*/
+
+Fx.Elements = new Class({
+
+ Extends: Fx.CSS,
+
+ initialize: function(elements, options){
+ this.elements = this.subject = $$(elements);
+ this.parent(options);
+ },
+
+ compute: function(from, to, delta){
+ var now = {};
+ for (var i in from){
+ var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
+ for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
+ }
+ return now;
+ },
+
+ set: function(now){
+ for (var i in now){
+ var iNow = now[i];
+ for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
+ }
+ return this;
+ },
+
+ start: function(obj){
+ if (!this.check(arguments.callee, obj)) return this;
+ var from = {}, to = {};
+ for (var i in obj){
+ var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
+ for (var p in iProps){
+ var parsed = this.prepare(this.elements[i], p, iProps[p]);
+ iFrom[p] = parsed.from;
+ iTo[p] = parsed.to;
+ }
+ }
+ return this.parent(from, to);
+ }
+
+});
+
+/*
+Script: Drag.js
+ The base Drag Class. Can be used to drag and resize Elements using mouse events.
+
+License:
+ MIT-style license.
+*/
+
+var Drag = new Class({
+
+ Implements: [Events, Options],
+
+ options: {/*
+ onBeforeStart: $empty,
+ onStart: $empty,
+ onDrag: $empty,
+ onCancel: $empty,
+ onComplete: $empty,*/
+ snap: 6,
+ unit: 'px',
+ grid: false,
+ style: true,
+ limit: false,
+ handle: false,
+ invert: false,
+ preventDefault: false,
+ modifiers: {x: 'left', y: 'top'}
+ },
+
+ initialize: function(){
+ var params = Array.link(arguments, {'options': Object.type, 'element':
$defined});
+ this.element = $(params.element);
+ this.document = this.element.getDocument();
+ this.setOptions(params.options || {});
+ var htype = $type(this.options.handle);
+ this.handles = (htype == 'array' || htype == 'collection') ?
$$(this.options.handle) : $(this.options.handle) || this.element;
+ this.mouse = {'now': {}, 'pos': {}};
+ this.value = {'start': {}, 'now': {}};
+
+ this.selection = (Browser.Engine.trident) ? 'selectstart' :
'mousedown';
+
+ this.bound = {
+ start: this.start.bind(this),
+ check: this.check.bind(this),
+ drag: this.drag.bind(this),
+ stop: this.stop.bind(this),
+ cancel: this.cancel.bind(this),
+ eventStop: $lambda(false)
+ };
+ this.attach();
+ },
+
+ attach: function(){
+ this.handles.addEvent('mousedown', this.bound.start);
+ return this;
+ },
+
+ detach: function(){
+ this.handles.removeEvent('mousedown', this.bound.start);
+ return this;
+ },
+
+ start: function(event){
+ if (this.options.preventDefault) event.preventDefault();
+ this.fireEvent('beforeStart', this.element);
+ this.mouse.start = event.page;
+ var limit = this.options.limit;
+ this.limit = {'x': [], 'y': []};
+ for (var z in this.options.modifiers){
+ if (!this.options.modifiers[z]) continue;
+ if (this.options.style) this.value.now[z] =
this.element.getStyle(this.options.modifiers[z]).toInt();
+ else this.value.now[z] = this.element[this.options.modifiers[z]];
+ if (this.options.invert) this.value.now[z] *= -1;
+ this.mouse.pos[z] = event.page[z] - this.value.now[z];
+ if (limit && limit[z]){
+ for (var i = 2; i--; i){
+ if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])();
+ }
+ }
+ }
+ if ($type(this.options.grid) == 'number') this.options.grid = {'x':
this.options.grid, 'y': this.options.grid};
+ this.document.addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel});
+ this.document.addEvent(this.selection, this.bound.eventStop);
+ },
+
+ check: function(event){
+ if (this.options.preventDefault) event.preventDefault();
+ var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) +
Math.pow(event.page.y - this.mouse.start.y, 2)));
+ if (distance > this.options.snap){
+ this.cancel();
+ this.document.addEvents({
+ mousemove: this.bound.drag,
+ mouseup: this.bound.stop
+ });
+ this.fireEvent('start', this.element).fireEvent('snap',
this.element);
+ }
+ },
+
+ drag: function(event){
+ if (this.options.preventDefault) event.preventDefault();
+ this.mouse.now = event.page;
+ for (var z in this.options.modifiers){
+ if (!this.options.modifiers[z]) continue;
+ this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
+ if (this.options.invert) this.value.now[z] *= -1;
+ if (this.options.limit && this.limit[z]){
+ if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){
+ this.value.now[z] = this.limit[z][1];
+ } else if ($chk(this.limit[z][0]) && (this.value.now[z] <
this.limit[z][0])){
+ this.value.now[z] = this.limit[z][0];
+ }
+ }
+ if (this.options.grid[z]) this.value.now[z] -= (this.value.now[z] %
this.options.grid[z]);
+ if (this.options.style) this.element.setStyle(this.options.modifiers[z],
this.value.now[z] + this.options.unit);
+ else this.element[this.options.modifiers[z]] = this.value.now[z];
+ }
+ this.fireEvent('drag', this.element);
+ },
+
+ cancel: function(event){
+ this.document.removeEvent('mousemove', this.bound.check);
+ this.document.removeEvent('mouseup', this.bound.cancel);
+ if (event){
+ this.document.removeEvent(this.selection, this.bound.eventStop);
+ this.fireEvent('cancel', this.element);
+ }
+ },
+
+ stop: function(event){
+ this.document.removeEvent(this.selection, this.bound.eventStop);
+ this.document.removeEvent('mousemove', this.bound.drag);
+ this.document.removeEvent('mouseup', this.bound.stop);
+ if (event) this.fireEvent('complete', this.element);
+ }
+
+});
+
+Element.implement({
+
+ makeResizable: function(options){
+ return new Drag(this, $merge({modifiers: {'x': 'width', 'y':
'height'}}, options));
+ }
+
+});
+
+/*
+Script: Drag.Move.js
+ A Drag extension that provides support for the constraining of draggables to containers
and droppables.
+
+License:
+ MIT-style license.
+*/
+
+Drag.Move = new Class({
+
+ Extends: Drag,
+
+ options: {
+ droppables: [],
+ container: false
+ },
+
+ initialize: function(element, options){
+ this.parent(element, options);
+ this.droppables = $$(this.options.droppables);
+ this.container = $(this.options.container);
+ if (this.container && $type(this.container) != 'element')
this.container = $(this.container.getDocument().body);
+ element = this.element;
+
+ var current = element.getStyle('position');
+ var position = (current != 'static') ? current : 'absolute';
+ if (element.getStyle('left') == 'auto' ||
element.getStyle('top') == 'auto')
element.position(element.getPosition(element.offsetParent));
+
+ element.setStyle('position', position);
+
+ this.addEvent('start', function(){
+ this.checkDroppables();
+ }, true);
+ },
+
+ start: function(event){
+ if (this.container){
+ var el = this.element, cont = this.container, ccoo =
cont.getCoordinates(el.offsetParent), cps = {}, ems = {};
+
+ ['top', 'right', 'bottom',
'left'].each(function(pad){
+ cps[pad] = cont.getStyle('padding-' + pad).toInt();
+ ems[pad] = el.getStyle('margin-' + pad).toInt();
+ }, this);
+
+ var width = el.offsetWidth + ems.left + ems.right, height = el.offsetHeight + ems.top
+ ems.bottom;
+ var x = [ccoo.left + cps.left, ccoo.right - cps.right - width];
+ var y = [ccoo.top + cps.top, ccoo.bottom - cps.bottom - height];
+
+ this.options.limit = {x: x, y: y};
+ }
+ this.parent(event);
+ },
+
+ checkAgainst: function(el){
+ el = el.getCoordinates();
+ var now = this.mouse.now;
+ return (now.x > el.left && now.x < el.right && now.y <
el.bottom && now.y > el.top);
+ },
+
+ checkDroppables: function(){
+ var overed = this.droppables.filter(this.checkAgainst, this).getLast();
+ if (this.overed != overed){
+ if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
+ if (overed){
+ this.overed = overed;
+ this.fireEvent('enter', [this.element, overed]);
+ } else {
+ this.overed = null;
+ }
+ }
+ },
+
+ drag: function(event){
+ this.parent(event);
+ if (this.droppables.length) this.checkDroppables();
+ },
+
+ stop: function(event){
+ this.checkDroppables();
+ this.fireEvent('drop', [this.element, this.overed]);
+ this.overed = null;
+ return this.parent(event);
+ }
+
+});
+
+Element.implement({
+
+ makeDraggable: function(options){
+ return new Drag.Move(this, options);
+ }
+
+});
+
+
+/*
+Script: Hash.Cookie.js
+ Class for creating, reading, and deleting Cookies in JSON format.
+
+License:
+ MIT-style license.
+*/
+
+Hash.Cookie = new Class({
+
+ Extends: Cookie,
+
+ options: {
+ autoSave: true
+ },
+
+ initialize: function(name, options){
+ this.parent(name, options);
+ this.load();
+ },
+
+ save: function(){
+ var value = JSON.encode(this.hash);
+ if (!value || value.length > 4096) return false; //cookie would be truncated!
+ if (value == '{}') this.dispose();
+ else this.write(value);
+ return true;
+ },
+
+ load: function(){
+ this.hash = new Hash(JSON.decode(this.read(), true));
+ return this;
+ }
+
+});
+
+Hash.Cookie.implement((function(){
+
+ var methods = {};
+
+ Hash.each(Hash.prototype, function(method, name){
+ methods[name] = function(){
+ var value = method.apply(this.hash, arguments);
+ if (this.options.autoSave) this.save();
+ return value;
+ };
+ });
+
+ return methods;
+
+})());
+
+/*
+Script: Color.js
+ Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB
Conversions and vice versa.
+
+License:
+ MIT-style license.
+*/
+
+var Color = new Native({
+
+ initialize: function(color, type){
+ if (arguments.length >= 3){
+ type = "rgb"; color = Array.slice(arguments, 0, 3);
+ } else if (typeof color == 'string'){
+ if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true);
+ else if (color.match(/hsb/)) color = color.hsbToRgb();
+ else color = color.hexToRgb(true);
+ }
+ type = type || 'rgb';
+ switch (type){
+ case 'hsb':
+ var old = color;
+ color = color.hsbToRgb();
+ color.hsb = old;
+ break;
+ case 'hex': color = color.hexToRgb(true); break;
+ }
+ color.rgb = color.slice(0, 3);
+ color.hsb = color.hsb || color.rgbToHsb();
+ color.hex = color.rgbToHex();
+ return $extend(color, this);
+ }
+
+});
+
+Color.implement({
+
+ mix: function(){
+ var colors = Array.slice(arguments);
+ var alpha = ($type(colors.getLast()) == 'number') ? colors.pop() : 50;
+ var rgb = this.slice();
+ colors.each(function(color){
+ color = new Color(color);
+ for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) +
(color[i] / 100 * alpha));
+ });
+ return new Color(rgb, 'rgb');
+ },
+
+ invert: function(){
+ return new Color(this.map(function(value){
+ return 255 - value;
+ }));
+ },
+
+ setHue: function(value){
+ return new Color([value, this.hsb[1], this.hsb[2]], 'hsb');
+ },
+
+ setSaturation: function(percent){
+ return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb');
+ },
+
+ setBrightness: function(percent){
+ return new Color([this.hsb[0], this.hsb[1], percent], 'hsb');
+ }
+
+});
+
+function $RGB(r, g, b){
+ return new Color([r, g, b], 'rgb');
+};
+
+function $HSB(h, s, b){
+ return new Color([h, s, b], 'hsb');
+};
+
+function $HEX(hex){
+ return new Color(hex, 'hex');
+};
+
+Array.implement({
+
+ rgbToHsb: function(){
+ var red = this[0], green = this[1], blue = this[2];
+ var hue, saturation, brightness;
+ var max = Math.max(red, green, blue), min = Math.min(red, green, blue);
+ var delta = max - min;
+ brightness = max / 255;
+ saturation = (max != 0) ? delta / max : 0;
+ if (saturation == 0){
+ hue = 0;
+ } else {
+ var rr = (max - red) / delta;
+ var gr = (max - green) / delta;
+ var br = (max - blue) / delta;
+ if (red == max) hue = br - gr;
+ else if (green == max) hue = 2 + rr - br;
+ else hue = 4 + gr - rr;
+ hue /= 6;
+ if (hue < 0) hue++;
+ }
+ return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness *
100)];
+ },
+
+ hsbToRgb: function(){
+ var br = Math.round(this[2] / 100 * 255);
+ if (this[1] == 0){
+ return [br, br, br];
+ } else {
+ var hue = this[0] % 360;
+ var f = hue % 60;
+ var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255);
+ var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255);
+ var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255);
+ switch (Math.floor(hue / 60)){
+ case 0: return [br, t, p];
+ case 1: return [q, br, p];
+ case 2: return [p, br, t];
+ case 3: return [p, q, br];
+ case 4: return [t, p, br];
+ case 5: return [br, p, q];
+ }
+ }
+ return false;
+ }
+
+});
+
+String.implement({
+
+ rgbToHsb: function(){
+ var rgb = this.match(/\d{1,3}/g);
+ return (rgb) ? hsb.rgbToHsb() : null;
+ },
+
+ hsbToRgb: function(){
+ var hsb = this.match(/\d{1,3}/g);
+ return (hsb) ? hsb.hsbToRgb() : null;
+ }
+
+});
+
+
+/*
+Script: Group.js
+ Class for monitoring collections of events
+
+License:
+ MIT-style license.
+*/
+
+var Group = new Class({
+
+ initialize: function(){
+ this.instances = Array.flatten(arguments);
+ this.events = {};
+ this.checker = {};
+ },
+
+ addEvent: function(type, fn){
+ this.checker[type] = this.checker[type] || {};
+ this.events[type] = this.events[type] || [];
+ if (this.events[type].contains(fn)) return false;
+ else this.events[type].push(fn);
+ this.instances.each(function(instance, i){
+ instance.addEvent(type, this.check.bind(this, [type, instance, i]));
+ }, this);
+ return this;
+ },
+
+ check: function(type, instance, i){
+ this.checker[type][i] = true;
+ var every = this.instances.every(function(current, j){
+ return this.checker[type][j] || false;
+ }, this);
+ if (!every) return;
+ this.checker[type] = {};
+ this.events[type].each(function(event){
+ event.call(this, this.instances, instance);
+ }, this);
+ }
+
+});
+
+
+/*
+Script: Assets.js
+ Provides methods to dynamically load JavaScript, CSS, and Image files into the
document.
+
+License:
+ MIT-style license.
+*/
+
+var Asset = new Hash({
+
+ javascript: function(source, properties){
+ properties = $extend({
+ onload: $empty,
+ document: document,
+ check: $lambda(true)
+ }, properties);
+
+ var script = new Element('script', {'src': source, 'type':
'text/javascript'});
+
+ var load = properties.onload.bind(script), check = properties.check, doc =
properties.document;
+ delete properties.onload; delete properties.check; delete properties.document;
+
+ script.addEvents({
+ load: load,
+ readystatechange: function(){
+ if (['loaded', 'complete'].contains(this.readyState)) load();
+ }
+ }).setProperties(properties);
+
+
+ if (Browser.Engine.webkit419) var checker = (function(){
+ if (!$try(check)) return;
+ $clear(checker);
+ load();
+ }).periodical(50);
+
+ return script.inject(doc.head);
+ },
+
+ css: function(source, properties){
+ return new Element('link', $merge({
+ 'rel': 'stylesheet', 'media': 'screen',
'type': 'text/css', 'href': source
+ }, properties)).inject(document.head);
+ },
+
+ image: function(source, properties){
+ properties = $merge({
+ 'onload': $empty,
+ 'onabort': $empty,
+ 'onerror': $empty
+ }, properties);
+ var image = new Image();
+ var element = $(image) || new Element('img');
+ ['load', 'abort', 'error'].each(function(name){
+ var type = 'on' + name;
+ var event = properties[type];
+ delete properties[type];
+ image[type] = function(){
+ if (!image) return;
+ if (!element.parentNode){
+ element.width = image.width;
+ element.height = image.height;
+ }
+ image = image.onload = image.onabort = image.onerror = null;
+ event.delay(1, element, element);
+ element.fireEvent(name, element, 1);
+ };
+ });
+ image.src = element.src = source;
+ if (image && image.complete) image.onload.delay(1);
+ return element.setProperties(properties);
+ },
+
+ images: function(sources, options){
+ options = $merge({
+ onComplete: $empty,
+ onProgress: $empty
+ }, options);
+ if (!sources.push) sources = [sources];
+ var images = [];
+ var counter = 0;
+ sources.each(function(source){
+ var img = new Asset.image(source, {
+ 'onload': function(){
+ options.onProgress.call(this, counter, sources.indexOf(source));
+ counter++;
+ if (counter == sources.length) options.onComplete();
+ }
+ });
+ images.push(img);
+ });
+ return new Elements(images);
+ }
+
+});
+
+/*
+Script: Sortables.js
+ Class for creating a drag and drop sorting interface for lists of items.
+
+License:
+ MIT-style license.
+*/
+
+var Sortables = new Class({
+
+ Implements: [Events, Options],
+
+ options: {/*
+ onSort: $empty,
+ onStart: $empty,
+ onComplete: $empty,*/
+ snap: 4,
+ opacity: 1,
+ clone: false,
+ revert: false,
+ handle: false,
+ constrain: false
+ },
+
+ initialize: function(lists, options){
+ this.setOptions(options);
+ this.elements = [];
+ this.lists = [];
+ this.idle = true;
+
+ this.addLists($$($(lists) || lists));
+ if (!this.options.clone) this.options.revert = false;
+ if (this.options.revert) this.effect = new Fx.Morph(null, $merge({duration: 250, link:
'cancel'}, this.options.revert));
+ },
+
+ attach: function(){
+ this.addLists(this.lists);
+ return this;
+ },
+
+ detach: function(){
+ this.lists = this.removeLists(this.lists);
+ return this;
+ },
+
+ addItems: function(){
+ Array.flatten(arguments).each(function(element){
+ this.elements.push(element);
+ var start = element.retrieve('sortables:start', this.start.bindWithEvent(this,
element));
+ (this.options.handle ? element.getElement(this.options.handle) || element :
element).addEvent('mousedown', start);
+ }, this);
+ return this;
+ },
+
+ addLists: function(){
+ Array.flatten(arguments).each(function(list){
+ this.lists.push(list);
+ this.addItems(list.getChildren());
+ }, this);
+ return this;
+ },
+
+ removeItems: function(){
+ var elements = [];
+ Array.flatten(arguments).each(function(element){
+ elements.push(element);
+ this.elements.erase(element);
+ var start = element.retrieve('sortables:start');
+ (this.options.handle ? element.getElement(this.options.handle) || element :
element).removeEvent('mousedown', start);
+ }, this);
+ return $$(elements);
+ },
+
+ removeLists: function(){
+ var lists = [];
+ Array.flatten(arguments).each(function(list){
+ lists.push(list);
+ this.lists.erase(list);
+ this.removeItems(list.getChildren());
+ }, this);
+ return $$(lists);
+ },
+
+ getClone: function(event, element){
+ if (!this.options.clone) return new Element('div').inject(document.body);
+ if ($type(this.options.clone) == 'function') return
this.options.clone.call(this, event, element, this.list);
+ return element.clone(true).setStyles({
+ 'margin': '0px',
+ 'position': 'absolute',
+ 'visibility': 'hidden',
+ 'width': element.getStyle('width')
+ }).inject(this.list).position(element.getPosition(element.getOffsetParent()));
+ },
+
+ getDroppables: function(){
+ var droppables = this.list.getChildren();
+ if (!this.options.constrain) droppables =
this.lists.concat(droppables).erase(this.list);
+ return droppables.erase(this.clone).erase(this.element);
+ },
+
+ insert: function(dragging, element){
+ var where = 'inside';
+ if (this.lists.contains(element)){
+ this.list = element;
+ this.drag.droppables = this.getDroppables();
+ } else {
+ where = this.element.getAllPrevious().contains(element) ? 'before' :
'after';
+ }
+ this.element.inject(element, where);
+ this.fireEvent('sort', [this.element, this.clone]);
+ },
+
+ start: function(event, element){
+ if (!this.idle) return;
+ this.idle = false;
+ this.element = element;
+ this.opacity = element.get('opacity');
+ this.list = element.getParent();
+ this.clone = this.getClone(event, element);
+
+ this.drag = new Drag.Move(this.clone, {
+ snap: this.options.snap,
+ container: this.options.constrain && this.element.getParent(),
+ droppables: this.getDroppables(),
+ onSnap: function(){
+ event.stop();
+ this.clone.setStyle('visibility', 'visible');
+ this.element.set('opacity', this.options.opacity || 0);
+ this.fireEvent('start', [this.element, this.clone]);
+ }.bind(this),
+ onEnter: this.insert.bind(this),
+ onCancel: this.reset.bind(this),
+ onComplete: this.end.bind(this)
+ });
+
+ this.clone.inject(this.element, 'before');
+ this.drag.start(event);
+ },
+
+ end: function(){
+ this.drag.detach();
+ this.element.set('opacity', this.opacity);
+ if (this.effect){
+ var dim = this.element.getStyles('width', 'height');
+ var pos =
this.clone.computePosition(this.element.getPosition(this.clone.offsetParent));
+ this.effect.element = this.clone;
+ this.effect.start({
+ top: pos.top,
+ left: pos.left,
+ width: dim.width,
+ height: dim.height,
+ opacity: 0.25
+ }).chain(this.reset.bind(this));
+ } else {
+ this.reset();
+ }
+ },
+
+ reset: function(){
+ this.idle = true;
+ this.clone.destroy();
+ this.fireEvent('complete', this.element);
+ },
+
+ serialize: function(){
+ var params = Array.link(arguments, {modifier: Function.type, index: $defined});
+ var serial = this.lists.map(function(list){
+ return list.getChildren().map(params.modifier || function(element){
+ return element.get('id');
+ }, this);
+ }, this);
+
+ var index = params.index;
+ if (this.lists.length == 1) index = 0;
+ return $chk(index) && index >= 0 && index < this.lists.length ?
serial[index] : serial;
+ }
+
+});
+
+/*
+Script: Tips.js
+ Class for creating nice tips that follow the mouse cursor when hovering an element.
+
+License:
+ MIT-style license.
+*/
+
+var Tips = new Class({
+
+ Implements: [Events, Options],
+
+ options: {
+ onShow: function(tip){
+ tip.setStyle('visibility', 'visible');
+ },
+ onHide: function(tip){
+ tip.setStyle('visibility', 'hidden');
+ },
+ showDelay: 100,
+ hideDelay: 100,
+ className: null,
+ offsets: {x: 16, y: 16},
+ fixed: false
+ },
+
+ initialize: function(){
+ var params = Array.link(arguments, {options: Object.type, elements: $defined});
+ this.setOptions(params.options || null);
+
+ this.tip = new Element('div').inject(document.body);
+
+ if (this.options.className) this.tip.addClass(this.options.className);
+
+ var top = new Element('div', {'class':
'tip-top'}).inject(this.tip);
+ this.container = new Element('div', {'class':
'tip'}).inject(this.tip);
+ var bottom = new Element('div', {'class':
'tip-bottom'}).inject(this.tip);
+
+ this.tip.setStyles({position: 'absolute', top: 0, left: 0, visibility:
'hidden'});
+
+ if (params.elements) this.attach(params.elements);
+ },
+
+ attach: function(elements){
+ $$(elements).each(function(element){
+ var title = element.retrieve('tip:title', element.get('title'));
+ var text = element.retrieve('tip:text', element.get('rel') ||
element.get('href'));
+ var enter = element.retrieve('tip:enter',
this.elementEnter.bindWithEvent(this, element));
+ var leave = element.retrieve('tip:leave',
this.elementLeave.bindWithEvent(this, element));
+ element.addEvents({mouseenter: enter, mouseleave: leave});
+ if (!this.options.fixed){
+ var move = element.retrieve('tip:move', this.elementMove.bindWithEvent(this,
element));
+ element.addEvent('mousemove', move);
+ }
+ element.store('tip:native', element.get('title'));
+ element.erase('title');
+ }, this);
+ return this;
+ },
+
+ detach: function(elements){
+ $$(elements).each(function(element){
+ element.removeEvent('mouseenter', element.retrieve('tip:enter') ||
$empty);
+ element.removeEvent('mouseleave', element.retrieve('tip:leave') ||
$empty);
+ element.removeEvent('mousemove', element.retrieve('tip:move') ||
$empty);
+ element.eliminate('tip:enter').eliminate('tip:leave').eliminate('tip:move');
+ var original = element.retrieve('tip:native');
+ if (original) element.set('title', original);
+ });
+ return this;
+ },
+
+ elementEnter: function(event, element){
+
+ $A(this.container.childNodes).each(Element.dispose);
+
+ var title = element.retrieve('tip:title');
+
+ if (title){
+ this.titleElement = new Element('div', {'class':
'tip-title'}).inject(this.container);
+ this.fill(this.titleElement, title);
+ }
+
+ var text = element.retrieve('tip:text');
+ if (text){
+ this.textElement = new Element('div', {'class':
'tip-text'}).inject(this.container);
+ this.fill(this.textElement, text);
+ }
+
+ this.timer = $clear(this.timer);
+ this.timer = this.show.delay(this.options.showDelay, this);
+
+ this.position((!this.options.fixed) ? event : {page: element.getPosition()});
+ },
+
+ elementLeave: function(event){
+ $clear(this.timer);
+ this.timer = this.hide.delay(this.options.hideDelay, this);
+ },
+
+ elementMove: function(event){
+ this.position(event);
+ },
+
+ position: function(event){
+ var size = window.getSize(), scroll = window.getScroll();
+ var tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight};
+ var props = {x: 'left', y: 'top'};
+ for (var z in props){
+ var pos = event.page[z] + this.options.offsets[z];
+ if ((pos + tip[z] - scroll[z]) > size[z]) pos = event.page[z] -
this.options.offsets[z] - tip[z];
+ this.tip.setStyle(props[z], pos);
+ }
+ },
+
+ fill: function(element, contents){
+ (typeof contents == 'string') ? element.set('html', contents) :
element.adopt(contents);
+ },
+
+ show: function(){
+ this.fireEvent('show', this.tip);
+ },
+
+ hide: function(){
+ this.fireEvent('hide', this.tip);
+ }
+
+});
+
+/*
+Script: SmoothScroll.js
+ Class for creating a smooth scrolling effect to all internal links on the page.
+
+License:
+ MIT-style license.
+*/
+
+var SmoothScroll = new Class({
+
+ Extends: Fx.Scroll,
+
+ initialize: function(options, context){
+ context = context || document;
+ var doc = context.getDocument(), win = context.getWindow();
+ this.parent(doc, options);
+ this.links = (this.options.links) ? $$(this.options.links) : $$(doc.links);
+ var location = win.location.href.match(/^[^#]*/)[0] + '#';
+ this.links.each(function(link){
+ if (link.href.indexOf(location) != 0) return;
+ var anchor = link.href.substr(location.length);
+ if (anchor && $(anchor)) this.useLink(link, anchor);
+ }, this);
+ if (!Browser.Engine.webkit419) this.addEvent('complete', function(){
+ win.location.hash = this.anchor;
+ }, true);
+ },
+
+ useLink: function(link, anchor){
+ link.addEvent('click', function(event){
+ this.anchor = anchor;
+ this.toElement(anchor);
+ event.stop();
+ }.bind(this));
+ }
+
+});
+
+/*
+Script: Slider.js
+ Class for creating horizontal and vertical slider controls.
+
+License:
+ MIT-style license.
+*/
+
+var Slider = new Class({
+
+ Implements: [Events, Options],
+
+ options: {/*
+ onChange: $empty,
+ onComplete: $empty,*/
+ onTick: function(position){
+ if(this.options.snap) position = this.toPosition(this.step);
+ this.knob.setStyle(this.property, position);
+ },
+ snap: false,
+ offset: 0,
+ range: false,
+ wheel: false,
+ steps: 100,
+ mode: 'horizontal'
+ },
+
+ initialize: function(element, knob, options){
+ this.setOptions(options);
+ this.element = $(element);
+ this.knob = $(knob);
+ this.previousChange = this.previousEnd = this.step = -1;
+ this.element.addEvent('mousedown', this.clickedElement.bind(this));
+ if (this.options.wheel) this.element.addEvent('mousewheel',
this.scrolledElement.bindWithEvent(this));
+ var offset, limit = {}, modifiers = {'x': false, 'y': false};
+ switch (this.options.mode){
+ case 'vertical':
+ this.axis = 'y';
+ this.property = 'top';
+ offset = 'offsetHeight';
+ break;
+ case 'horizontal':
+ this.axis = 'x';
+ this.property = 'left';
+ offset = 'offsetWidth';
+ }
+ this.half = this.knob[offset] / 2;
+ this.full = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
+ this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
+ this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
+ this.range = this.max - this.min;
+ this.steps = this.options.steps || this.full;
+ this.stepSize = Math.abs(this.range) / this.steps;
+ this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;
+
+ this.knob.setStyle('position', 'relative').setStyle(this.property, -
this.options.offset);
+ modifiers[this.axis] = this.property;
+ limit[this.axis] = [- this.options.offset, this.full - this.options.offset];
+ this.drag = new Drag(this.knob, {
+ snap: 0,
+ limit: limit,
+ modifiers: modifiers,
+ onDrag: this.draggedKnob.bind(this),
+ onStart: this.draggedKnob.bind(this),
+ onComplete: function(){
+ this.draggedKnob();
+ this.end();
+ }.bind(this)
+ });
+ if (this.options.snap) {
+ this.drag.options.grid = Math.ceil(this.stepWidth);
+ this.drag.options.limit[this.axis][1] = this.full;
+ }
+ },
+
+ set: function(step){
+ if (!((this.range > 0) ^ (step < this.min))) step = this.min;
+ if (!((this.range > 0) ^ (step > this.max))) step = this.max;
+
+ this.step = Math.round(step);
+ this.checkStep();
+ this.end();
+ this.fireEvent('tick', this.toPosition(this.step));
+ return this;
+ },
+
+ clickedElement: function(event){
+ var dir = this.range < 0 ? -1 : 1;
+ var position = event.page[this.axis] - this.element.getPosition()[this.axis] -
this.half;
+ position = position.limit(-this.options.offset, this.full -this.options.offset);
+
+ this.step = Math.round(this.min + dir * this.toStep(position));
+ this.checkStep();
+ this.end();
+ this.fireEvent('tick', position);
+ },
+
+ scrolledElement: function(event){
+ var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) :
(event.wheel > 0);
+ this.set(mode ? this.step - this.stepSize : this.step + this.stepSize);
+ event.stop();
+ },
+
+ draggedKnob: function(){
+ var dir = this.range < 0 ? -1 : 1;
+ var position = this.drag.value.now[this.axis];
+ position = position.limit(-this.options.offset, this.full -this.options.offset);
+ this.step = Math.round(this.min + dir * this.toStep(position));
+ this.checkStep();
+ },
+
+ checkStep: function(){
+ if (this.previousChange != this.step){
+ this.previousChange = this.step;
+ this.fireEvent('change', this.step);
+ }
+ },
+
+ end: function(){
+ if (this.previousEnd !== this.step){
+ this.previousEnd = this.step;
+ this.fireEvent('complete', this.step + '');
+ }
+ },
+
+ toStep: function(position){
+ var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
+ return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
+ },
+
+ toPosition: function(step){
+ return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) -
this.options.offset;
+ }
+
+});
+
+/*
+Script: Scroller.js
+ Class which scrolls the contents of any Element (including the window) when the mouse
reaches the Element's boundaries.
+
+License:
+ MIT-style license.
+*/
+
+var Scroller = new Class({
+
+ Implements: [Events, Options],
+
+ options: {
+ area: 20,
+ velocity: 1,
+ onChange: function(x, y){
+ this.element.scrollTo(x, y);
+ }
+ },
+
+ initialize: function(element, options){
+ this.setOptions(options);
+ this.element = $(element);
+ this.listener = ($type(this.element) != 'element') ?
$(this.element.getDocument().body) : this.element;
+ this.timer = null;
+ this.coord = this.getCoords.bind(this);
+ },
+
+ start: function(){
+ this.listener.addEvent('mousemove', this.coord);
+ },
+
+ stop: function(){
+ this.listener.removeEvent('mousemove', this.coord);
+ this.timer = $clear(this.timer);
+ },
+
+ getCoords: function(event){
+ this.page = (this.listener.get('tag') == 'body') ? event.client :
event.page;
+ if (!this.timer) this.timer = this.scroll.periodical(50, this);
+ },
+
+ scroll: function(){
+ var size = this.element.getSize(), scroll = this.element.getScroll(), pos =
this.element.getPosition(), change = {'x': 0, 'y': 0};
+ for (var z in this.page){
+ if (this.page[z] < (this.options.area + pos[z]) && scroll[z] != 0)
+ change[z] = (this.page[z] - this.options.area - pos[z]) * this.options.velocity;
+ else if (this.page[z] + this.options.area > (size[z] + pos[z]) && size[z] +
size[z] != scroll[z])
+ change[z] = (this.page[z] - size[z] + this.options.area - pos[z]) *
this.options.velocity;
+ }
+ if (change.y || change.x) this.fireEvent('change', [scroll.x + change.x,
scroll.y + change.y]);
+ }
+
+});
+
+/*
+Script: Accordion.js
+ An Fx.Elements extension which allows you to easily create accordion type controls.
+
+License:
+ MIT-style license.
+*/
+
+var Accordion = new Class({
+
+ Extends: Fx.Elements,
+
+ options: {/*
+ onActive: $empty,
+ onBackground: $empty,*/
+ display: 0,
+ show: false,
+ height: true,
+ width: false,
+ opacity: true,
+ fixedHeight: false,
+ fixedWidth: false,
+ wait: false,
+ alwaysHide: false
+ },
+
+ initialize: function(){
+ var params = Array.link(arguments, {'container': Element.type,
'options': Object.type, 'togglers': $defined, 'elements':
$defined});
+ this.parent(params.elements, params.options);
+ this.togglers = $$(params.togglers);
+ this.container = $(params.container);
+ this.previous = -1;
+ if (this.options.alwaysHide) this.options.wait = true;
+ if ($chk(this.options.show)){
+ this.options.display = false;
+ this.previous = this.options.show;
+ }
+ if (this.options.start){
+ this.options.display = false;
+ this.options.show = false;
+ }
+ this.effects = {};
+ if (this.options.opacity) this.effects.opacity = 'fullOpacity';
+ if (this.options.width) this.effects.width = this.options.fixedWidth ?
'fullWidth' : 'offsetWidth';
+ if (this.options.height) this.effects.height = this.options.fixedHeight ?
'fullHeight' : 'scrollHeight';
+ for (var i = 0, l = this.togglers.length; i < l; i++)
this.addSection(this.togglers[i], this.elements[i]);
+ this.elements.each(function(el, i){
+ if (this.options.show === i){
+ this.fireEvent('active', [this.togglers[i], el]);
+ } else {
+ for (var fx in this.effects) el.setStyle(fx, 0);
+ }
+ }, this);
+ if ($chk(this.options.display)) this.display(this.options.display);
+ },
+
+ addSection: function(toggler, element, pos){
+ toggler = $(toggler);
+ element = $(element);
+ var test = this.togglers.contains(toggler);
+ var len = this.togglers.length;
+ this.togglers.include(toggler);
+ this.elements.include(element);
+ if (len && (!test || pos)){
+ pos = $pick(pos, len - 1);
+ toggler.inject(this.togglers[pos], 'before');
+ element.inject(toggler, 'after');
+ } else if (this.container && !test){
+ toggler.inject(this.container);
+ element.inject(this.container);
+ }
+ var idx = this.togglers.indexOf(toggler);
+ toggler.addEvent('click', this.display.bind(this, idx));
+ if (this.options.height) element.setStyles({'padding-top': 0,
'border-top': 'none', 'padding-bottom': 0,
'border-bottom': 'none'});
+ if (this.options.width) element.setStyles({'padding-left': 0,
'border-left': 'none', 'padding-right': 0, 'border-right':
'none'});
+ element.fullOpacity = 1;
+ if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;
+ if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;
+ element.setStyle('overflow', 'hidden');
+ if (!test){
+ for (var fx in this.effects) element.setStyle(fx, 0);
+ }
+ return this;
+ },
+
+ display: function(index){
+ index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
+ if ((this.timer && this.options.wait) || (index === this.previous &&
!this.options.alwaysHide)) return this;
+ this.previous = index;
+ var obj = {};
+ this.elements.each(function(el, i){
+ obj[i] = {};
+ var hide = (i != index) || (this.options.alwaysHide && (el.offsetHeight >
0));
+ this.fireEvent(hide ? 'background' : 'active', [this.togglers[i],
el]);
+ for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
+ }, this);
+ return this.start(obj);
+ }
+
+});
\ No newline at end of file
commit 79470812f74ebe31a192d49d0fec17bab04aef16
Merge: df1bccc... bdc0667...
Author: Luke Macken <lmacken(a)redhat.com>
Date: Tue Jun 30 23:30:58 2009 -0400
Merge branch 'master' of
git+ssh://git.fedorahosted.org/git/moksha
commit df1bccc20b123567f08cec39f84a7d83c4dbd95b
Merge: caa38fa... 154fe2a...
Author: Luke Macken <lmacken(a)redhat.com>
Date: Fri Jun 12 17:13:49 2009 -0400
Merge branch 'master' of
git+ssh://git.fedorahosted.org/git/moksha
commit caa38fa7031eb7ebdd65cdd66460bed2a6efe862
Merge: 2261aa2... 767aa35...
Author: Luke Macken <lmacken(a)redhat.com>
Date: Tue Jun 2 20:56:04 2009 -0400
Merge branch 'master' of
git+ssh://git.fedorahosted.org/git/moksha
commit 2261aa2ae0978f75bb5457ceceaab43b77e2c4b7
Merge: b75a5f0... c8c5b99...
Author: Luke Macken <lmacken(a)redhat.com>
Date: Thu May 21 23:21:48 2009 -0400
Merge branch 'master' of
git+ssh://git.fedorahosted.org/git/moksha
commit b75a5f0c44477b9c7d7628fe792277f20fc6a846
Author: Luke Macken <lmacken(a)redhat.com>
Date: Thu May 21 23:21:40 2009 -0400
We're no longer using the flat-file feed cache, and link to the TG2 fedora wiki
page
diff --git a/docs/main/GettingStarted.rst b/docs/main/GettingStarted.rst
index e76d1f7..ebcae16 100644
--- a/docs/main/GettingStarted.rst
+++ b/docs/main/GettingStarted.rst
@@ -7,16 +7,17 @@ Getting Started
This guide will help quickly get you up and running with a local copy of
Moksha. It will run the Moksha WSGI application using the Paste threaded http
server, a single orbited daemon with an embeded MorbidQ stomp message broker,
-flat-file feed caches, SQLite SQLAlchemy databases, and an in-memory cache.
-
-This setup is meant to be dead-simple to get up and running, and is not
-designed for production deployments.
+SQLite SQLAlchemy and Feed databases, and an in-memory cache. This setup is
+meant to be dead-simple to get up and running, and is not designed for
+production deployments.
At the moment, all of Moksha's dependencies are not all in Fedora. They are
all currently under review, but in the mean time these instructions will run
Moksha within a virtual Python environment, without changing your global
site-packages.
+You can track the progress of getting TurboGears2 into Fedora `here
<
http://fedoraproject.org/wiki/TurboGears2>`_.
+
Installing the necessary dependencies
-------------------------------------