JBoss Portal SVN: r9939 - in modules/portlet/trunk/test/src/test/resources/simple-portal-war: layouts/nav and 1 other directory.
by portal-commits@lists.jboss.org
Author: wesleyhales
Date: 2008-02-12 08:41:35 -0500 (Tue, 12 Feb 2008)
New Revision: 9939
Added:
modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/wikipedia.jsp
Modified:
modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/home.jsp
modules/portlet/trunk/test/src/test/resources/simple-portal-war/layouts/nav/main.jsp
Log:
wikipedia stuff and new home page
Modified: modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/home.jsp
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/home.jsp 2008-02-12 03:35:38 UTC (rev 9938)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/home.jsp 2008-02-12 13:41:35 UTC (rev 9939)
@@ -7,79 +7,7 @@
<%@ include file="/layouts/header.jsp" %>
<portal:page>
- <div class="home-content full-width">
- <div class="third-width float-right">
- <jbp:portlet name="GoogleMap" applicationName="samples-google-map-portlet" />
- </div>
- <h2 class="title">Demo Page Title</h2>
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
- dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
- ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
- fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
- mollit anim id est laborum.
- <br/>
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
- dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
- ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
- fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
- mollit anim id est laborum.
- <br/>
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
- dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
- ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
- fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
- mollit anim id est laborum.
- <br/>
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
- dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
- ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
- fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
- mollit anim id est laborum.
- <br/>
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
- dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
- ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
- fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
- mollit anim id est laborum.
- <br/>
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
- dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
- ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
- fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
- mollit anim id est laborum.
- <br/>
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
- dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
- ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
- fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
- mollit anim id est laborum.
- <br/>
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
- dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
- ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
- fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
- mollit anim id est laborum.
- <br/>
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
- dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
- ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
- fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
- mollit anim id est laborum.
- <br/>
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
- dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
- ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
- fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
- mollit anim id est laborum.
- <br/>
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
- dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
- ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
- fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
- mollit anim id est laborum.
- <br/>
- </div>
-
+
</portal:page>
<%@ include file="/layouts/footer.jsp" %>
\ No newline at end of file
Copied: modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/wikipedia.jsp (from rev 9936, modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/home.jsp)
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/wikipedia.jsp (rev 0)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/wikipedia.jsp 2008-02-12 13:41:35 UTC (rev 9939)
@@ -0,0 +1,37 @@
+<%@ page contentType="text/html" %>
+<%@ page language="java" %>
+<%@ taglib prefix="jbp" tagdir="/WEB-INF/tags/portal" %>
+<%@ taglib uri="/WEB-INF/portal.tld" prefix="portal" %>
+<%@ page isELIgnored="false" %>
+
+ <%@ include file="/layouts/header.jsp" %>
+
+ <portal:page>
+ <portal:pageparam namespaceURI="urn:jboss:portal:simple:google" localName="zipcode" value="32080"/>
+ <div class="home-content full-width">
+ <div class="third-width float-right">
+ <jbp:portlet name="GoogleMap" applicationName="samples-google-map-portlet"/>
+ </div>
+ <h2 class="title">St. Augustine, Florida</h2>
+ <p>St. Augustine is the county seat of St. Johns County [1], Florida, in the United States. It is the oldest continuously occupied European-established city, and the oldest port in the continental United States.[2] St. Augustine lies in a region of Florida known as The First Coast, which extends from Amelia Island in the north, south to Jacksonville, St. Augustine and Palm Coast. According to the 2000 census, the city population was 11,592; in 2004, the population estimated by the U.S. Census Bureau was 12,157 [1].</p>
+ <br/>
+ <p>St. Augustine was founded by the Spanish in 1565.[2] The first Christian worship service held in a permanent settlement in the continental United States was a Catholic Mass celebrated in St. Augustine. A few settlements were founded prior to St. Augustine but all failed, including the original Pensacola colony in West Florida, founded by Trist�n de Luna y Arellano in 1559, with the area abandoned in 1561 due to hurricanes, famine and warring tribes, and Fort Caroline in what is today Jacksonville, Florida, in 1564. The city was founded by the Spanish admiral Pedro Men�ndez de Avil�s on September 8, 1565. Men�ndez first sighted land on August 28, the feast day of Augustine of Hippo, and consequently named the settlement San Agust�n. Mart�n de Arg�elles was born here one year later in 1566, the first child of European ancestry to be born in what is now the continental United States. This came 21 years before the English settlement at Roanoke Island in Virginia!
Colony, and 42 years before the successful settlements of Santa Fe, New Mexico, and Jamestown, Virginia. In all the territory under the jurisdiction of the United States, only (European) settlements in Puerto Rico are older than St. Augustine, with the oldest being Caparra, founded in 1508, whose inhabitants relocated and founded San Juan, in 1521.
+ <br/> In 1586 St. Augustine was attacked and burned by Sir Francis Drake. In 1668 it was plundered by pirates and most of the inhabitants were killed. In 1702 and 1740 it was unsuccessfully attacked by British forces from their new colonies in the Carolinas and Georgia. The most serious of these came in the latter year, when James Oglethorpe of Georgia allied himself with Ahaya the Cowkeeper, chief of the Alachua band of the Seminole tribe to lay siege to the city.</p>
+ <br/>
+ In 1763, the Treaty of Paris ended the French and Indian War and gave Florida and St. Augustine to the British, an acquisition the British had been unable to take by force and keep due to the strong fort there. St. Augustine came under British rule and served as a Loyalist colony during the American Revolutionary War. The Treaty of Paris in 1783 gave the American colonies north of Florida their independence, and ceded Florida to Spain in recognition of Spanish efforts on behalf of the American colonies during the war.
+ <br/>
+ Florida was under Spanish control again from 1784 to 1821. During this time, Spain was being invaded by Napoleon and was struggling to retain its colonies. Florida no longer held its past importance to Spain. The expanding United States, however, regarded Florida as vital to its interests. In 1821, the Adams-On�s Treaty peaceably turned the Spanish colonies in Florida and, with them, St. Augustine, over to the United States.
+ <br/>
+ Florida was a United States territory until 1845 when it became a U.S. state. In 1861, the American Civil War began and Florida seceded from the Union and joined the Confederacy. Days before Florida seceded, state troops took the fort at St. Augustine from a small Union garrison (January 7, 1861). However, federal troops loyal to the United States Government quickly reoccupied the city (March 11, 1862) and remained in control throughout the four-year-long war. In 1865, Florida rejoined the United States.
+ <br/>
+ Spanish Colonial era buildings still existing in the city include the fortress Castillo de San Marcos. The fortress successfully repelled the British attacks of the 18th century, served as a prison for the Native American leader Osceola in 1837, and was occupied by Union troops during the American Civil War. It was removed from the Army's active duty rolls in 1900 after 205 years of service under five different flags. It is now the Castillo de San Marcos National Monument.
+ <br/>
+ In the late 19th century the railroad came to town, and led by northeastern industrialist Henry Flagler, St. Augustine became a winter resort for the very wealthy. A number of mansions and palatial grand hotels of this era still exist, some converted to other use, such as housing parts of Flagler College and museums. Flagler went on to develop much more of Florida's east coast, including his Florida East Coast Railway which eventually reached Key West in 1912.
+ <br/>
+ The city is a popular tourist attraction, for the rich Spanish Colonial Revival Style architectural heritage as well as elite 19th century architecture. In 1938 the theme park Marineland opened just south of St. Augustine, becoming one of Florida's first themed parks and setting the stage for the development of this industry in the following decades. The city is also one terminus of the Old Spanish Trail, which in the 1920's linked St. Augustine, Florida, to San Diego, California with 3000 miles of roadways.
+
+ </div>
+
+ </portal:page>
+
+ <%@ include file="/layouts/footer.jsp" %>
\ No newline at end of file
Modified: modules/portlet/trunk/test/src/test/resources/simple-portal-war/layouts/nav/main.jsp
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/layouts/nav/main.jsp 2008-02-12 03:35:38 UTC (rev 9938)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/layouts/nav/main.jsp 2008-02-12 13:41:35 UTC (rev 9939)
@@ -15,6 +15,10 @@
<a href="${pageContext.request.contextPath}/demo/home.jsp" class="${fn:substringAfter(pageContext.request.servletPath, 'demo/') == 'home.jsp' ? 'selected' : ''}">Home</a>
</li>
+ <li>
+
+ <a href="${pageContext.request.contextPath}/demo/wikipedia.jsp" class="${fn:substringAfter(pageContext.request.servletPath, 'demo/') == 'wikipedia.jsp' ? 'selected' : ''}">Wikipedia</a>
+ </li>
<li>
<a href="${pageContext.request.contextPath}/demo/demo.jsp" class="${fn:substringAfter(pageContext.request.servletPath, 'demo/') == 'demo.jsp' ? 'selected' : ''}">Demo Portlets</a>
</li>
16 years, 8 months
JBoss Portal SVN: r9938 - docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules.
by portal-commits@lists.jboss.org
Author: mmcallis
Date: 2008-02-11 22:35:38 -0500 (Mon, 11 Feb 2008)
New Revision: 9938
Modified:
docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml
Log:
revising 5.2.1.5. Building your Portlet
adding orderedlist/procedure for building/exploding helloworldportlet
example
Modified: docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml
===================================================================
--- docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml 2008-02-12 01:08:04 UTC (rev 9937)
+++ docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml 2008-02-12 03:35:38 UTC (rev 9938)
@@ -519,41 +519,67 @@
</para>
</sect3>
<sect3 id="first_portlet_build">
- <title>Building your portlet</title>
- <para>If you have downloaded the sample, you can execute the build.xml with ant or inside your IDE.
- Executing the <literal>deploy</literal> target will compile all the source files and produce a
- <literal>helloworldportlet.war</literal> file under
- <literal>HelloWorldPortlet\helloworldportlet.war.</literal>
- </para>
+ <title>Building your Portlet</title>
<para>
- <mediaobject>
- <imageobject>
- <imagedata align="center" fileref="images/tutorials/first_portlet/building.gif" valign="middle"/>
- </imageobject>
- </mediaobject>
- </para>
+ A pre-compiled <filename>HelloWorldPortlet/helloworldportlet.war</filename> file is created after extracting the <ulink url="http://anonsvn.jboss.org/repos/portletswap/portlets/2_6/bundles/HelloWorl..."><filename>HelloWorldPorlet.zip</filename></ulink> file. To manually build the <filename>helloworldportlet.war</filename> file:
+ </para>
+ <para>
+ <orderedlist>
+ <listitem>
+ <para>
+ Change into the <filename>HelloWorldPortlet/</filename> directory, and remove the existing <filename>helloworldportlet.war</filename> file.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Run the <command>ant deploy</command> command:
+ </para>
+ <para>
+ <mediaobject>
+ <imageobject>
+ <imagedata align="center" fileref="images/tutorials/first_portlet/building.gif" valign="middle"/>
+ </imageobject>
+ </mediaobject>
+ </para>
+ </listitem>
+ </orderedlist>
+ </para>
<para>
- If you want to create an expanded WAR directory, after executing the above deploy target, you should
- execute the <literal>explode</literal> target.
+ Expanding the <filename>helloworldportlet.war</filename> allows you to deploy the portlet as an expanded directory, which makes development easier. This gives you access to the XML descriptors, resource files, JSF, and JSP pages. To expand the <filename>helloworldportlet.war</filename> file:
+ </para>
+ <para>
+ <orderedlist>
+ <listitem>
+ <para>
+ If you do not have the <filename>HelloWorldPortlet/helloworldportlet.war</filename> file, change into the run the <filename>HelloWorldPortlet/</filename> directory and run the <command>ant deploy</command> command to create it.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Change into the <filename>HelloWorldPortlet/</filename> directory and run the <command>ant explode</command> command:
+ </para>
+ <para>
+ <mediaobject>
+ <imageobject>
+ <imagedata align="center" fileref="images/tutorials/first_portlet/buildexplode.gif" valign="middle"/>
+ </imageobject>
+ </mediaobject>
+ </para>
+ </listitem>
+ </orderedlist>
+ </para>
+ <para>
+ The <command>ant explode</command> command creates a directory structure such as the following:
+ </para>
+ <para>
<mediaobject>
<imageobject>
- <imagedata align="center" fileref="images/tutorials/first_portlet/buildexplode.gif"
- valign="middle"/>
- </imageobject>
- </mediaobject>
- </para>
- <para>
- The above target will produce the following:
- <mediaobject>
- <imageobject>
<imagedata align="center" fileref="images/tutorials/first_portlet/exploded.gif" valign="middle"/>
</imageobject>
</mediaobject>
- This will deflate <literal>helloworldportlet.war</literal>, and allow you to deploy it as an
- expanded directory. It will work just the same but is easier to work with during development as you can
- easily modify the XML descriptors, resources files, JSF/JSP pages. A simple <literal>touch</literal>
- operation (or equivalent) on the <literal>web.xml</literal> file will let any live JBoss Application
- Server instance know that it needs to hot-redeploy your web application.
+ </para>
+ <para>
+ The expanded directory and the XML descriptors are located in the <filename>HelloWorldPortlet/output/lib/exploded/helloworldportlet.war/</filename> directory. Touching the <filename>web.xml</filename> file will let any live JBoss AS instance know that it needs to hot re-deploy the web application.
</para>
</sect3>
<sect3>
16 years, 8 months
JBoss Portal SVN: r9937 - docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules.
by portal-commits@lists.jboss.org
Author: mmcallis
Date: 2008-02-11 20:08:04 -0500 (Mon, 11 Feb 2008)
New Revision: 9937
Modified:
docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml
Log:
5.2.1.4. Application Descriptors final revision,
updating certain literal tags to computeroutput,
as per content in screen tags...
Modified: docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml
===================================================================
--- docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml 2008-02-11 21:47:47 UTC (rev 9936)
+++ docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml 2008-02-12 01:08:04 UTC (rev 9937)
@@ -316,9 +316,8 @@
</supports>]]></screen></term>
<listitem>
<para>
- The <literal><supports></literal> element allows you to declare all the markup types your
- portlet supports in the <literal>render</literal> method. This is accomplished via the
- <literal><mime-type></literal> element, which is required for every portlet. The declared MIME types must match the capability of the portlet. As well, it allows you to pair which modes and window states are supported for each markup type. All portlets must support the VIEW portlet mode, so this does not have to be declared. Use <literal><mime-type></literal> to define which markup type your portlet supports, which in this example, is <literal>text/html</literal>. This section tells the portal that it will only output text and HTML, and that it only supports the VIEW mode.
+ The <computeroutput><supports></computeroutput> element allows you to declare all the markup types your portlet supports in the <literal>render</literal> method. This is accomplished via the
+ <computeroutput><mime-type></computeroutput> element, which is required for every portlet. The declared MIME types must match the capability of the portlet. As well, it allows you to pair which modes and window states are supported for each markup type. All portlets must support the VIEW portlet mode, so this does not have to be declared. Use <computeroutput><mime-type></computeroutput> to define which markup type your portlet supports, which in this example, is <computeroutput>text/html</computeroutput>. This section tells the portal that it will only output text and HTML, and that it only supports the <computeroutput>VIEW</computeroutput> mode.
</para>
</listitem>
</varlistentry>
@@ -336,10 +335,10 @@
</para>
<para>
The <filename>HelloWorldPortlet/WEB-INF/portlet-instances.xml</filename> file is a JBoss Portal specific descriptor, that allows you to create instances of portlets. The
- <literal><portlet-ref></literal> value must match the <literal><portlet-name></literal> value
- given in the <filename>HelloWorldPortlet/WEB-INF/portlet.xml</filename> file. The <literal><instance-id></literal>
- value can be named anything, but it must match the <literal><instance-ref></literal> value given
- in the <literal>*-object.xml</literal> files, which in this example, would be the <filename>HelloWorldPortlet/WEB-INF/helloworld-object.xml</filename> file.
+ <computeroutput><portlet-ref></computeroutput> value must match the <computeroutput><portlet-name></computeroutput> value
+ given in the <filename>HelloWorldPortlet/WEB-INF/portlet.xml</filename> file. The <computeroutput><instance-id></computeroutput>
+ value can be named anything, but it must match the <computeroutput><instance-ref></computeroutput> value given
+ in the <filename>*-object.xml</filename> files, which in this example, would be the <filename>HelloWorldPortlet/WEB-INF/helloworld-object.xml</filename> file.
</para>
<para>
The following is an example of the <filename>HelloWorldPortlet/WEB-INF/portlet-instances.xml</filename> file:
@@ -361,8 +360,8 @@
</screen>
</para>
<para>
- The <literal>*-object.xml</literal> files are JBoss Portal specific descriptors that allow users to
- define the structure of their portal instances, as well as create and configure their windows and
+ The <filename>*-object.xml</filename> files are JBoss Portal specific descriptors that allow users to
+ define the structure of their portal instances, and create and configure their windows and
pages. In the following example:
</para>
<para>
@@ -374,12 +373,12 @@
</listitem>
<listitem>
<para>
- specifies that the window will display the markup generated by the <literal>HelloWorldPortletInstance</literal> portlet instance.
+ specifies that the window will display the markup generated by the <computeroutput>HelloWorldPortletInstance</computeroutput> portlet instance.
</para>
</listitem>
<listitem>
<para>
- the window is assigned to the <literal>default.default</literal> page.
+ the window is assigned to the <computeroutput>default.default</computeroutput> page.
</para>
</listitem>
<listitem>
@@ -419,7 +418,7 @@
</screen></term>
<listitem>
<para>
- Tells the portal where this portlet should appear. In this case, <computeroutput>default.default</computeroutput> specifies that the portlet will appear in the portal instance named <literal>default</literal>, and on the page named <literal>default</literal>.
+ Tells the portal where this portlet should appear. In this case, <computeroutput>default.default</computeroutput> specifies that the portlet will appear in the portal instance named <computeroutput>default</computeroutput>, and on the page named <computeroutput>default</computeroutput>.
</para>
</listitem>
</varlistentry>
@@ -437,7 +436,7 @@
</screen></term>
<listitem>
<para>
- Can be named anything.
+ This can be named anything.
</para>
</listitem>
</varlistentry>
@@ -446,8 +445,8 @@
</screen></term>
<listitem>
<para>
- The value of <literal><instance-ref></literal> must match the value of one of the
- <literal><instance-id></literal> elements found in the <filename>HelloWorldPortlet/WEB-INF/portlet-instances.xml</filename> file.
+ The value of <computeroutput><instance-ref></computeroutput> must match the value of one of the
+ <computeroutput><instance-id></computeroutput> elements found in the <filename>HelloWorldPortlet/WEB-INF/portlet-instances.xml</filename> file.
</para>
</listitem>
</varlistentry>
@@ -457,7 +456,7 @@
</screen></term>
<listitem>
<para>
- Specifies the layout region, and the order this window will be found on the portal page.
+ Specifies the layout region and the order this window will be found on the portal page.
</para>
</listitem>
</varlistentry>
@@ -505,7 +504,7 @@
</para>
<para>
- This declaration is equivalent to the previous <filename>HelloWorldPortlet/WEB-INF/helloworld-object.xml</filename> example. Use <computeroutput><content-type></computeroutput> to specify the content to display. In this example, the content being displayed by <computeroutput>HelloWorldPortletWindow</computeroutput> is a portlet. The <computeroutput><content-uri></computeroutput> element specifies which content to display, in this example, the <computeroutput>HelloWorldPortletInstance</computeroutput>:
+ This declaration is equivalent to the previous <filename>HelloWorldPortlet/WEB-INF/helloworld-object.xml</filename> example. Use <computeroutput><content-type></computeroutput> to specify the content to display. In this example, the content being displayed by the <computeroutput>HelloWorldPortletWindow</computeroutput> is a <computeroutput>portlet</computeroutput>. The <computeroutput><content-uri></computeroutput> element specifies which content to display, which in this example, is the <computeroutput>HelloWorldPortletInstance</computeroutput>:
</para>
<para>
<screen><![CDATA[
@@ -516,7 +515,7 @@
</screen>
</para>
<para>
- To display certain content or a file, use the <computeroutput>cms</computeroutput> content-type, with the <computeroutput><content-uri></computeroutput> being the path to the file in the CMS. This behavior is pluggable, and it is possible to plug almost any kind of content.
+ To display certain content or a file, use the <computeroutput>cms</computeroutput> content-type, with the <computeroutput><content-uri></computeroutput> element being the path to the file in the CMS. This behavior is pluggable, and it is possible to plug in almost any kind of content.
</para>
</sect3>
<sect3 id="first_portlet_build">
16 years, 8 months
JBoss Portal SVN: r9936 - docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules.
by portal-commits@lists.jboss.org
Author: mmcallis
Date: 2008-02-11 16:47:47 -0500 (Mon, 11 Feb 2008)
New Revision: 9936
Modified:
docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml
Log:
5.2.1.4. Application Descriptors rewriting
me rewrite, seemed to have missed the point...
Modified: docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml
===================================================================
--- docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml 2008-02-11 21:01:10 UTC (rev 9935)
+++ docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml 2008-02-11 21:47:47 UTC (rev 9936)
@@ -505,7 +505,7 @@
</para>
<para>
- This is equivalent to the previous <filename>HelloWorldPortlet/WEB-INF/helloworld-object.xml</filename> example. The content to be display can be specified using <computeroutput><content-type></computeroutput>. In this example, the content being displayed is a portlet. The <computeroutput><content-uri></computeroutput> specifies which content to display, in this case, the <computeroutput>HelloWorldPortletInstance</computeroutput>:
+ This declaration is equivalent to the previous <filename>HelloWorldPortlet/WEB-INF/helloworld-object.xml</filename> example. Use <computeroutput><content-type></computeroutput> to specify the content to display. In this example, the content being displayed by <computeroutput>HelloWorldPortletWindow</computeroutput> is a portlet. The <computeroutput><content-uri></computeroutput> element specifies which content to display, in this example, the <computeroutput>HelloWorldPortletInstance</computeroutput>:
</para>
<para>
<screen><![CDATA[
16 years, 8 months
JBoss Portal SVN: r9935 - docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules.
by portal-commits@lists.jboss.org
Author: mmcallis
Date: 2008-02-11 16:01:10 -0500 (Mon, 11 Feb 2008)
New Revision: 9935
Modified:
docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml
Log:
revising last two paragraphs for
5.2.1.4. Application Descriptors
Modified: docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml
===================================================================
--- docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml 2008-02-11 07:52:14 UTC (rev 9934)
+++ docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml 2008-02-11 21:01:10 UTC (rev 9935)
@@ -476,11 +476,13 @@
</para>
<para>
- Portal 2.6 introduces the notion of <emphasis>content type</emphasis>, which is a generic mechanism to
- specify which content will be displayed by a given portlet window. The <literal>window</literal> section
- of the previous example can be re-written to take advantage of the new content framework, resulting in
- the following deployment descriptor:
- <programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+ JBoss Portal 2.6 introduces the notion of <emphasis>content type</emphasis>, which is a generic mechanism to
+ specify what content will be displayed by a given portlet window. The <computeroutput>window</computeroutput> section
+ of the previous example, <filename>HelloWorldPortlet/WEB-INF/helloworld-object.xml</filename>, can be re-written to take advantage of the new content framework. The following is an example deployment descriptor that uses the new content framework:
+ </para>
+ <para>
+<screen><![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE deployments PUBLIC
"-//JBoss Portal//DTD Portal Object 2.6//EN"
"http://www.jboss.org/portal/dtd/portal-object_2_6.dtd">
@@ -498,17 +500,24 @@
<height>1</height>
</window>
</deployment>
-</deployments>]]></programlisting>
-</para>
-<para>
+</deployments>]]>
+</screen>
+ </para>
+ <para>
- This declaration is equivalent to the previous example. We specify that the content being displayed by
- the <literal>HelloWorldPortletWindow</literal> is a <literal>portlet</literal> content. The content URI
- identifies which content to be displayed, in this case, the <literal>HelloWorldPortletInstance</literal>.
- It is possible to declare windows with a <literal>cms</literal> content type and use directly the path to
- the file in the CMS to make the window show the content of the associated file. That behavior is
- pluggable and it is possible to plug virtually any kind of content.
- </para>
+ This is equivalent to the previous <filename>HelloWorldPortlet/WEB-INF/helloworld-object.xml</filename> example. The content to be display can be specified using <computeroutput><content-type></computeroutput>. In this example, the content being displayed is a portlet. The <computeroutput><content-uri></computeroutput> specifies which content to display, in this case, the <computeroutput>HelloWorldPortletInstance</computeroutput>:
+ </para>
+ <para>
+<screen><![CDATA[
+<content>
+ <content-type>portlet</content-type>
+ <content-uri>HelloWorldPortletInstance</content-uri>
+</content>]]>
+</screen>
+ </para>
+ <para>
+ To display certain content or a file, use the <computeroutput>cms</computeroutput> content-type, with the <computeroutput><content-uri></computeroutput> being the path to the file in the CMS. This behavior is pluggable, and it is possible to plug almost any kind of content.
+ </para>
</sect3>
<sect3 id="first_portlet_build">
<title>Building your portlet</title>
16 years, 8 months
JBoss Portal SVN: r9934 - docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules.
by portal-commits@lists.jboss.org
Author: mmcallis
Date: 2008-02-11 02:52:14 -0500 (Mon, 11 Feb 2008)
New Revision: 9934
Modified:
docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml
Log:
minor rivisions to 5.2.1.4. Application Descriptors
Modified: docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml
===================================================================
--- docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml 2008-02-11 04:56:13 UTC (rev 9933)
+++ docs/branches/JBoss_Portal_Branch_2_6/referenceGuide/en/modules/tutorials.xml 2008-02-11 07:52:14 UTC (rev 9934)
@@ -297,7 +297,7 @@
<portlet-name>HelloWorldPortlet</portlet-name>]]></screen></term>
<listitem>
<para>
- Define the portlet name. It does not have to be the Class name.
+ Define the portlet name. It does not have to be the class name.
</para>
</listitem>
</varlistentry>
@@ -318,11 +318,8 @@
<para>
The <literal><supports></literal> element allows you to declare all the markup types your
portlet supports in the <literal>render</literal> method. This is accomplished via the
- <literal><mime-type></literal> element, which is required for every portlet. The declared MIME types must match the capability of the portlet.
+ <literal><mime-type></literal> element, which is required for every portlet. The declared MIME types must match the capability of the portlet. As well, it allows you to pair which modes and window states are supported for each markup type. All portlets must support the VIEW portlet mode, so this does not have to be declared. Use <literal><mime-type></literal> to define which markup type your portlet supports, which in this example, is <literal>text/html</literal>. This section tells the portal that it will only output text and HTML, and that it only supports the VIEW mode.
</para>
- <para>
- As well, it allows you to pair which modes and window states are supported for each markup type. All portlets must support the VIEW portlet mode, so this does not have to be declared. Use <literal><mime-type></literal> to define which markup type your portlet supports, which in this example, is <literal>text/html</literal>. This section tells the portal that it will only output text and HTML, and that it only supports the VIEW mode.
- </para>
</listitem>
</varlistentry>
<varlistentry><term><screen><![CDATA[
@@ -337,14 +334,15 @@
</varlistentry>
</variablelist>
</para>
-<para>
<para>
The <filename>HelloWorldPortlet/WEB-INF/portlet-instances.xml</filename> file is a JBoss Portal specific descriptor, that allows you to create instances of portlets. The
<literal><portlet-ref></literal> value must match the <literal><portlet-name></literal> value
given in the <filename>HelloWorldPortlet/WEB-INF/portlet.xml</filename> file. The <literal><instance-id></literal>
- value can be named anything, but it must match the <literal><instance-ref></literal> values given
- in the <literal>*-object.xml</literal> files, which in this example, would be <filename>HelloWorldPortlet/WEB-INF/helloworld-object.xml</filename>.
-</para>The following is an example of the <filename>HelloWorldPortlet/WEB-INF/portlet-instances.xml</filename> file:
+ value can be named anything, but it must match the <literal><instance-ref></literal> value given
+ in the <literal>*-object.xml</literal> files, which in this example, would be the <filename>HelloWorldPortlet/WEB-INF/helloworld-object.xml</filename> file.
+ </para>
+<para>
+ The following is an example of the <filename>HelloWorldPortlet/WEB-INF/portlet-instances.xml</filename> file:
</para>
<para>
<screen><![CDATA[
@@ -376,7 +374,7 @@
</listitem>
<listitem>
<para>
- specifies that it will display the markup generated by the <literal>HelloWorldPortletInstance</literal> portlet instance.
+ specifies that the window will display the markup generated by the <literal>HelloWorldPortletInstance</literal> portlet instance.
</para>
</listitem>
<listitem>
@@ -386,7 +384,7 @@
</listitem>
<listitem>
<para>
- specifies where the window will appear on the page, by the <computeroutput><region></computeroutput> element.
+ the <computeroutput><region></computeroutput> element specifies where the window will appear on the page.
</para>
</listitem>
</itemizedlist>
@@ -421,8 +419,7 @@
</screen></term>
<listitem>
<para>
- Tells the portal where this portlet should appear. In this case, <computeroutput>default.default</computeroutput> specifies that this portlet should appear in the portal instance named <literal>default</literal> and the page named
- <literal>default</literal>.
+ Tells the portal where this portlet should appear. In this case, <computeroutput>default.default</computeroutput> specifies that the portlet will appear in the portal instance named <literal>default</literal>, and on the page named <literal>default</literal>.
</para>
</listitem>
</varlistentry>
@@ -431,7 +428,7 @@
</screen></term>
<listitem>
<para>
- Instructs the portal to overwrite or keep this object if it already exists. Possible values are <literal>overwrite</literal> or <literal>keep</literal>. <literal>overwrite</literal> will destroy the existing object and create a new one based on the content of the deployment. <literal>keep</literal> will maintain the existing object deployment or create a new one if it does not yet exist.
+ Instructs the portal to overwrite or keep this object if it already exists. Possible values are <literal>overwrite</literal> or <literal>keep</literal>. The <literal>overwrite</literal> value will destroy the existing object and create a new one based on the content of the deployment. The <literal>keep</literal> option will maintain the existing object deployment or create a new one if it does not exist.
</para>
</listitem>
</varlistentry>
@@ -449,8 +446,8 @@
</screen></term>
<listitem>
<para>
- The value of <literal>instance-ref</literal> must match the value of one of the
- <literal>instance-id</literal> found in <literal>portlet-instances.xml</literal>.
+ The value of <literal><instance-ref></literal> must match the value of one of the
+ <literal><instance-id></literal> elements found in the <filename>HelloWorldPortlet/WEB-INF/portlet-instances.xml</filename> file.
</para>
</listitem>
</varlistentry>
@@ -460,22 +457,23 @@
</screen></term>
<listitem>
<para>
- Specify the layout region and order this window will be found on the portal page.
+ Specifies the layout region, and the order this window will be found on the portal page.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
-
- <para>
- To illustrate the relationship between the descriptors, we have provided this simple diagram
+<para>
+ The following diagram illustrates the relationship between the <filename>portlet.xml</filename>, <filename>portlet-instances.xml</filename>, and <filename>helloworld-object.xml</filename> descriptors:
+</para>
+<para>
<mediaobject>
<imageobject>
<imagedata align="center" fileref="images/tutorials/first_portlet/desc_relationship.gif"
valign="middle"/>
</imageobject>
</mediaobject>
- </para>
+</para>
<para>
Portal 2.6 introduces the notion of <emphasis>content type</emphasis>, which is a generic mechanism to
16 years, 8 months
JBoss Portal SVN: r9933 - modules/portlet/trunk/test/src/test.
by portal-commits@lists.jboss.org
Author: chris.laprun(a)jboss.com
Date: 2008-02-10 23:56:13 -0500 (Sun, 10 Feb 2008)
New Revision: 9933
Modified:
modules/portlet/trunk/test/src/test/build.xml
Log:
- Started packaging effort. Only as42 version has been tested so far.
Modified: modules/portlet/trunk/test/src/test/build.xml
===================================================================
--- modules/portlet/trunk/test/src/test/build.xml 2008-02-11 04:05:50 UTC (rev 9932)
+++ modules/portlet/trunk/test/src/test/build.xml 2008-02-11 04:56:13 UTC (rev 9933)
@@ -404,7 +404,6 @@
<path refid="mc.portal-test-generic"/>
-
<!-- MC 2.0.0.Beta4 -->
<path refid="mc.trove"/>
<path refid="mc.javassist"/>
@@ -688,10 +687,40 @@
</target>
+
+ <macrodef name="generic-package-simple-portal">
+ <attribute name="server"/>
+ <sequential>
+ <copy todir="@{server}/simple-portal" flatten="true">
+ <path>
+ <path location="${dependency.portal-common.jar}"/>
+ <path location="${dependency.portal-common-portal.jar}"/>
+ <path location="${dependency.portal-portlet.jar}"/>
+ <path location="${dependency.portal-web.jar}"/>
+ <path location="${dependency.jsr168api.jar}"/>
+ </path>
+ </copy>
+
+ <copy todir="@{server}/simple-portal/simple-portal.war">
+ <fileset dir="${test.temp.dir}/simple-portal-war"/>
+ </copy>
+ </sequential>
+ </macrodef>
+
<target name="package-simple-portal" depends="prepare_env">
+ <property name="portal.dir" value="${target}/portal"/>
+ <property name="simple.tomcat.dir" value="${portal.dir}/tomcat"/>
+ <property name="simple.as42.dir" value="${portal.dir}/as42"/>
+ <property name="simple.as5.dir" value="${portal.dir}/as5"/>
- <delete dir="${target}/simple-portal"/>
+ <delete dir="${portal.dir}"/>
+ <mkdir dir="${portal.dir}"/>
+ <mkdir dir="${simple.tomcat.dir}"/>
+ <mkdir dir="${simple.as42.dir}"/>
+ <mkdir dir="${simple.as5.dir}"/>
+
+
<jar jarfile="${target}/portlet-test-lib.jar">
<fileset dir="${target}/classes"/>
</jar>
@@ -722,83 +751,77 @@
<path refid="mc.jboss_xb"/>
<path refid="mc.jboss_aop"/>
<path refid="mc.jboss_microcontainer"/>
+ </copy>
- <!-- For Tomcat -->
+ <!-- AS 4.2 -->
+ <generic-package-simple-portal server="${simple.as42.dir}"/>
+
+ <!-- Tomcat -->
+ <generic-package-simple-portal server="${simple.tomcat.dir}"/>
+ <copy todir="${simple.tomcat.dir}/simple-portal/simple-portal.war/WEB-INF/lib" flatten="true">
<path>
<pathelement path="${dependency.log4j.jar}"/>
<pathelement path="${dependency.activation.jar}"/>
<pathelement path="${dependency.xercesImpl.jar}"/>
- <!--<path refid="mc.jaxb-api"/>-->
</path>
-
</copy>
- <mkdir dir="${target}/simple-portal"/>
+ <!-- AS 5 -->
+ <generic-package-simple-portal server="${simple.as5.dir}"/>
- <jar jarfile="${target}/simple-portal/simple-portal.war">
- <fileset dir="${test.temp.dir}/simple-portal-war"/>
- </jar>
-
- <copy todir="${target}/simple-portal" flatten="true">
- <path>
- <path location="${dependency.portal-common.jar}"/>
- <path location="${dependency.portal-common-portal.jar}"/>
- <path location="${dependency.portal-portlet.jar}"/>
- <path location="${dependency.portal-web.jar}"/>
- <path location="${dependency.jsr168api.jar}"/>
- </path>
- </copy>
-
<delete file="${target}/portlet-test-lib.jar"/>
- <antcall target="package-demo-portlets"/>
+ <antcall target="__package-demo-portlets"/>
</target>
- <target name="package-demo-portlets" depends="prepare_env">
+ <target name="__package-demo-portlets" depends="prepare_env">
- <mkdir dir="${test.temp.dir}/portal"/>
- <mkdir dir="${test.temp.dir}/portal/samples"/>
- <mkdir dir="${target}/portal"/>
+ <property name="samples.target.dir" value="${portal.dir}/samples"/>
+ <property name="samples.tmp.dir" value="${test.temp.dir}/portal"/>
- <copy todir="${test.temp.dir}/portal/samples-google-map-portlet-war">
+ <mkdir dir="${samples.tmp.dir}"/>
+ <mkdir dir="${samples.target.dir}"/>
+
+ <copy todir="${samples.tmp.dir}/samples-google-map-portlet-war">
<fileset dir="src/test/resources/portal/samples/google-map-portlet-war"/>
- <fileset dir="${target}/classes" includes="org/jboss/portal/portlet/portal/samples/GoogleClippingPortlet.class"/>
+ <fileset dir="${target}/classes"
+ includes="org/jboss/portal/portlet/portal/samples/GoogleClippingPortlet.class"/>
</copy>
- <jar jarfile="${target}/portal/samples-google-map-portlet.war">
- <fileset dir="${test.temp.dir}/portal/samples-google-map-portlet-war"/>
+ <jar jarfile="${samples.target.dir}/samples-google-map-portlet.war">
+ <fileset dir="${samples.tmp.dir}/samples-google-map-portlet-war"/>
</jar>
- <copy todir="${test.temp.dir}/portal/samples-google-weather-portlet-war">
+ <copy todir="${samples.tmp.dir}/samples-google-weather-portlet-war">
<fileset dir="src/test/resources/portal/samples/google-weather-portlet-war"/>
<fileset dir="${target}/classes" includes="org/jboss/portal/portlet/portal/samples/*.class"/>
</copy>
- <jar jarfile="${target}/portal/samples-google-weather-portlet.war">
- <fileset dir="${test.temp.dir}/portal/samples-google-weather-portlet-war"/>
+ <jar jarfile="${samples.target.dir}/samples-google-weather-portlet.war">
+ <fileset dir="${samples.tmp.dir}/samples-google-weather-portlet-war"/>
</jar>
- <copy todir="${test.temp.dir}/portal/samples-remotecontrol-portlet-war">
+ <copy todir="${samples.tmp.dir}/samples-remotecontrol-portlet-war">
<fileset dir="src/test/resources/portal/samples/remotecontrol-portlet-war"/>
<fileset dir="${target}/classes"
includes="org/jboss/portal/portlet/portal/samples/RemoteControlResourcePortlet.class"/>
</copy>
- <jar jarfile="${target}/portal/samples-remotecontrol-portlet.war">
- <fileset dir="${test.temp.dir}/portal/samples-remotecontrol-portlet-war"/>
+ <jar jarfile="${samples.target.dir}/samples-remotecontrol-portlet.war">
+ <fileset dir="${samples.tmp.dir}/samples-remotecontrol-portlet-war"/>
</jar>
- <copy todir="${test.temp.dir}/portal/samples-cart-event-portlet-war">
+ <copy todir="${samples.tmp.dir}/samples-cart-event-portlet-war">
<fileset dir="src/test/resources/portal/samples/cart-event-portlet-war"/>
<fileset dir="${target}/classes" includes="org/jboss/portal/portlet/portal/samples/event/*.class"/>
</copy>
- <jar jarfile="${target}/portal/samples-cart-event-portlet.war">
- <fileset dir="${test.temp.dir}/portal/samples-cart-event-portlet-war"/>
+ <jar jarfile="${samples.target.dir}/samples-cart-event-portlet.war">
+ <fileset dir="${samples.tmp.dir}/samples-cart-event-portlet-war"/>
</jar>
- <copy todir="${test.temp.dir}/portal/samples-basic-war">
+ <copy todir="${samples.tmp.dir}/samples-basic-war">
<fileset dir="src/test/resources/portal/samples/basic-war"/>
<fileset dir="${target}/classes" includes="org/jboss/portal/portlet/portal/samples/basic/**"/>
</copy>
- <jar jarfile="${target}/portal/samples-basic.war">
- <fileset dir="${test.temp.dir}/portal/samples-basic-war"/>
+ <jar jarfile="${samples.target.dir}/samples-basic.war">
+ <fileset dir="${samples.tmp.dir}/samples-basic-war"/>
</jar>
</target>
16 years, 8 months
JBoss Portal SVN: r9932 - in modules/portlet/trunk/test/src: main/java/org/jboss/portal/portlet/portal/samples/event and 2 other directories.
by portal-commits@lists.jboss.org
Author: wesleyhales
Date: 2008-02-10 23:05:50 -0500 (Sun, 10 Feb 2008)
New Revision: 9932
Modified:
modules/portlet/trunk/test/src/main/java/org/jboss/portal/portlet/portal/samples/basic/HeaderPortlet.java
modules/portlet/trunk/test/src/main/java/org/jboss/portal/portlet/portal/samples/event/CartPortlet.java
modules/portlet/trunk/test/src/main/java/org/jboss/portal/portlet/portal/samples/event/CatalogPortlet.java
modules/portlet/trunk/test/src/test/resources/simple-portal-war/WEB-INF/tags/portal/portlet.tag
modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/demo1.jsp
modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/demo3.jsp
Log:
some script enhancements
Modified: modules/portlet/trunk/test/src/main/java/org/jboss/portal/portlet/portal/samples/basic/HeaderPortlet.java
===================================================================
--- modules/portlet/trunk/test/src/main/java/org/jboss/portal/portlet/portal/samples/basic/HeaderPortlet.java 2008-02-11 02:57:55 UTC (rev 9931)
+++ modules/portlet/trunk/test/src/main/java/org/jboss/portal/portlet/portal/samples/basic/HeaderPortlet.java 2008-02-11 04:05:50 UTC (rev 9932)
@@ -54,7 +54,7 @@
//
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
- writer.print("<a href='javascript:" + resp.getNamespace() + "_handle()'>Click me to trigger script</a>");
+ writer.print("<div style=\"height:100px;width:100%;text-align:center;padding-top:50px;\"><a href='javascript:" + resp.getNamespace() + "_handle()'>Click me to trigger script</a></div>");
}
public void serveResource(ResourceRequest req, ResourceResponse resp) throws PortletException, IOException
@@ -62,7 +62,8 @@
String namespace =resp.getNamespace();
resp.setContentType("text/javascript");
PrintWriter writer = resp.getWriter();
- writer.print("function " + namespace + "_handle() { alert('Some alert'); }");
+ writer.print("function " + namespace + "_handle() { " +
+ "new Effect.Grow(document.getElementById('header-portlet')) }");
writer.close();
}
}
Modified: modules/portlet/trunk/test/src/main/java/org/jboss/portal/portlet/portal/samples/event/CartPortlet.java
===================================================================
--- modules/portlet/trunk/test/src/main/java/org/jboss/portal/portlet/portal/samples/event/CartPortlet.java 2008-02-11 02:57:55 UTC (rev 9931)
+++ modules/portlet/trunk/test/src/main/java/org/jboss/portal/portlet/portal/samples/event/CartPortlet.java 2008-02-11 04:05:50 UTC (rev 9932)
@@ -52,7 +52,7 @@
if (!items.isEmpty())
{
- writer.append("<table width='95%'><thead><tr align='left'><th width='66%'>Description</th><th align='right'>Price</th></tr></thead><tbody>");
+ writer.append("<table width='95%' id=\"cart-table\"><thead><tr align='left'><th width='66%'>Description</th><th align='right'>Price</th></tr></thead><tbody>");
int total = 0;
for (CatalogItem item : items)
{
Modified: modules/portlet/trunk/test/src/main/java/org/jboss/portal/portlet/portal/samples/event/CatalogPortlet.java
===================================================================
--- modules/portlet/trunk/test/src/main/java/org/jboss/portal/portlet/portal/samples/event/CatalogPortlet.java 2008-02-11 02:57:55 UTC (rev 9931)
+++ modules/portlet/trunk/test/src/main/java/org/jboss/portal/portlet/portal/samples/event/CatalogPortlet.java 2008-02-11 04:05:50 UTC (rev 9932)
@@ -56,7 +56,7 @@
{
addURL.setParameter("id", item.getId());
writer.append("<tr><td>").append(item.getDescription()).append("</td><td>$").append("" + item.getPrice())
- .append("</td><td><a href='").append(addURL.toString()).append("'>Add to cart</a></td></tr>");
+ .append("</td><td><a href='").append(addURL.toString()).append("' onclick=\"new Effect.Highlight(document.getElementById('cart-table'))\">Add to cart</a></td></tr>");
}
writer.append("</tbody></table>");
Modified: modules/portlet/trunk/test/src/test/resources/simple-portal-war/WEB-INF/tags/portal/portlet.tag
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/WEB-INF/tags/portal/portlet.tag 2008-02-11 02:57:55 UTC (rev 9931)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/WEB-INF/tags/portal/portlet.tag 2008-02-11 04:05:50 UTC (rev 9932)
@@ -1,11 +1,13 @@
<%@ tag body-content="scriptless" %>
<%@ attribute name="name" rtexprvalue="true" required="true" %>
<%@ attribute name="applicationName" rtexprvalue="true" required="true" %>
+<%@ attribute name="id" rtexprvalue="true" required="false" %>
<%@ attribute name="content" fragment="true" required="false" %>
<%@ taglib uri="/WEB-INF/portal.tld" prefix="portal" %>
+<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<portal:portlet name="${name}" applicationName="${applicationName}" errorPage="/WEB-INF/jsp/error.jsp">
- <div class="portlet">
+ <div class="portlet" id="${!empty id ? id : name}">
<div class="portlet-frame">
<div class="header full-width">
<div class="header-layer full-width">
Modified: modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/demo1.jsp
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/demo1.jsp 2008-02-11 02:57:55 UTC (rev 9931)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/demo1.jsp 2008-02-11 04:05:50 UTC (rev 9932)
@@ -10,10 +10,10 @@
<portal:pageparam namespaceURI="urn:jboss:portal:simple:google" localName="zipcode" value="80201"/>
<jbp:layout1>
<jsp:attribute name="leftcol">
- <jbp:portlet name="Cart" applicationName="samples-cart-event-portlet"/>
+ <jbp:portlet name="Cart" applicationName="samples-cart-event-portlet" id="cart-portlet"/>
</jsp:attribute>
<jsp:attribute name="rightcol">
- <jbp:portlet name="Catalog" applicationName="samples-cart-event-portlet"/>
+ <jbp:portlet name="Catalog" applicationName="samples-cart-event-portlet" />
</jsp:attribute>
</jbp:layout1>
</portal:page>
Modified: modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/demo3.jsp
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/demo3.jsp 2008-02-11 02:57:55 UTC (rev 9931)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/demo/demo3.jsp 2008-02-11 04:05:50 UTC (rev 9932)
@@ -10,14 +10,11 @@
<portal:pageparam namespaceURI="urn:jboss:portal:simple:google" localName="zipcode" value="80201"/>
<jbp:layout1>
<jsp:attribute name="leftcol">
-
<jbp:portlet name="Foo" applicationName="bar"/>
-
-
</jsp:attribute>
<jsp:attribute name="rightcol">
<jbp:portlet name="ExceptionPortlet" applicationName="samples-basic"/>
- <jbp:portlet name="HeaderPortlet" applicationName="samples-basic"/>
+ <jbp:portlet name="HeaderPortlet" applicationName="samples-basic" id="header-portlet"/>
</jsp:attribute>
</jbp:layout1>
</portal:page>
16 years, 8 months
JBoss Portal SVN: r9931 - modules/portlet/trunk/test/src/test/resources/simple-portal-war/js.
by portal-commits@lists.jboss.org
Author: wesleyhales
Date: 2008-02-10 21:57:55 -0500 (Sun, 10 Feb 2008)
New Revision: 9931
Modified:
modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/shadowbox.js
Log:
small fix
Modified: modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/shadowbox.js
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/shadowbox.js 2008-02-11 02:47:55 UTC (rev 9930)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/shadowbox.js 2008-02-11 02:57:55 UTC (rev 9931)
@@ -69,7 +69,7 @@
*
* @var {String} loadingImage
*/
- loadingImage: 'images/loading.gif',
+ loadingImage: '../images/loading.gif',
/**
* Enable animations.
@@ -118,7 +118,7 @@
*
* @var {String} overlayBgImage
*/
- overlayBgImage: 'images/overlay-85.png',
+ overlayBgImage: '../images/overlay-85.png',
/**
* Automatically play movies.
16 years, 8 months
JBoss Portal SVN: r9930 - in modules/portlet/trunk/test/src/test/resources/simple-portal-war: WEB-INF/jsp and 5 other directories.
by portal-commits@lists.jboss.org
Author: wesleyhales
Date: 2008-02-10 21:47:55 -0500 (Sun, 10 Feb 2008)
New Revision: 9930
Added:
modules/portlet/trunk/test/src/test/resources/simple-portal-war/css/shadowbox-light.css
modules/portlet/trunk/test/src/test/resources/simple-portal-war/css/shadowbox.css
modules/portlet/trunk/test/src/test/resources/simple-portal-war/images/loading-light.gif
modules/portlet/trunk/test/src/test/resources/simple-portal-war/images/loading.gif
modules/portlet/trunk/test/src/test/resources/simple-portal-war/images/overlay-85.png
modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/
modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/prototype.js
modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/
modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/builder.js
modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/controls.js
modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/dragdrop.js
modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/effects.js
modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/scriptaculous.js
modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/slider.js
modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/sound.js
modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/unittest.js
modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/shadowbox-prototype.js
modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/shadowbox.js
Modified:
modules/portlet/trunk/test/src/test/resources/simple-portal-war/WEB-INF/jsp/error.jsp
modules/portlet/trunk/test/src/test/resources/simple-portal-war/css/master.css
modules/portlet/trunk/test/src/test/resources/simple-portal-war/layouts/header.jsp
Log:
added prototype, scriptaculous, and error modal
Modified: modules/portlet/trunk/test/src/test/resources/simple-portal-war/WEB-INF/jsp/error.jsp
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/WEB-INF/jsp/error.jsp 2008-02-10 23:55:18 UTC (rev 9929)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/WEB-INF/jsp/error.jsp 2008-02-11 02:47:55 UTC (rev 9930)
@@ -1,3 +1,5 @@
+<%@ page import="org.jboss.portal.common.util.Exceptions" %>
+<%@ page import="java.io.PrintWriter" %>
<%@ page language="java" %>
<div class="portlet">
@@ -25,6 +27,14 @@
</li>
<li>
<span>Exception:</span> ${!empty requestScope['org.jboss.portal.portlet.portal.error.cause'] ? requestScope['org.jboss.portal.portlet.portal.error.cause'] : 'Error cause unavailable'}
+ <%if (request.getAttribute("org.jboss.portal.portlet.portal.error.cause") != null){%>
+ <a class="option" href="#exception-stack-trace" title="${requestScope['org.jboss.portal.portlet.portal.error.cause']}" rel="shadowbox;width=900;height=400">${!empty requestScope['org.jboss.portal.portlet.portal.error.cause'] ? 'View Stack Trace' : ''}</a>
+ <div id="exception-stack-trace" class="hidden">
+ <div class="exception">
+ <%=Exceptions.toHTML((Throwable)request.getAttribute("org.jboss.portal.portlet.portal.error.cause"))%>
+ </div>
+ </div>
+ <%}%>
</li>
<li>
<span>Window id:</span> ${requestScope['org.jboss.portal.portlet.portal.error.window_id']}
Modified: modules/portlet/trunk/test/src/test/resources/simple-portal-war/css/master.css
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/css/master.css 2008-02-10 23:55:18 UTC (rev 9929)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/css/master.css 2008-02-11 02:47:55 UTC (rev 9930)
@@ -94,6 +94,10 @@
}
+.hidden {
+ display: none;
+}
+
/*--------------------------------Decoration*/
h2 {
@@ -281,6 +285,10 @@
color: #cc1100
}
+div.exception code{
+ color: #33ff00;
+}
+
/*-------------------------------------Remote Window*/
div.remote-container {
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/css/shadowbox-light.css
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/css/shadowbox-light.css (rev 0)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/css/shadowbox-light.css 2008-02-11 02:47:55 UTC (rev 9930)
@@ -0,0 +1,211 @@
+/**
+ * The CSS for Shadowbox.
+ *
+ * This file is part of Shadowbox.
+ *
+ * Shadowbox is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Shadowbox 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Shadowbox. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Michael J. I. Jackson <mjijackson(a)gmail.com>
+ * @copyright 2007 Michael J. I. Jackson
+ * @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU LGPL 3.0
+ * @version SVN: $Id: shadowbox.js 150 2007-12-04 18:42:03Z michael $
+ */
+
+/*_________________________________________________________________ Overlay */
+#shadowbox_overlay {
+ visibility: hidden;
+ position: absolute;
+ display: block;
+ margin: 0;
+ padding: 0;
+ top: 0;
+ left: 0;
+ z-index: 998;
+}
+
+/*_______________________________________________________________ Container */
+#shadowbox_container {
+ position: fixed;
+ height: 0px;
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ top: 0;
+ left: 0;
+ z-index: 999;
+ text-align: center; /* centers in quirks and IE */
+}
+
+/*_______________________________________________________________ Shadowbox */
+#shadowbox {
+ visibility: hidden;
+ margin: 0 auto;
+ position: relative;
+ font-family: 'Lucida Grande', Tahoma, sans-serif;
+ text-align: left; /* reset left alignment */
+ color: #888;
+}
+
+/*___________________________________________________________________ Title */
+/*
+HTML snapshot:
+<div id="shadowbox_title">
+ <div id="shadowbox_title_inner">Title Here</div>
+</div>
+*/
+#shadowbox_title {
+ height: 30px;
+ padding: 0;
+ overflow: hidden;
+}
+#shadowbox_title_inner {
+ height: 16px;
+ font-size: 16px;
+ line-height: 16px;
+ white-space: nowrap;
+ padding: 10px 0 4px 10px;
+ background-color: #fff;
+}
+
+/*____________________________________________________________________ Body */
+/*
+HTML snapsnot:
+<div id="shadowbox_body">
+ <div id="shadowbox_body_inner">
+ <img src="my_image.jpg" id="shadowbox_content" height="350" width="580">
+ </div>
+ <div id="shadowbox_loading">
+ <div id="shadowbox_loading_img"></div>
+ <span><a href="javascript:Shadowbox.close();">Cancel</a></span>
+ </div>
+</div>
+*/
+#shadowbox_body {
+ position: relative;
+ overflow: hidden;
+ margin: 0;
+ padding: 0;
+ border: 10px solid #fff;
+ background-color: #eee;
+}
+#shadowbox_body_inner {
+ position: relative;
+ overflow: hidden;
+}
+#shadowbox_loading {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 10000px; /* overflow:hidden will clip this */
+ width: 100%;
+ background-color: #ccc;
+}
+#shadowbox_loading img {
+ float: left;
+ margin: 10px 10px 0 10px;
+}
+#shadowbox_loading span {
+ float: left;
+ margin-top: 16px;
+ font-size: 10px;
+}
+#shadowbox_loading span a,
+#shadowbox_loading span a:link,
+#shadowbox_loading span a:visited {
+ color: #fff;
+}
+#shadowbox_content.html { /* make html content scrollable */
+ height: 100%;
+ width: 100%;
+ overflow: auto;
+}
+
+/*_________________________________________________________________ Toolbar */
+/*
+HTML snapshot:
+<div id="shadowbox_toolbar">
+ <div id="shadowbox_toolbar_inner">
+ <div id="shadowbox_nav_close">
+ <a href="javascript:Shadowbox.close();"><span class="shortcut">C</span>lose</a>
+ </div>
+ <div id="shadowbox_nav_next">
+ <a href="javascript:Shadowbox.next();"><span class="shortcut">N</span>ext</a>
+ </div>
+ <div id="shadowbox_nav_previous">
+ <a href="javascript:Shadowbox.previous();"><span class="shortcut">P</span>revious</a>
+ </div>
+ <div id="shadowbox_counter">
+ <a href="javascript:Shadowbox.change(0);">1</a>
+ <a href="javascript:Shadowbox.change(1);" class="shadowbox_counter_current">2</a>
+ <a href="javascript:Shadowbox.change(2);">3</a>
+ </div>
+ </div>
+</div>
+*/
+#shadowbox_toolbar {
+ margin: 0;
+ padding: 0;
+ height: 26px;
+ overflow: hidden;
+}
+#shadowbox_toolbar_inner {
+ height: 26px;
+ background-color: #fff;
+}
+#shadowbox_toolbar_inner div {
+ padding-top: 6px;
+ font-size: 10px;
+ height: 10px;
+ line-height: 10px;
+}
+#shadowbox_toolbar_inner div span.shortcut {
+ text-decoration: underline;
+ font-weight: bold;
+}
+#shadowbox_toolbar_inner div a {
+ text-decoration: none;
+ color: #888;
+}
+#shadowbox_nav_close,
+#shadowbox_nav_previous,
+#shadowbox_nav_next {
+ float: right;
+ padding-right: 10px;
+}
+#shadowbox_counter {
+ float: left;
+ padding-left: 10px;
+}
+#shadowbox_counter a {
+ padding: 0 4px 0 0;
+}
+#shadowbox_counter a.shadowbox_counter_current {
+ text-decoration: underline;
+ font-weight: bold;
+}
+
+/*________________________________________________________________ Messages */
+div.shadowbox_message {
+ text-align: center;
+ padding: 10px;
+}
+div.shadowbox_message a:link,
+div.shadowbox_message a:visited {
+ text-decoration: none;
+ color: #bed333;
+}
+div.shadowbox_message a:hover,
+div.shadowbox_message a:focus {
+ text-decoration: underline;
+}
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/css/shadowbox.css
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/css/shadowbox.css (rev 0)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/css/shadowbox.css 2008-02-11 02:47:55 UTC (rev 9930)
@@ -0,0 +1,212 @@
+/**
+ * The CSS for Shadowbox.
+ *
+ * This file is part of Shadowbox.
+ *
+ * Shadowbox is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Shadowbox 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Shadowbox. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Michael J. I. Jackson <mjijackson(a)gmail.com>
+ * @copyright 2007 Michael J. I. Jackson
+ * @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU LGPL 3.0
+ * @version SVN: $Id: shadowbox.js 150 2007-12-04 18:42:03Z michael $
+ */
+
+/*_________________________________________________________________ Overlay */
+#shadowbox_overlay {
+ visibility: hidden;
+ position: absolute;
+ display: block;
+ margin: 0;
+ padding: 0;
+ top: 0;
+ left: 0;
+ z-index: 998;
+}
+
+/*_______________________________________________________________ Container */
+#shadowbox_container {
+ position: fixed;
+ height: 0px;
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ top: 0;
+ left: 0;
+ z-index: 999;
+ text-align: center; /* centers in quirks and IE */
+}
+
+/*_______________________________________________________________ Shadowbox */
+#shadowbox {
+ visibility: hidden;
+ margin: 0 auto;
+ position: relative;
+ font-family: 'Lucida Grande', Tahoma, sans-serif;
+ text-align: left; /* reset left alignment */
+}
+
+/*___________________________________________________________________ Title */
+/*
+HTML snapshot:
+<div id="shadowbox_title">
+ <div id="shadowbox_title_inner">Title Here</div>
+</div>
+*/
+#shadowbox_title {
+ height: 25px;
+ padding: 0;
+ overflow: hidden;
+}
+#shadowbox_title_inner {
+ height: 16px;
+ font-size: 16px;
+ line-height: 16px;
+ white-space: nowrap;
+ padding: 5px 0 4px 8px;
+ color: #fff;
+}
+
+/*____________________________________________________________________ Body */
+/*
+HTML snapsnot:
+<div id="shadowbox_body">
+ <div id="shadowbox_body_inner">
+ <img src="my_image.jpg" id="shadowbox_content" height="350" width="580">
+ </div>
+ <div id="shadowbox_loading">
+ <img src="images/loading.gif" alt="loading"></div>
+ <span><a href="javascript:Shadowbox.close();">Cancel</a></span>
+ </div>
+</div>
+*/
+#shadowbox_body {
+ position: relative;
+ overflow: hidden;
+ margin: 0;
+ padding: 0;
+ border: 2px solid #fff;
+ background-color: #000;
+}
+#shadowbox_body_inner {
+ position: relative;
+ overflow: hidden;
+}
+#shadowbox_loading {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 10000px; /* overflow:hidden will clip this */
+ width: 100%;
+ background-color: #222;
+}
+#shadowbox_loading img {
+ float: left;
+ margin: 10px 10px 0 10px;
+}
+#shadowbox_loading span {
+ float: left;
+ margin-top: 16px;
+ font-size: 10px;
+}
+#shadowbox_loading span a,
+#shadowbox_loading span a:link,
+#shadowbox_loading span a:visited {
+ color: #fff;
+}
+
+#shadowbox_content.html { /* make html content scrollable */
+ height: 100%;
+ width: 100%;
+ overflow: auto;
+}
+
+/*_________________________________________________________________ Toolbar */
+/*
+HTML snapshot:
+<div id="shadowbox_toolbar">
+ <div id="shadowbox_toolbar_inner">
+ <div id="shadowbox_nav_close">
+ <a href="javascript:Shadowbox.close();"><span class="shortcut">C</span>lose</a>
+ </div>
+ <div id="shadowbox_nav_next">
+ <a href="javascript:Shadowbox.next();"><span class="shortcut">N</span>ext</a>
+ </div>
+ <div id="shadowbox_nav_previous">
+ <a href="javascript:Shadowbox.previous();"><span class="shortcut">P</span>revious</a>
+ </div>
+ <div id="shadowbox_counter">
+ <a href="javascript:Shadowbox.change(0);">1</a>
+ <a href="javascript:Shadowbox.change(1);" class="shadowbox_counter_current">2</a>
+ <a href="javascript:Shadowbox.change(2);">3</a>
+ </div>
+ </div>
+</div>
+*/
+#shadowbox_toolbar {
+ margin: 0;
+ padding: 0;
+ height: 20px;
+ overflow: hidden;
+}
+#shadowbox_toolbar_inner {
+ height: 20px;
+ color: #fff;
+}
+#shadowbox_toolbar_inner div {
+ padding-top: 4px;
+ padding-bottom: 2px;
+ font-size: 10px;
+ height: 10px;
+ line-height: 10px;
+}
+#shadowbox_toolbar_inner div span.shortcut {
+ text-decoration: underline;
+ font-weight: bold;
+}
+#shadowbox_toolbar_inner div a {
+ text-decoration: none;
+ color: #fff;
+}
+#shadowbox_nav_close,
+#shadowbox_nav_previous,
+#shadowbox_nav_next {
+ float: right;
+ padding-right: 10px;
+}
+#shadowbox_counter {
+ float: left;
+ padding-left: 10px;
+}
+#shadowbox_counter a {
+ padding: 0 4px 0 0;
+}
+#shadowbox_counter a.shadowbox_counter_current {
+ text-decoration: underline;
+ font-weight: bold;
+}
+
+/*________________________________________________________________ Messages */
+div.shadowbox_message {
+ text-align: center;
+ padding: 10px;
+}
+div.shadowbox_message a:link,
+div.shadowbox_message a:visited {
+ text-decoration: none;
+ color: #bed333;
+}
+div.shadowbox_message a:hover,
+div.shadowbox_message a:focus {
+ text-decoration: underline;
+}
Property changes on: modules/portlet/trunk/test/src/test/resources/simple-portal-war/css/shadowbox.css
___________________________________________________________________
Name: svn:executable
+ *
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/images/loading-light.gif
===================================================================
(Binary files differ)
Property changes on: modules/portlet/trunk/test/src/test/resources/simple-portal-war/images/loading-light.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/images/loading.gif
===================================================================
(Binary files differ)
Property changes on: modules/portlet/trunk/test/src/test/resources/simple-portal-war/images/loading.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/images/overlay-85.png
===================================================================
(Binary files differ)
Property changes on: modules/portlet/trunk/test/src/test/resources/simple-portal-war/images/overlay-85.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/prototype.js
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/prototype.js (rev 0)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/prototype.js 2008-02-11 02:47:55 UTC (rev 9930)
@@ -0,0 +1,4221 @@
+/* Prototype JavaScript framework, version 1.6.0.2
+ * (c) 2005-2008 Sam Stephenson
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ * For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.6.0.2',
+
+ Browser: {
+ IE: !!(window.attachEvent && !window.opera),
+ Opera: !!window.opera,
+ WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
+ MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
+ },
+
+ BrowserFeatures: {
+ XPath: !!document.evaluate,
+ ElementExtensions: !!window.HTMLElement,
+ SpecificElementExtensions:
+ document.createElement('div').__proto__ &&
+ document.createElement('div').__proto__ !==
+ document.createElement('form').__proto__
+ },
+
+ ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
+ JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
+
+ emptyFunction: function() { },
+ K: function(x) { return x }
+};
+
+if (Prototype.Browser.MobileSafari)
+ Prototype.BrowserFeatures.SpecificElementExtensions = false;
+
+
+/* Based on Alex Arnell's inheritance implementation. */
+var Class = {
+ create: function() {
+ var parent = null, properties = $A(arguments);
+ if (Object.isFunction(properties[0]))
+ parent = properties.shift();
+
+ function klass() {
+ this.initialize.apply(this, arguments);
+ }
+
+ Object.extend(klass, Class.Methods);
+ klass.superclass = parent;
+ klass.subclasses = [];
+
+ if (parent) {
+ var subclass = function() { };
+ subclass.prototype = parent.prototype;
+ klass.prototype = new subclass;
+ parent.subclasses.push(klass);
+ }
+
+ for (var i = 0; i < properties.length; i++)
+ klass.addMethods(properties[i]);
+
+ if (!klass.prototype.initialize)
+ klass.prototype.initialize = Prototype.emptyFunction;
+
+ klass.prototype.constructor = klass;
+
+ return klass;
+ }
+};
+
+Class.Methods = {
+ addMethods: function(source) {
+ var ancestor = this.superclass && this.superclass.prototype;
+ var properties = Object.keys(source);
+
+ if (!Object.keys({ toString: true }).length)
+ properties.push("toString", "valueOf");
+
+ for (var i = 0, length = properties.length; i < length; i++) {
+ var property = properties[i], value = source[property];
+ if (ancestor && Object.isFunction(value) &&
+ value.argumentNames().first() == "$super") {
+ var method = value, value = Object.extend((function(m) {
+ return function() { return ancestor[m].apply(this, arguments) };
+ })(property).wrap(method), {
+ valueOf: function() { return method },
+ toString: function() { return method.toString() }
+ });
+ }
+ this.prototype[property] = value;
+ }
+
+ return this;
+ }
+};
+
+var Abstract = { };
+
+Object.extend = function(destination, source) {
+ for (var property in source)
+ destination[property] = source[property];
+ return destination;
+};
+
+Object.extend(Object, {
+ inspect: function(object) {
+ try {
+ if (Object.isUndefined(object)) return 'undefined';
+ if (object === null) return 'null';
+ return object.inspect ? object.inspect() : String(object);
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+ },
+
+ toJSON: function(object) {
+ var type = typeof object;
+ switch (type) {
+ case 'undefined':
+ case 'function':
+ case 'unknown': return;
+ case 'boolean': return object.toString();
+ }
+
+ if (object === null) return 'null';
+ if (object.toJSON) return object.toJSON();
+ if (Object.isElement(object)) return;
+
+ var results = [];
+ for (var property in object) {
+ var value = Object.toJSON(object[property]);
+ if (!Object.isUndefined(value))
+ results.push(property.toJSON() + ': ' + value);
+ }
+
+ return '{' + results.join(', ') + '}';
+ },
+
+ toQueryString: function(object) {
+ return $H(object).toQueryString();
+ },
+
+ toHTML: function(object) {
+ return object && object.toHTML ? object.toHTML() : String.interpret(object);
+ },
+
+ keys: function(object) {
+ var keys = [];
+ for (var property in object)
+ keys.push(property);
+ return keys;
+ },
+
+ values: function(object) {
+ var values = [];
+ for (var property in object)
+ values.push(object[property]);
+ return values;
+ },
+
+ clone: function(object) {
+ return Object.extend({ }, object);
+ },
+
+ isElement: function(object) {
+ return object && object.nodeType == 1;
+ },
+
+ isArray: function(object) {
+ return object != null && typeof object == "object" &&
+ 'splice' in object && 'join' in object;
+ },
+
+ isHash: function(object) {
+ return object instanceof Hash;
+ },
+
+ isFunction: function(object) {
+ return typeof object == "function";
+ },
+
+ isString: function(object) {
+ return typeof object == "string";
+ },
+
+ isNumber: function(object) {
+ return typeof object == "number";
+ },
+
+ isUndefined: function(object) {
+ return typeof object == "undefined";
+ }
+});
+
+Object.extend(Function.prototype, {
+ argumentNames: function() {
+ var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
+ return names.length == 1 && !names[0] ? [] : names;
+ },
+
+ bind: function() {
+ if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+ },
+
+ bindAsEventListener: function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function(event) {
+ return __method.apply(object, [event || window.event].concat(args));
+ }
+ },
+
+ curry: function() {
+ if (!arguments.length) return this;
+ var __method = this, args = $A(arguments);
+ return function() {
+ return __method.apply(this, args.concat($A(arguments)));
+ }
+ },
+
+ delay: function() {
+ var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+ return window.setTimeout(function() {
+ return __method.apply(__method, args);
+ }, timeout);
+ },
+
+ wrap: function(wrapper) {
+ var __method = this;
+ return function() {
+ return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+ }
+ },
+
+ methodize: function() {
+ if (this._methodized) return this._methodized;
+ var __method = this;
+ return this._methodized = function() {
+ return __method.apply(null, [this].concat($A(arguments)));
+ };
+ }
+});
+
+Function.prototype.defer = Function.prototype.delay.curry(0.01);
+
+Date.prototype.toJSON = function() {
+ return '"' + this.getUTCFullYear() + '-' +
+ (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+ this.getUTCDate().toPaddedString(2) + 'T' +
+ this.getUTCHours().toPaddedString(2) + ':' +
+ this.getUTCMinutes().toPaddedString(2) + ':' +
+ this.getUTCSeconds().toPaddedString(2) + 'Z"';
+};
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) { }
+ }
+
+ return returnValue;
+ }
+};
+
+RegExp.prototype.match = RegExp.prototype.test;
+
+RegExp.escape = function(str) {
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create({
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ execute: function() {
+ this.callback(this);
+ },
+
+ stop: function() {
+ if (!this.timer) return;
+ clearInterval(this.timer);
+ this.timer = null;
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.execute();
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+});
+Object.extend(String, {
+ interpret: function(value) {
+ return value == null ? '' : String(value);
+ },
+ specialChar: {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '\\': '\\\\'
+ }
+});
+
+Object.extend(String.prototype, {
+ gsub: function(pattern, replacement) {
+ var result = '', source = this, match;
+ replacement = arguments.callee.prepareReplacement(replacement);
+
+ while (source.length > 0) {
+ if (match = source.match(pattern)) {
+ result += source.slice(0, match.index);
+ result += String.interpret(replacement(match));
+ source = source.slice(match.index + match[0].length);
+ } else {
+ result += source, source = '';
+ }
+ }
+ return result;
+ },
+
+ sub: function(pattern, replacement, count) {
+ replacement = this.gsub.prepareReplacement(replacement);
+ count = Object.isUndefined(count) ? 1 : count;
+
+ return this.gsub(pattern, function(match) {
+ if (--count < 0) return match[0];
+ return replacement(match);
+ });
+ },
+
+ scan: function(pattern, iterator) {
+ this.gsub(pattern, iterator);
+ return String(this);
+ },
+
+ truncate: function(length, truncation) {
+ length = length || 30;
+ truncation = Object.isUndefined(truncation) ? '...' : truncation;
+ return this.length > length ?
+ this.slice(0, length - truncation.length) + truncation : String(this);
+ },
+
+ strip: function() {
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
+ },
+
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ },
+
+ evalScripts: function() {
+ return this.extractScripts().map(function(script) { return eval(script) });
+ },
+
+ escapeHTML: function() {
+ var self = arguments.callee;
+ self.text.data = this;
+ return self.div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = new Element('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0] ? (div.childNodes.length > 1 ?
+ $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+ div.childNodes[0].nodeValue) : '';
+ },
+
+ toQueryParams: function(separator) {
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
+ if (!match) return { };
+
+ return match[1].split(separator || '&').inject({ }, function(hash, pair) {
+ if ((pair = pair.split('='))[0]) {
+ var key = decodeURIComponent(pair.shift());
+ var value = pair.length > 1 ? pair.join('=') : pair[0];
+ if (value != undefined) value = decodeURIComponent(value);
+
+ if (key in hash) {
+ if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
+ hash[key].push(value);
+ }
+ else hash[key] = value;
+ }
+ return hash;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ succ: function() {
+ return this.slice(0, this.length - 1) +
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+ },
+
+ times: function(count) {
+ return count < 1 ? '' : new Array(count + 1).join(this);
+ },
+
+ camelize: function() {
+ var parts = this.split('-'), len = parts.length;
+ if (len == 1) return parts[0];
+
+ var camelized = this.charAt(0) == '-'
+ ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+ : parts[0];
+
+ for (var i = 1; i < len; i++)
+ camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+ return camelized;
+ },
+
+ capitalize: function() {
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+ },
+
+ underscore: function() {
+ return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+ },
+
+ dasherize: function() {
+ return this.gsub(/_/,'-');
+ },
+
+ inspect: function(useDoubleQuotes) {
+ var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+ var character = String.specialChar[match[0]];
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+ });
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+ },
+
+ toJSON: function() {
+ return this.inspect(true);
+ },
+
+ unfilterJSON: function(filter) {
+ return this.sub(filter || Prototype.JSONFilter, '#{1}');
+ },
+
+ isJSON: function() {
+ var str = this;
+ if (str.blank()) return false;
+ str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+ return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
+ },
+
+ evalJSON: function(sanitize) {
+ var json = this.unfilterJSON();
+ try {
+ if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+ } catch (e) { }
+ throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+ },
+
+ include: function(pattern) {
+ return this.indexOf(pattern) > -1;
+ },
+
+ startsWith: function(pattern) {
+ return this.indexOf(pattern) === 0;
+ },
+
+ endsWith: function(pattern) {
+ var d = this.length - pattern.length;
+ return d >= 0 && this.lastIndexOf(pattern) === d;
+ },
+
+ empty: function() {
+ return this == '';
+ },
+
+ blank: function() {
+ return /^\s*$/.test(this);
+ },
+
+ interpolate: function(object, pattern) {
+ return new Template(this, pattern).evaluate(object);
+ }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+ escapeHTML: function() {
+ return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
+ },
+ unescapeHTML: function() {
+ return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
+ }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+ if (Object.isFunction(replacement)) return replacement;
+ var template = new Template(replacement);
+ return function(match) { return template.evaluate(match) };
+};
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+ div: document.createElement('div'),
+ text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create({
+ initialize: function(template, pattern) {
+ this.template = template.toString();
+ this.pattern = pattern || Template.Pattern;
+ },
+
+ evaluate: function(object) {
+ if (Object.isFunction(object.toTemplateReplacements))
+ object = object.toTemplateReplacements();
+
+ return this.template.gsub(this.pattern, function(match) {
+ if (object == null) return '';
+
+ var before = match[1] || '';
+ if (before == '\\') return match[2];
+
+ var ctx = object, expr = match[3];
+ var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+ match = pattern.exec(expr);
+ if (match == null) return before;
+
+ while (match != null) {
+ var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+ ctx = ctx[comp];
+ if (null == ctx || '' == match[3]) break;
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+ match = pattern.exec(expr);
+ }
+
+ return before + String.interpret(ctx);
+ });
+ }
+});
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+
+var $break = { };
+
+var Enumerable = {
+ each: function(iterator, context) {
+ var index = 0;
+ iterator = iterator.bind(context);
+ try {
+ this._each(function(value) {
+ iterator(value, index++);
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ return this;
+ },
+
+ eachSlice: function(number, iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var index = -number, slices = [], array = this.toArray();
+ while ((index += number) < array.length)
+ slices.push(array.slice(index, index+number));
+ return slices.collect(iterator, context);
+ },
+
+ all: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!iterator(value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result = false;
+ this.each(function(value, index) {
+ if (result = !!iterator(value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ detect: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var result;
+ this.each(function(value, index) {
+ if (iterator(value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(filter, iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var results = [];
+
+ if (Object.isString(filter))
+ filter = new RegExp(filter);
+
+ this.each(function(value, index) {
+ if (filter.match(value))
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ include: function(object) {
+ if (Object.isFunction(this.indexOf))
+ if (this.indexOf(object) != -1) return true;
+
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inGroupsOf: function(number, fillWith) {
+ fillWith = Object.isUndefined(fillWith) ? null : fillWith;
+ return this.eachSlice(number, function(slice) {
+ while(slice.length < number) slice.push(fillWith);
+ return slice;
+ });
+ },
+
+ inject: function(memo, iterator, context) {
+ iterator = iterator.bind(context);
+ this.each(function(value, index) {
+ memo = iterator(memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.map(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result;
+ this.each(function(value, index) {
+ value = iterator(value, index);
+ if (result == null || value >= result)
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result;
+ this.each(function(value, index) {
+ value = iterator(value, index);
+ if (result == null || value < result)
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ (iterator(value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator, context) {
+ iterator = iterator.bind(context);
+ return this.map(function(value, index) {
+ return {value: value, criteria: iterator(value, index)};
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.map();
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (Object.isFunction(args.last()))
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ return iterator(collections.pluck(index));
+ });
+ },
+
+ size: function() {
+ return this.toArray().length;
+ },
+
+ inspect: function() {
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
+ }
+};
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ filter: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray,
+ every: Enumerable.all,
+ some: Enumerable.any
+});
+function $A(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) return iterable.toArray();
+ var length = iterable.length || 0, results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+}
+
+if (Prototype.Browser.WebKit) {
+ $A = function(iterable) {
+ if (!iterable) return [];
+ if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
+ iterable.toArray) return iterable.toArray();
+ var length = iterable.length || 0, results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+ };
+}
+
+Array.from = $A;
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0, length = this.length; i < length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(Object.isArray(value) ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ reduce: function() {
+ return this.length > 1 ? this : this[0];
+ },
+
+ uniq: function(sorted) {
+ return this.inject([], function(array, value, index) {
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+ array.push(value);
+ return array;
+ });
+ },
+
+ intersect: function(array) {
+ return this.uniq().findAll(function(item) {
+ return array.detect(function(value) { return item === value });
+ });
+ },
+
+ clone: function() {
+ return [].concat(this);
+ },
+
+ size: function() {
+ return this.length;
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ },
+
+ toJSON: function() {
+ var results = [];
+ this.each(function(object) {
+ var value = Object.toJSON(object);
+ if (!Object.isUndefined(value)) results.push(value);
+ });
+ return '[' + results.join(', ') + ']';
+ }
+});
+
+// use native browser JS 1.6 implementation if available
+if (Object.isFunction(Array.prototype.forEach))
+ Array.prototype._each = Array.prototype.forEach;
+
+if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
+ i || (i = 0);
+ var length = this.length;
+ if (i < 0) i = length + i;
+ for (; i < length; i++)
+ if (this[i] === item) return i;
+ return -1;
+};
+
+if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
+ i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+ var n = this.slice(0, i).reverse().indexOf(item);
+ return (n < 0) ? n : i - n - 1;
+};
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+ if (!Object.isString(string)) return [];
+ string = string.strip();
+ return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+ Array.prototype.concat = function() {
+ var array = [];
+ for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ if (Object.isArray(arguments[i])) {
+ for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+ array.push(arguments[i][j]);
+ } else {
+ array.push(arguments[i]);
+ }
+ }
+ return array;
+ };
+}
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ return this.toPaddedString(2, 16);
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator) {
+ $R(0, this, true).each(iterator);
+ return this;
+ },
+
+ toPaddedString: function(length, radix) {
+ var string = this.toString(radix || 10);
+ return '0'.times(length - string.length) + string;
+ },
+
+ toJSON: function() {
+ return isFinite(this) ? this.toString() : 'null';
+ }
+});
+
+$w('abs round ceil floor').each(function(method){
+ Number.prototype[method] = Math[method].methodize();
+});
+function $H(object) {
+ return new Hash(object);
+};
+
+var Hash = Class.create(Enumerable, (function() {
+
+ function toQueryPair(key, value) {
+ if (Object.isUndefined(value)) return key;
+ return key + '=' + encodeURIComponent(String.interpret(value));
+ }
+
+ return {
+ initialize: function(object) {
+ this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+ },
+
+ _each: function(iterator) {
+ for (var key in this._object) {
+ var value = this._object[key], pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ },
+
+ set: function(key, value) {
+ return this._object[key] = value;
+ },
+
+ get: function(key) {
+ return this._object[key];
+ },
+
+ unset: function(key) {
+ var value = this._object[key];
+ delete this._object[key];
+ return value;
+ },
+
+ toObject: function() {
+ return Object.clone(this._object);
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ index: function(value) {
+ var match = this.detect(function(pair) {
+ return pair.value === value;
+ });
+ return match && match.key;
+ },
+
+ merge: function(object) {
+ return this.clone().update(object);
+ },
+
+ update: function(object) {
+ return new Hash(object).inject(this, function(result, pair) {
+ result.set(pair.key, pair.value);
+ return result;
+ });
+ },
+
+ toQueryString: function() {
+ return this.map(function(pair) {
+ var key = encodeURIComponent(pair.key), values = pair.value;
+
+ if (values && typeof values == 'object') {
+ if (Object.isArray(values))
+ return values.map(toQueryPair.curry(key)).join('&');
+ }
+ return toQueryPair(key, values);
+ }).join('&');
+ },
+
+ inspect: function() {
+ return '#<Hash:{' + this.map(function(pair) {
+ return pair.map(Object.inspect).join(': ');
+ }).join(', ') + '}>';
+ },
+
+ toJSON: function() {
+ return Object.toJSON(this.toObject());
+ },
+
+ clone: function() {
+ return new Hash(this);
+ }
+ }
+})());
+
+Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
+Hash.from = $H;
+var ObjectRange = Class.create(Enumerable, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ while (this.include(value)) {
+ iterator(value);
+ value = value.succ();
+ }
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+};
+
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new XMLHttpRequest()},
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+};
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responder) {
+ if (!this.include(responder))
+ this.responders.push(responder);
+ },
+
+ unregister: function(responder) {
+ this.responders = this.responders.without(responder);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (Object.isFunction(responder[callback])) {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) { }
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() { Ajax.activeRequestCount++ },
+ onComplete: function() { Ajax.activeRequestCount-- }
+});
+
+Ajax.Base = Class.create({
+ initialize: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ contentType: 'application/x-www-form-urlencoded',
+ encoding: 'UTF-8',
+ parameters: '',
+ evalJSON: true,
+ evalJS: true
+ };
+ Object.extend(this.options, options || { });
+
+ this.options.method = this.options.method.toLowerCase();
+
+ if (Object.isString(this.options.parameters))
+ this.options.parameters = this.options.parameters.toQueryParams();
+ else if (Object.isHash(this.options.parameters))
+ this.options.parameters = this.options.parameters.toObject();
+ }
+});
+
+Ajax.Request = Class.create(Ajax.Base, {
+ _complete: false,
+
+ initialize: function($super, url, options) {
+ $super(options);
+ this.transport = Ajax.getTransport();
+ this.request(url);
+ },
+
+ request: function(url) {
+ this.url = url;
+ this.method = this.options.method;
+ var params = Object.clone(this.options.parameters);
+
+ if (!['get', 'post'].include(this.method)) {
+ // simulate other verbs over post
+ params['_method'] = this.method;
+ this.method = 'post';
+ }
+
+ this.parameters = params;
+
+ if (params = Object.toQueryString(params)) {
+ // when GET, append parameters to URL
+ if (this.method == 'get')
+ this.url += (this.url.include('?') ? '&' : '?') + params;
+ else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+ params += '&_=';
+ }
+
+ try {
+ var response = new Ajax.Response(this);
+ if (this.options.onCreate) this.options.onCreate(response);
+ Ajax.Responders.dispatch('onCreate', this, response);
+
+ this.transport.open(this.method.toUpperCase(), this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
+
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ this.setRequestHeaders();
+
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+ this.transport.send(this.body);
+
+ /* Force Firefox to handle ready state 4 for synchronous requests */
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
+ this.onStateChange();
+
+ }
+ catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState > 1 && !((readyState == 4) && this._complete))
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ setRequestHeaders: function() {
+ var headers = {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'X-Prototype-Version': Prototype.Version,
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+ };
+
+ if (this.method == 'post') {
+ headers['Content-type'] = this.options.contentType +
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+ /* Force "Connection: close" for older Mozilla browsers to work
+ * around a bug where XMLHttpRequest sends an incorrect
+ * Content-length header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType &&
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+ headers['Connection'] = 'close';
+ }
+
+ // user-defined headers
+ if (typeof this.options.requestHeaders == 'object') {
+ var extras = this.options.requestHeaders;
+
+ if (Object.isFunction(extras.push))
+ for (var i = 0, length = extras.length; i < length; i += 2)
+ headers[extras[i]] = extras[i+1];
+ else
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+ }
+
+ for (var name in headers)
+ this.transport.setRequestHeader(name, headers[name]);
+ },
+
+ success: function() {
+ var status = this.getStatus();
+ return !status || (status >= 200 && status < 300);
+ },
+
+ getStatus: function() {
+ try {
+ return this.transport.status || 0;
+ } catch (e) { return 0 }
+ },
+
+ respondToReadyState: function(readyState) {
+ var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
+
+ if (state == 'Complete') {
+ try {
+ this._complete = true;
+ (this.options['on' + response.status]
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ var contentType = response.getHeader('Content-type');
+ if (this.options.evalJS == 'force'
+ || (this.options.evalJS && this.isSameOrigin() && contentType
+ && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
+ Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if (state == 'Complete') {
+ // avoid memory leak in MSIE: clean up
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ }
+ },
+
+ isSameOrigin: function() {
+ var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+ return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
+ protocol: location.protocol,
+ domain: document.domain,
+ port: location.port ? ':' + location.port : ''
+ }));
+ },
+
+ getHeader: function(name) {
+ try {
+ return this.transport.getResponseHeader(name) || null;
+ } catch (e) { return null }
+ },
+
+ evalResponse: function() {
+ try {
+ return eval((this.transport.responseText || '').unfilterJSON());
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Response = Class.create({
+ initialize: function(request){
+ this.request = request;
+ var transport = this.transport = request.transport,
+ readyState = this.readyState = transport.readyState;
+
+ if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+ this.status = this.getStatus();
+ this.statusText = this.getStatusText();
+ this.responseText = String.interpret(transport.responseText);
+ this.headerJSON = this._getHeaderJSON();
+ }
+
+ if(readyState == 4) {
+ var xml = transport.responseXML;
+ this.responseXML = Object.isUndefined(xml) ? null : xml;
+ this.responseJSON = this._getResponseJSON();
+ }
+ },
+
+ status: 0,
+ statusText: '',
+
+ getStatus: Ajax.Request.prototype.getStatus,
+
+ getStatusText: function() {
+ try {
+ return this.transport.statusText || '';
+ } catch (e) { return '' }
+ },
+
+ getHeader: Ajax.Request.prototype.getHeader,
+
+ getAllHeaders: function() {
+ try {
+ return this.getAllResponseHeaders();
+ } catch (e) { return null }
+ },
+
+ getResponseHeader: function(name) {
+ return this.transport.getResponseHeader(name);
+ },
+
+ getAllResponseHeaders: function() {
+ return this.transport.getAllResponseHeaders();
+ },
+
+ _getHeaderJSON: function() {
+ var json = this.getHeader('X-JSON');
+ if (!json) return null;
+ json = decodeURIComponent(escape(json));
+ try {
+ return json.evalJSON(this.request.options.sanitizeJSON ||
+ !this.request.isSameOrigin());
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ },
+
+ _getResponseJSON: function() {
+ var options = this.request.options;
+ if (!options.evalJSON || (options.evalJSON != 'force' &&
+ !(this.getHeader('Content-type') || '').include('application/json')) ||
+ this.responseText.blank())
+ return null;
+ try {
+ return this.responseText.evalJSON(options.sanitizeJSON ||
+ !this.request.isSameOrigin());
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ }
+});
+
+Ajax.Updater = Class.create(Ajax.Request, {
+ initialize: function($super, container, url, options) {
+ this.container = {
+ success: (container.success || container),
+ failure: (container.failure || (container.success ? null : container))
+ };
+
+ options = Object.clone(options);
+ var onComplete = options.onComplete;
+ options.onComplete = (function(response, json) {
+ this.updateContent(response.responseText);
+ if (Object.isFunction(onComplete)) onComplete(response, json);
+ }).bind(this);
+
+ $super(url, options);
+ },
+
+ updateContent: function(responseText) {
+ var receiver = this.container[this.success() ? 'success' : 'failure'],
+ options = this.options;
+
+ if (!options.evalScripts) responseText = responseText.stripScripts();
+
+ if (receiver = $(receiver)) {
+ if (options.insertion) {
+ if (Object.isString(options.insertion)) {
+ var insertion = { }; insertion[options.insertion] = responseText;
+ receiver.insert(insertion);
+ }
+ else options.insertion(receiver, responseText);
+ }
+ else receiver.update(responseText);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
+ initialize: function($super, container, url, options) {
+ $super(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = { };
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.options.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(response) {
+ if (this.options.decay) {
+ this.decay = (response.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = response.responseText;
+ }
+ this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+function $(element) {
+ if (arguments.length > 1) {
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+ elements.push($(arguments[i]));
+ return elements;
+ }
+ if (Object.isString(element))
+ element = document.getElementById(element);
+ return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+ document._getElementsByXPath = function(expression, parentElement) {
+ var results = [];
+ var query = document.evaluate(expression, $(parentElement) || document,
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
+ results.push(Element.extend(query.snapshotItem(i)));
+ return results;
+ };
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Node) var Node = { };
+
+if (!Node.ELEMENT_NODE) {
+ // DOM level 2 ECMAScript Language Binding
+ Object.extend(Node, {
+ ELEMENT_NODE: 1,
+ ATTRIBUTE_NODE: 2,
+ TEXT_NODE: 3,
+ CDATA_SECTION_NODE: 4,
+ ENTITY_REFERENCE_NODE: 5,
+ ENTITY_NODE: 6,
+ PROCESSING_INSTRUCTION_NODE: 7,
+ COMMENT_NODE: 8,
+ DOCUMENT_NODE: 9,
+ DOCUMENT_TYPE_NODE: 10,
+ DOCUMENT_FRAGMENT_NODE: 11,
+ NOTATION_NODE: 12
+ });
+}
+
+(function() {
+ var element = this.Element;
+ this.Element = function(tagName, attributes) {
+ attributes = attributes || { };
+ tagName = tagName.toLowerCase();
+ var cache = Element.cache;
+ if (Prototype.Browser.IE && attributes.name) {
+ tagName = '<' + tagName + ' name="' + attributes.name + '">';
+ delete attributes.name;
+ return Element.writeAttribute(document.createElement(tagName), attributes);
+ }
+ if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
+ return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
+ };
+ Object.extend(this.Element, element || { });
+}).call(window);
+
+Element.cache = { };
+
+Element.Methods = {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function(element) {
+ element = $(element);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ return element;
+ },
+
+ hide: function(element) {
+ $(element).style.display = 'none';
+ return element;
+ },
+
+ show: function(element) {
+ $(element).style.display = '';
+ return element;
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ return element;
+ },
+
+ update: function(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) return element.update().insert(content);
+ content = Object.toHTML(content);
+ element.innerHTML = content.stripScripts();
+ content.evalScripts.bind(content).defer();
+ return element;
+ },
+
+ replace: function(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ else if (!Object.isElement(content)) {
+ content = Object.toHTML(content);
+ var range = element.ownerDocument.createRange();
+ range.selectNode(element);
+ content.evalScripts.bind(content).defer();
+ content = range.createContextualFragment(content.stripScripts());
+ }
+ element.parentNode.replaceChild(content, element);
+ return element;
+ },
+
+ insert: function(element, insertions) {
+ element = $(element);
+
+ if (Object.isString(insertions) || Object.isNumber(insertions) ||
+ Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+ insertions = {bottom:insertions};
+
+ var content, insert, tagName, childNodes;
+
+ for (var position in insertions) {
+ content = insertions[position];
+ position = position.toLowerCase();
+ insert = Element._insertionTranslations[position];
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ insert(element, content);
+ continue;
+ }
+
+ content = Object.toHTML(content);
+
+ tagName = ((position == 'before' || position == 'after')
+ ? element.parentNode : element).tagName.toUpperCase();
+
+ childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+
+ if (position == 'top' || position == 'after') childNodes.reverse();
+ childNodes.each(insert.curry(element));
+
+ content.evalScripts.bind(content).defer();
+ }
+
+ return element;
+ },
+
+ wrap: function(element, wrapper, attributes) {
+ element = $(element);
+ if (Object.isElement(wrapper))
+ $(wrapper).writeAttribute(attributes || { });
+ else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
+ else wrapper = new Element('div', wrapper);
+ if (element.parentNode)
+ element.parentNode.replaceChild(wrapper, element);
+ wrapper.appendChild(element);
+ return wrapper;
+ },
+
+ inspect: function(element) {
+ element = $(element);
+ var result = '<' + element.tagName.toLowerCase();
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+ var property = pair.first(), attribute = pair.last();
+ var value = (element[property] || '').toString();
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
+ });
+ return result + '>';
+ },
+
+ recursivelyCollect: function(element, property) {
+ element = $(element);
+ var elements = [];
+ while (element = element[property])
+ if (element.nodeType == 1)
+ elements.push(Element.extend(element));
+ return elements;
+ },
+
+ ancestors: function(element) {
+ return $(element).recursivelyCollect('parentNode');
+ },
+
+ descendants: function(element) {
+ return $(element).select("*");
+ },
+
+ firstDescendant: function(element) {
+ element = $(element).firstChild;
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ return $(element);
+ },
+
+ immediateDescendants: function(element) {
+ if (!(element = $(element).firstChild)) return [];
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ if (element) return [element].concat($(element).nextSiblings());
+ return [];
+ },
+
+ previousSiblings: function(element) {
+ return $(element).recursivelyCollect('previousSibling');
+ },
+
+ nextSiblings: function(element) {
+ return $(element).recursivelyCollect('nextSibling');
+ },
+
+ siblings: function(element) {
+ element = $(element);
+ return element.previousSiblings().reverse().concat(element.nextSiblings());
+ },
+
+ match: function(element, selector) {
+ if (Object.isString(selector))
+ selector = new Selector(selector);
+ return selector.match($(element));
+ },
+
+ up: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(element.parentNode);
+ var ancestors = element.ancestors();
+ return Object.isNumber(expression) ? ancestors[expression] :
+ Selector.findElement(ancestors, expression, index);
+ },
+
+ down: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return element.firstDescendant();
+ return Object.isNumber(expression) ? element.descendants()[expression] :
+ element.select(expression)[index || 0];
+ },
+
+ previous: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+ var previousSiblings = element.previousSiblings();
+ return Object.isNumber(expression) ? previousSiblings[expression] :
+ Selector.findElement(previousSiblings, expression, index);
+ },
+
+ next: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+ var nextSiblings = element.nextSiblings();
+ return Object.isNumber(expression) ? nextSiblings[expression] :
+ Selector.findElement(nextSiblings, expression, index);
+ },
+
+ select: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element, args);
+ },
+
+ adjacent: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element.parentNode, args).without(element);
+ },
+
+ identify: function(element) {
+ element = $(element);
+ var id = element.readAttribute('id'), self = arguments.callee;
+ if (id) return id;
+ do { id = 'anonymous_element_' + self.counter++ } while ($(id));
+ element.writeAttribute('id', id);
+ return id;
+ },
+
+ readAttribute: function(element, name) {
+ element = $(element);
+ if (Prototype.Browser.IE) {
+ var t = Element._attributeTranslations.read;
+ if (t.values[name]) return t.values[name](element, name);
+ if (t.names[name]) name = t.names[name];
+ if (name.include(':')) {
+ return (!element.attributes || !element.attributes[name]) ? null :
+ element.attributes[name].value;
+ }
+ }
+ return element.getAttribute(name);
+ },
+
+ writeAttribute: function(element, name, value) {
+ element = $(element);
+ var attributes = { }, t = Element._attributeTranslations.write;
+
+ if (typeof name == 'object') attributes = name;
+ else attributes[name] = Object.isUndefined(value) ? true : value;
+
+ for (var attr in attributes) {
+ name = t.names[attr] || attr;
+ value = attributes[attr];
+ if (t.values[attr]) name = t.values[attr](element, value);
+ if (value === false || value === null)
+ element.removeAttribute(name);
+ else if (value === true)
+ element.setAttribute(name, name);
+ else element.setAttribute(name, value);
+ }
+ return element;
+ },
+
+ getHeight: function(element) {
+ return $(element).getDimensions().height;
+ },
+
+ getWidth: function(element) {
+ return $(element).getDimensions().width;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ var elementClassName = element.className;
+ return (elementClassName.length > 0 && (elementClassName == className ||
+ new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ if (!element.hasClassName(className))
+ element.className += (element.className ? ' ' : '') + className;
+ return element;
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ element.className = element.className.replace(
+ new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
+ return element;
+ },
+
+ toggleClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return element[element.hasClassName(className) ?
+ 'removeClassName' : 'addClassName'](className);
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ var node = element.firstChild;
+ while (node) {
+ var nextNode = node.nextSibling;
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ element.removeChild(node);
+ node = nextNode;
+ }
+ return element;
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.blank();
+ },
+
+ descendantOf: function(element, ancestor) {
+ element = $(element), ancestor = $(ancestor);
+ var originalAncestor = ancestor;
+
+ if (element.compareDocumentPosition)
+ return (element.compareDocumentPosition(ancestor) & 8) === 8;
+
+ if (element.sourceIndex && !Prototype.Browser.Opera) {
+ var e = element.sourceIndex, a = ancestor.sourceIndex,
+ nextAncestor = ancestor.nextSibling;
+ if (!nextAncestor) {
+ do { ancestor = ancestor.parentNode; }
+ while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
+ }
+ if (nextAncestor && nextAncestor.sourceIndex)
+ return (e > a && e < nextAncestor.sourceIndex);
+ }
+
+ while (element = element.parentNode)
+ if (element == originalAncestor) return true;
+ return false;
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var pos = element.cumulativeOffset();
+ window.scrollTo(pos[0], pos[1]);
+ return element;
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ style = style == 'float' ? 'cssFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css[style] : null;
+ }
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+ return value == 'auto' ? null : value;
+ },
+
+ getOpacity: function(element) {
+ return $(element).getStyle('opacity');
+ },
+
+ setStyle: function(element, styles) {
+ element = $(element);
+ var elementStyle = element.style, match;
+ if (Object.isString(styles)) {
+ element.style.cssText += ';' + styles;
+ return styles.include('opacity') ?
+ element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
+ }
+ for (var property in styles)
+ if (property == 'opacity') element.setOpacity(styles[property]);
+ else
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
+ (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
+ property] = styles[property];
+
+ return element;
+ },
+
+ setOpacity: function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+ return element;
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ var display = $(element).getStyle('display');
+ if (display != 'none' && display != null) // Safari bug
+ return {width: element.offsetWidth, height: element.offsetHeight};
+
+ // All *Width and *Height properties give 0 on elements with display none,
+ // so enable the element temporarily
+ var els = element.style;
+ var originalVisibility = els.visibility;
+ var originalPosition = els.position;
+ var originalDisplay = els.display;
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = 'block';
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ els.display = originalDisplay;
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ return {width: originalWidth, height: originalHeight};
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (window.opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ return element;
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ return element;
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return element;
+ element._overflow = Element.getStyle(element, 'overflow') || 'auto';
+ if (element._overflow !== 'hidden')
+ element.style.overflow = 'hidden';
+ return element;
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (!element._overflow) return element;
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+ element._overflow = null;
+ return element;
+ },
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ positionedOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ if (element.tagName == 'BODY') break;
+ var p = Element.getStyle(element, 'position');
+ if (p !== 'static') break;
+ }
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.getStyle('position') == 'absolute') return;
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+ var offsets = element.positionedOffset();
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.width = width + 'px';
+ element.style.height = height + 'px';
+ return element;
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.getStyle('position') == 'relative') return;
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+ return element;
+ },
+
+ cumulativeScrollOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ getOffsetParent: function(element) {
+ if (element.offsetParent) return $(element.offsetParent);
+ if (element == document.body) return $(element);
+
+ while ((element = element.parentNode) && element != document.body)
+ if (Element.getStyle(element, 'position') != 'static')
+ return $(element);
+
+ return $(document.body);
+ },
+
+ viewportOffset: function(forElement) {
+ var valueT = 0, valueL = 0;
+
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ // Safari fix
+ if (element.offsetParent == document.body &&
+ Element.getStyle(element, 'position') == 'absolute') break;
+
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ }
+ } while (element = element.parentNode);
+
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ clonePosition: function(element, source) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || { });
+
+ // find page position of source
+ source = $(source);
+ var p = source.viewportOffset();
+
+ // find coordinate system to use
+ element = $(element);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(element, 'position') == 'absolute') {
+ parent = element.getOffsetParent();
+ delta = parent.viewportOffset();
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
+ if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
+ if (options.setWidth) element.style.width = source.offsetWidth + 'px';
+ if (options.setHeight) element.style.height = source.offsetHeight + 'px';
+ return element;
+ }
+};
+
+Element.Methods.identify.counter = 1;
+
+Object.extend(Element.Methods, {
+ getElementsBySelector: Element.Methods.select,
+ childElements: Element.Methods.immediateDescendants
+});
+
+Element._attributeTranslations = {
+ write: {
+ names: {
+ className: 'class',
+ htmlFor: 'for'
+ },
+ values: { }
+ }
+};
+
+if (Prototype.Browser.Opera) {
+ Element.Methods.getStyle = Element.Methods.getStyle.wrap(
+ function(proceed, element, style) {
+ switch (style) {
+ case 'left': case 'top': case 'right': case 'bottom':
+ if (proceed(element, 'position') === 'static') return null;
+ case 'height': case 'width':
+ // returns '0px' for hidden elements; we want it to return null
+ if (!Element.visible(element)) return null;
+
+ // returns the border-box dimensions rather than the content-box
+ // dimensions, so we subtract padding and borders from the value
+ var dim = parseInt(proceed(element, style), 10);
+
+ if (dim !== element['offset' + style.capitalize()])
+ return dim + 'px';
+
+ var properties;
+ if (style === 'height') {
+ properties = ['border-top-width', 'padding-top',
+ 'padding-bottom', 'border-bottom-width'];
+ }
+ else {
+ properties = ['border-left-width', 'padding-left',
+ 'padding-right', 'border-right-width'];
+ }
+ return properties.inject(dim, function(memo, property) {
+ var val = proceed(element, property);
+ return val === null ? memo : memo - parseInt(val, 10);
+ }) + 'px';
+ default: return proceed(element, style);
+ }
+ }
+ );
+
+ Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
+ function(proceed, element, attribute) {
+ if (attribute === 'title') return element.title;
+ return proceed(element, attribute);
+ }
+ );
+}
+
+else if (Prototype.Browser.IE) {
+ // IE doesn't report offsets correctly for static elements, so we change them
+ // to "relative" to get the values, then change them back.
+ Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
+ function(proceed, element) {
+ element = $(element);
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ }
+ );
+
+ $w('positionedOffset viewportOffset').each(function(method) {
+ Element.Methods[method] = Element.Methods[method].wrap(
+ function(proceed, element) {
+ element = $(element);
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+ // Trigger hasLayout on the offset parent so that IE6 reports
+ // accurate offsetTop and offsetLeft values for position: fixed.
+ var offsetParent = element.getOffsetParent();
+ if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+ offsetParent.setStyle({ zoom: 1 });
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ }
+ );
+ });
+
+ Element.Methods.getStyle = function(element, style) {
+ element = $(element);
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value && element.currentStyle) value = element.currentStyle[style];
+
+ if (style == 'opacity') {
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+ if (value[1]) return parseFloat(value[1]) / 100;
+ return 1.0;
+ }
+
+ if (value == 'auto') {
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+ return element['offset' + style.capitalize()] + 'px';
+ return null;
+ }
+ return value;
+ };
+
+ Element.Methods.setOpacity = function(element, value) {
+ function stripAlpha(filter){
+ return filter.replace(/alpha\([^\)]*\)/gi,'');
+ }
+ element = $(element);
+ var currentStyle = element.currentStyle;
+ if ((currentStyle && !currentStyle.hasLayout) ||
+ (!currentStyle && element.style.zoom == 'normal'))
+ element.style.zoom = 1;
+
+ var filter = element.getStyle('filter'), style = element.style;
+ if (value == 1 || value === '') {
+ (filter = stripAlpha(filter)) ?
+ style.filter = filter : style.removeAttribute('filter');
+ return element;
+ } else if (value < 0.00001) value = 0;
+ style.filter = stripAlpha(filter) +
+ 'alpha(opacity=' + (value * 100) + ')';
+ return element;
+ };
+
+ Element._attributeTranslations = {
+ read: {
+ names: {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ },
+ values: {
+ _getAttr: function(element, attribute) {
+ return element.getAttribute(attribute, 2);
+ },
+ _getAttrNode: function(element, attribute) {
+ var node = element.getAttributeNode(attribute);
+ return node ? node.value : "";
+ },
+ _getEv: function(element, attribute) {
+ attribute = element.getAttribute(attribute);
+ return attribute ? attribute.toString().slice(23, -2) : null;
+ },
+ _flag: function(element, attribute) {
+ return $(element).hasAttribute(attribute) ? attribute : null;
+ },
+ style: function(element) {
+ return element.style.cssText.toLowerCase();
+ },
+ title: function(element) {
+ return element.title;
+ }
+ }
+ }
+ };
+
+ Element._attributeTranslations.write = {
+ names: Object.extend({
+ cellpadding: 'cellPadding',
+ cellspacing: 'cellSpacing'
+ }, Element._attributeTranslations.read.names),
+ values: {
+ checked: function(element, value) {
+ element.checked = !!value;
+ },
+
+ style: function(element, value) {
+ element.style.cssText = value ? value : '';
+ }
+ }
+ };
+
+ Element._attributeTranslations.has = {};
+
+ $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
+ 'encType maxLength readOnly longDesc').each(function(attr) {
+ Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
+ Element._attributeTranslations.has[attr.toLowerCase()] = attr;
+ });
+
+ (function(v) {
+ Object.extend(v, {
+ href: v._getAttr,
+ src: v._getAttr,
+ type: v._getAttr,
+ action: v._getAttrNode,
+ disabled: v._flag,
+ checked: v._flag,
+ readonly: v._flag,
+ multiple: v._flag,
+ onload: v._getEv,
+ onunload: v._getEv,
+ onclick: v._getEv,
+ ondblclick: v._getEv,
+ onmousedown: v._getEv,
+ onmouseup: v._getEv,
+ onmouseover: v._getEv,
+ onmousemove: v._getEv,
+ onmouseout: v._getEv,
+ onfocus: v._getEv,
+ onblur: v._getEv,
+ onkeypress: v._getEv,
+ onkeydown: v._getEv,
+ onkeyup: v._getEv,
+ onsubmit: v._getEv,
+ onreset: v._getEv,
+ onselect: v._getEv,
+ onchange: v._getEv
+ });
+ })(Element._attributeTranslations.read.values);
+}
+
+else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1) ? 0.999999 :
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
+ return element;
+ };
+}
+
+else if (Prototype.Browser.WebKit) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+
+ if (value == 1)
+ if(element.tagName == 'IMG' && element.width) {
+ element.width++; element.width--;
+ } else try {
+ var n = document.createTextNode(' ');
+ element.appendChild(n);
+ element.removeChild(n);
+ } catch (e) { }
+
+ return element;
+ };
+
+ // Safari returns margins on body which is incorrect if the child is absolutely
+ // positioned. For performance reasons, redefine Element#cumulativeOffset for
+ // KHTML/WebKit only.
+ Element.Methods.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return Element._returnOffset(valueL, valueT);
+ };
+}
+
+if (Prototype.Browser.IE || Prototype.Browser.Opera) {
+ // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
+ Element.Methods.update = function(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) return element.update().insert(content);
+
+ content = Object.toHTML(content);
+ var tagName = element.tagName.toUpperCase();
+
+ if (tagName in Element._insertionTranslations.tags) {
+ $A(element.childNodes).each(function(node) { element.removeChild(node) });
+ Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+ .each(function(node) { element.appendChild(node) });
+ }
+ else element.innerHTML = content.stripScripts();
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ };
+}
+
+if ('outerHTML' in document.createElement('div')) {
+ Element.Methods.replace = function(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ element.parentNode.replaceChild(content, element);
+ return element;
+ }
+
+ content = Object.toHTML(content);
+ var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
+
+ if (Element._insertionTranslations.tags[tagName]) {
+ var nextSibling = element.next();
+ var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+ parent.removeChild(element);
+ if (nextSibling)
+ fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
+ else
+ fragments.each(function(node) { parent.appendChild(node) });
+ }
+ else element.outerHTML = content.stripScripts();
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ };
+}
+
+Element._returnOffset = function(l, t) {
+ var result = [l, t];
+ result.left = l;
+ result.top = t;
+ return result;
+};
+
+Element._getContentFromAnonymousElement = function(tagName, html) {
+ var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
+ if (t) {
+ div.innerHTML = t[0] + html + t[1];
+ t[2].times(function() { div = div.firstChild });
+ } else div.innerHTML = html;
+ return $A(div.childNodes);
+};
+
+Element._insertionTranslations = {
+ before: function(element, node) {
+ element.parentNode.insertBefore(node, element);
+ },
+ top: function(element, node) {
+ element.insertBefore(node, element.firstChild);
+ },
+ bottom: function(element, node) {
+ element.appendChild(node);
+ },
+ after: function(element, node) {
+ element.parentNode.insertBefore(node, element.nextSibling);
+ },
+ tags: {
+ TABLE: ['<table>', '</table>', 1],
+ TBODY: ['<table><tbody>', '</tbody></table>', 2],
+ TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
+ TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
+ SELECT: ['<select>', '</select>', 1]
+ }
+};
+
+(function() {
+ Object.extend(this.tags, {
+ THEAD: this.tags.TBODY,
+ TFOOT: this.tags.TBODY,
+ TH: this.tags.TD
+ });
+}).call(Element._insertionTranslations);
+
+Element.Methods.Simulated = {
+ hasAttribute: function(element, attribute) {
+ attribute = Element._attributeTranslations.has[attribute] || attribute;
+ var node = $(element).getAttributeNode(attribute);
+ return node && node.specified;
+ }
+};
+
+Element.Methods.ByTag = { };
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+ document.createElement('div').__proto__) {
+ window.HTMLElement = { };
+ window.HTMLElement.prototype = document.createElement('div').__proto__;
+ Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.extend = (function() {
+ if (Prototype.BrowserFeatures.SpecificElementExtensions)
+ return Prototype.K;
+
+ var Methods = { }, ByTag = Element.Methods.ByTag;
+
+ var extend = Object.extend(function(element) {
+ if (!element || element._extendedByPrototype ||
+ element.nodeType != 1 || element == window) return element;
+
+ var methods = Object.clone(Methods),
+ tagName = element.tagName, property, value;
+
+ // extend methods for specific tags
+ if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+ for (property in methods) {
+ value = methods[property];
+ if (Object.isFunction(value) && !(property in element))
+ element[property] = value.methodize();
+ }
+
+ element._extendedByPrototype = Prototype.emptyFunction;
+ return element;
+
+ }, {
+ refresh: function() {
+ // extend methods for all tags (Safari doesn't need this)
+ if (!Prototype.BrowserFeatures.ElementExtensions) {
+ Object.extend(Methods, Element.Methods);
+ Object.extend(Methods, Element.Methods.Simulated);
+ }
+ }
+ });
+
+ extend.refresh();
+ return extend;
+})();
+
+Element.hasAttribute = function(element, attribute) {
+ if (element.hasAttribute) return element.hasAttribute(attribute);
+ return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+ if (!methods) {
+ Object.extend(Form, Form.Methods);
+ Object.extend(Form.Element, Form.Element.Methods);
+ Object.extend(Element.Methods.ByTag, {
+ "FORM": Object.clone(Form.Methods),
+ "INPUT": Object.clone(Form.Element.Methods),
+ "SELECT": Object.clone(Form.Element.Methods),
+ "TEXTAREA": Object.clone(Form.Element.Methods)
+ });
+ }
+
+ if (arguments.length == 2) {
+ var tagName = methods;
+ methods = arguments[1];
+ }
+
+ if (!tagName) Object.extend(Element.Methods, methods || { });
+ else {
+ if (Object.isArray(tagName)) tagName.each(extend);
+ else extend(tagName);
+ }
+
+ function extend(tagName) {
+ tagName = tagName.toUpperCase();
+ if (!Element.Methods.ByTag[tagName])
+ Element.Methods.ByTag[tagName] = { };
+ Object.extend(Element.Methods.ByTag[tagName], methods);
+ }
+
+ function copy(methods, destination, onlyIfAbsent) {
+ onlyIfAbsent = onlyIfAbsent || false;
+ for (var property in methods) {
+ var value = methods[property];
+ if (!Object.isFunction(value)) continue;
+ if (!onlyIfAbsent || !(property in destination))
+ destination[property] = value.methodize();
+ }
+ }
+
+ function findDOMClass(tagName) {
+ var klass;
+ var trans = {
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+ "FrameSet", "IFRAME": "IFrame"
+ };
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName.capitalize() + 'Element';
+ if (window[klass]) return window[klass];
+
+ window[klass] = { };
+ window[klass].prototype = document.createElement(tagName).__proto__;
+ return window[klass];
+ }
+
+ if (F.ElementExtensions) {
+ copy(Element.Methods, HTMLElement.prototype);
+ copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+ }
+
+ if (F.SpecificElementExtensions) {
+ for (var tag in Element.Methods.ByTag) {
+ var klass = findDOMClass(tag);
+ if (Object.isUndefined(klass)) continue;
+ copy(T[tag], klass.prototype);
+ }
+ }
+
+ Object.extend(Element, Element.Methods);
+ delete Element.ByTag;
+
+ if (Element.extend.refresh) Element.extend.refresh();
+ Element.cache = { };
+};
+
+document.viewport = {
+ getDimensions: function() {
+ var dimensions = { };
+ var B = Prototype.Browser;
+ $w('width height').each(function(d) {
+ var D = d.capitalize();
+ dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
+ (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
+ });
+ return dimensions;
+ },
+
+ getWidth: function() {
+ return this.getDimensions().width;
+ },
+
+ getHeight: function() {
+ return this.getDimensions().height;
+ },
+
+ getScrollOffsets: function() {
+ return Element._returnOffset(
+ window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
+ window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+ }
+};
+/* Portions of the Selector class are derived from Jack Slocum�۪s DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license. Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create({
+ initialize: function(expression) {
+ this.expression = expression.strip();
+ this.compileMatcher();
+ },
+
+ shouldUseXPath: function() {
+ if (!Prototype.BrowserFeatures.XPath) return false;
+
+ var e = this.expression;
+
+ // Safari 3 chokes on :*-of-type and :empty
+ if (Prototype.Browser.WebKit &&
+ (e.include("-of-type") || e.include(":empty")))
+ return false;
+
+ // XPath can't do namespaced attributes, nor can it read
+ // the "checked" property from DOM nodes
+ if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
+ return false;
+
+ return true;
+ },
+
+ compileMatcher: function() {
+ if (this.shouldUseXPath())
+ return this.compileXPathMatcher();
+
+ var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+ c = Selector.criteria, le, p, m;
+
+ if (Selector._cache[e]) {
+ this.matcher = Selector._cache[e];
+ return;
+ }
+
+ this.matcher = ["this.matcher = function(root) {",
+ "var r = root, h = Selector.handlers, c = false, n;"];
+
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
+ new Template(c[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.matcher.push("return h.unique(n);\n}");
+ eval(this.matcher.join('\n'));
+ Selector._cache[this.expression] = this.matcher;
+ },
+
+ compileXPathMatcher: function() {
+ var e = this.expression, ps = Selector.patterns,
+ x = Selector.xpath, le, m;
+
+ if (Selector._cache[e]) {
+ this.xpath = Selector._cache[e]; return;
+ }
+
+ this.matcher = ['.//*'];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ if (m = e.match(ps[i])) {
+ this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
+ new Template(x[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.xpath = this.matcher.join('');
+ Selector._cache[this.expression] = this.xpath;
+ },
+
+ findElements: function(root) {
+ root = root || document;
+ if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+ return this.matcher(root);
+ },
+
+ match: function(element) {
+ this.tokens = [];
+
+ var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
+ var le, p, m;
+
+ while (e && le !== e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ // use the Selector.assertions methods unless the selector
+ // is too complex.
+ if (as[i]) {
+ this.tokens.push([i, Object.clone(m)]);
+ e = e.replace(m[0], '');
+ } else {
+ // reluctantly do a document-wide search
+ // and look for a match in the array
+ return this.findElements(document).include(element);
+ }
+ }
+ }
+ }
+
+ var match = true, name, matches;
+ for (var i = 0, token; token = this.tokens[i]; i++) {
+ name = token[0], matches = token[1];
+ if (!Selector.assertions[name](element, matches)) {
+ match = false; break;
+ }
+ }
+
+ return match;
+ },
+
+ toString: function() {
+ return this.expression;
+ },
+
+ inspect: function() {
+ return "#<Selector:" + this.expression.inspect() + ">";
+ }
+});
+
+Object.extend(Selector, {
+ _cache: { },
+
+ xpath: {
+ descendant: "//*",
+ child: "/*",
+ adjacent: "/following-sibling::*[1]",
+ laterSibling: '/following-sibling::*',
+ tagName: function(m) {
+ if (m[1] == '*') return '';
+ return "[local-name()='" + m[1].toLowerCase() +
+ "' or local-name()='" + m[1].toUpperCase() + "']";
+ },
+ className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+ id: "[@id='#{1}']",
+ attrPresence: function(m) {
+ m[1] = m[1].toLowerCase();
+ return new Template("[@#{1}]").evaluate(m);
+ },
+ attr: function(m) {
+ m[1] = m[1].toLowerCase();
+ m[3] = m[5] || m[6];
+ return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+ },
+ pseudo: function(m) {
+ var h = Selector.xpath.pseudos[m[1]];
+ if (!h) return '';
+ if (Object.isFunction(h)) return h(m);
+ return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+ },
+ operators: {
+ '=': "[@#{1}='#{3}']",
+ '!=': "[@#{1}!='#{3}']",
+ '^=': "[starts-with(@#{1}, '#{3}')]",
+ '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+ '*=': "[contains(@#{1}, '#{3}')]",
+ '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+ '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+ },
+ pseudos: {
+ 'first-child': '[not(preceding-sibling::*)]',
+ 'last-child': '[not(following-sibling::*)]',
+ 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
+ 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+ 'checked': "[@checked]",
+ 'disabled': "[@disabled]",
+ 'enabled': "[not(@disabled)]",
+ 'not': function(m) {
+ var e = m[6], p = Selector.patterns,
+ x = Selector.xpath, le, v;
+
+ var exclusion = [];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in p) {
+ if (m = e.match(p[i])) {
+ v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
+ exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+ return "[not(" + exclusion.join(" and ") + ")]";
+ },
+ 'nth-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+ },
+ 'nth-last-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+ },
+ 'nth-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("position() ", m);
+ },
+ 'nth-last-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+ },
+ 'first-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+ },
+ 'last-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+ },
+ 'only-of-type': function(m) {
+ var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+ },
+ nth: function(fragment, m) {
+ var mm, formula = m[6], predicate;
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ if (mm = formula.match(/^(\d+)$/)) // digit only
+ return '[' + fragment + "= " + mm[1] + ']';
+ if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+ if (mm[1] == "-") mm[1] = -1;
+ var a = mm[1] ? Number(mm[1]) : 1;
+ var b = mm[2] ? Number(mm[2]) : 0;
+ predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+ "((#{fragment} - #{b}) div #{a} >= 0)]";
+ return new Template(predicate).evaluate({
+ fragment: fragment, a: a, b: b });
+ }
+ }
+ }
+ },
+
+ criteria: {
+ tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
+ className: 'n = h.className(n, r, "#{1}", c); c = false;',
+ id: 'n = h.id(n, r, "#{1}", c); c = false;',
+ attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
+ attr: function(m) {
+ m[3] = (m[5] || m[6]);
+ return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
+ },
+ pseudo: function(m) {
+ if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+ return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+ },
+ descendant: 'c = "descendant";',
+ child: 'c = "child";',
+ adjacent: 'c = "adjacent";',
+ laterSibling: 'c = "laterSibling";'
+ },
+
+ patterns: {
+ // combinators must be listed first
+ // (and descendant needs to be last combinator)
+ laterSibling: /^\s*~\s*/,
+ child: /^\s*>\s*/,
+ adjacent: /^\s*\+\s*/,
+ descendant: /^\s/,
+
+ // selectors follow
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
+ id: /^#([\w\-\*]+)(\b|$)/,
+ className: /^\.([\w\-\*]+)(\b|$)/,
+ pseudo:
+/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
+ attrPresence: /^\[([\w]+)\]/,
+ attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+ },
+
+ // for Selector.match and Element#match
+ assertions: {
+ tagName: function(element, matches) {
+ return matches[1].toUpperCase() == element.tagName.toUpperCase();
+ },
+
+ className: function(element, matches) {
+ return Element.hasClassName(element, matches[1]);
+ },
+
+ id: function(element, matches) {
+ return element.id === matches[1];
+ },
+
+ attrPresence: function(element, matches) {
+ return Element.hasAttribute(element, matches[1]);
+ },
+
+ attr: function(element, matches) {
+ var nodeValue = Element.readAttribute(element, matches[1]);
+ return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
+ }
+ },
+
+ handlers: {
+ // UTILITY FUNCTIONS
+ // joins two collections
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ a.push(node);
+ return a;
+ },
+
+ // marks an array of nodes for counting
+ mark: function(nodes) {
+ var _true = Prototype.emptyFunction;
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._countedByPrototype = _true;
+ return nodes;
+ },
+
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._countedByPrototype = undefined;
+ return nodes;
+ },
+
+ // mark each child node with its position (for nth calls)
+ // "ofType" flag indicates whether we're indexing for nth-of-type
+ // rather than nth-child
+ index: function(parentNode, reverse, ofType) {
+ parentNode._countedByPrototype = Prototype.emptyFunction;
+ if (reverse) {
+ for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+ var node = nodes[i];
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+ }
+ } else {
+ for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+ }
+ },
+
+ // filters out duplicates and extends all nodes
+ unique: function(nodes) {
+ if (nodes.length == 0) return nodes;
+ var results = [], n;
+ for (var i = 0, l = nodes.length; i < l; i++)
+ if (!(n = nodes[i])._countedByPrototype) {
+ n._countedByPrototype = Prototype.emptyFunction;
+ results.push(Element.extend(n));
+ }
+ return Selector.handlers.unmark(results);
+ },
+
+ // COMBINATOR FUNCTIONS
+ descendant: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName('*'));
+ return results;
+ },
+
+ child: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ for (var j = 0, child; child = node.childNodes[j]; j++)
+ if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+ }
+ return results;
+ },
+
+ adjacent: function(nodes) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ var next = this.nextElementSibling(node);
+ if (next) results.push(next);
+ }
+ return results;
+ },
+
+ laterSibling: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, Element.nextSiblings(node));
+ return results;
+ },
+
+ nextElementSibling: function(node) {
+ while (node = node.nextSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ previousElementSibling: function(node) {
+ while (node = node.previousSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ // TOKEN FUNCTIONS
+ tagName: function(nodes, root, tagName, combinator) {
+ var uTagName = tagName.toUpperCase();
+ var results = [], h = Selector.handlers;
+ if (nodes) {
+ if (combinator) {
+ // fastlane for ordinary descendant combinators
+ if (combinator == "descendant") {
+ for (var i = 0, node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName(tagName));
+ return results;
+ } else nodes = this[combinator](nodes);
+ if (tagName == "*") return nodes;
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.tagName.toUpperCase() === uTagName) results.push(node);
+ return results;
+ } else return root.getElementsByTagName(tagName);
+ },
+
+ id: function(nodes, root, id, combinator) {
+ var targetNode = $(id), h = Selector.handlers;
+ if (!targetNode) return [];
+ if (!nodes && root == document) return [targetNode];
+ if (nodes) {
+ if (combinator) {
+ if (combinator == 'child') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (targetNode.parentNode == node) return [targetNode];
+ } else if (combinator == 'descendant') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.descendantOf(targetNode, node)) return [targetNode];
+ } else if (combinator == 'adjacent') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Selector.handlers.previousElementSibling(targetNode) == node)
+ return [targetNode];
+ } else nodes = h[combinator](nodes);
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node == targetNode) return [targetNode];
+ return [];
+ }
+ return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+ },
+
+ className: function(nodes, root, className, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ return Selector.handlers.byClassName(nodes, root, className);
+ },
+
+ byClassName: function(nodes, root, className) {
+ if (!nodes) nodes = Selector.handlers.descendant([root]);
+ var needle = ' ' + className + ' ';
+ for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+ nodeClassName = node.className;
+ if (nodeClassName.length == 0) continue;
+ if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+ results.push(node);
+ }
+ return results;
+ },
+
+ attrPresence: function(nodes, root, attr, combinator) {
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ var results = [];
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.hasAttribute(node, attr)) results.push(node);
+ return results;
+ },
+
+ attr: function(nodes, root, attr, value, operator, combinator) {
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ var handler = Selector.operators[operator], results = [];
+ for (var i = 0, node; node = nodes[i]; i++) {
+ var nodeValue = Element.readAttribute(node, attr);
+ if (nodeValue === null) continue;
+ if (handler(nodeValue, value)) results.push(node);
+ }
+ return results;
+ },
+
+ pseudo: function(nodes, name, value, root, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ return Selector.pseudos[name](nodes, value, root);
+ }
+ },
+
+ pseudos: {
+ 'first-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.previousElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'last-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.nextElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'only-child': function(nodes, value, root) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+ results.push(node);
+ return results;
+ },
+ 'nth-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root);
+ },
+ 'nth-last-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true);
+ },
+ 'nth-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, false, true);
+ },
+ 'nth-last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true, true);
+ },
+ 'first-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, false, true);
+ },
+ 'last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, true, true);
+ },
+ 'only-of-type': function(nodes, formula, root) {
+ var p = Selector.pseudos;
+ return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+ },
+
+ // handles the an+b logic
+ getIndices: function(a, b, total) {
+ if (a == 0) return b > 0 ? [b] : [];
+ return $R(1, total).inject([], function(memo, i) {
+ if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+ return memo;
+ });
+ },
+
+ // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+ nth: function(nodes, formula, root, reverse, ofType) {
+ if (nodes.length == 0) return [];
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ var h = Selector.handlers, results = [], indexed = [], m;
+ h.mark(nodes);
+ for (var i = 0, node; node = nodes[i]; i++) {
+ if (!node.parentNode._countedByPrototype) {
+ h.index(node.parentNode, reverse, ofType);
+ indexed.push(node.parentNode);
+ }
+ }
+ if (formula.match(/^\d+$/)) { // just a number
+ formula = Number(formula);
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.nodeIndex == formula) results.push(node);
+ } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+ if (m[1] == "-") m[1] = -1;
+ var a = m[1] ? Number(m[1]) : 1;
+ var b = m[2] ? Number(m[2]) : 0;
+ var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+ for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+ for (var j = 0; j < l; j++)
+ if (node.nodeIndex == indices[j]) results.push(node);
+ }
+ }
+ h.unmark(nodes);
+ h.unmark(indexed);
+ return results;
+ },
+
+ 'empty': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ // IE treats comments as element nodes
+ if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+ results.push(node);
+ }
+ return results;
+ },
+
+ 'not': function(nodes, selector, root) {
+ var h = Selector.handlers, selectorType, m;
+ var exclusions = new Selector(selector).findElements(root);
+ h.mark(exclusions);
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node._countedByPrototype) results.push(node);
+ h.unmark(exclusions);
+ return results;
+ },
+
+ 'enabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node.disabled) results.push(node);
+ return results;
+ },
+
+ 'disabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.disabled) results.push(node);
+ return results;
+ },
+
+ 'checked': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.checked) results.push(node);
+ return results;
+ }
+ },
+
+ operators: {
+ '=': function(nv, v) { return nv == v; },
+ '!=': function(nv, v) { return nv != v; },
+ '^=': function(nv, v) { return nv.startsWith(v); },
+ '$=': function(nv, v) { return nv.endsWith(v); },
+ '*=': function(nv, v) { return nv.include(v); },
+ '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+ '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+ },
+
+ split: function(expression) {
+ var expressions = [];
+ expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+ expressions.push(m[1].strip());
+ });
+ return expressions;
+ },
+
+ matchElements: function(elements, expression) {
+ var matches = $$(expression), h = Selector.handlers;
+ h.mark(matches);
+ for (var i = 0, results = [], element; element = elements[i]; i++)
+ if (element._countedByPrototype) results.push(element);
+ h.unmark(matches);
+ return results;
+ },
+
+ findElement: function(elements, expression, index) {
+ if (Object.isNumber(expression)) {
+ index = expression; expression = false;
+ }
+ return Selector.matchElements(elements, expression || '*')[index || 0];
+ },
+
+ findChildElements: function(element, expressions) {
+ expressions = Selector.split(expressions.join(','));
+ var results = [], h = Selector.handlers;
+ for (var i = 0, l = expressions.length, selector; i < l; i++) {
+ selector = new Selector(expressions[i].strip());
+ h.concat(results, selector.findElements(element));
+ }
+ return (l > 1) ? h.unique(results) : results;
+ }
+});
+
+if (Prototype.Browser.IE) {
+ Object.extend(Selector.handlers, {
+ // IE returns comment nodes on getElementsByTagName("*").
+ // Filter them out.
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ if (node.tagName !== "!") a.push(node);
+ return a;
+ },
+
+ // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node.removeAttribute('_countedByPrototype');
+ return nodes;
+ }
+ });
+}
+
+function $$() {
+ return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+ reset: function(form) {
+ $(form).reset();
+ return form;
+ },
+
+ serializeElements: function(elements, options) {
+ if (typeof options != 'object') options = { hash: !!options };
+ else if (Object.isUndefined(options.hash)) options.hash = true;
+ var key, value, submitted = false, submit = options.submit;
+
+ var data = elements.inject({ }, function(result, element) {
+ if (!element.disabled && element.name) {
+ key = element.name; value = $(element).getValue();
+ if (value != null && (element.type != 'submit' || (!submitted &&
+ submit !== false && (!submit || key == submit) && (submitted = true)))) {
+ if (key in result) {
+ // a key is already present; construct an array of values
+ if (!Object.isArray(result[key])) result[key] = [result[key]];
+ result[key].push(value);
+ }
+ else result[key] = value;
+ }
+ }
+ return result;
+ });
+
+ return options.hash ? data : Object.toQueryString(data);
+ }
+};
+
+Form.Methods = {
+ serialize: function(form, options) {
+ return Form.serializeElements(Form.getElements(form), options);
+ },
+
+ getElements: function(form) {
+ return $A($(form).getElementsByTagName('*')).inject([],
+ function(elements, child) {
+ if (Form.Element.Serializers[child.tagName.toLowerCase()])
+ elements.push(Element.extend(child));
+ return elements;
+ }
+ );
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) || (name && input.name != name))
+ continue;
+ matchingInputs.push(Element.extend(input));
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ form = $(form);
+ Form.getElements(form).invoke('disable');
+ return form;
+ },
+
+ enable: function(form) {
+ form = $(form);
+ Form.getElements(form).invoke('enable');
+ return form;
+ },
+
+ findFirstElement: function(form) {
+ var elements = $(form).getElements().findAll(function(element) {
+ return 'hidden' != element.type && !element.disabled;
+ });
+ var firstByIndex = elements.findAll(function(element) {
+ return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
+ }).sortBy(function(element) { return element.tabIndex }).first();
+
+ return firstByIndex ? firstByIndex : elements.find(function(element) {
+ return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ form = $(form);
+ form.findFirstElement().activate();
+ return form;
+ },
+
+ request: function(form, options) {
+ form = $(form), options = Object.clone(options || { });
+
+ var params = options.parameters, action = form.readAttribute('action') || '';
+ if (action.blank()) action = window.location.href;
+ options.parameters = form.serialize(true);
+
+ if (params) {
+ if (Object.isString(params)) params = params.toQueryParams();
+ Object.extend(options.parameters, params);
+ }
+
+ if (form.hasAttribute('method') && !options.method)
+ options.method = form.method;
+
+ return new Ajax.Request(action, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+ focus: function(element) {
+ $(element).focus();
+ return element;
+ },
+
+ select: function(element) {
+ $(element).select();
+ return element;
+ }
+};
+
+Form.Element.Methods = {
+ serialize: function(element) {
+ element = $(element);
+ if (!element.disabled && element.name) {
+ var value = element.getValue();
+ if (value != undefined) {
+ var pair = { };
+ pair[element.name] = value;
+ return Object.toQueryString(pair);
+ }
+ }
+ return '';
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ return Form.Element.Serializers[method](element);
+ },
+
+ setValue: function(element, value) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ Form.Element.Serializers[method](element, value);
+ return element;
+ },
+
+ clear: function(element) {
+ $(element).value = '';
+ return element;
+ },
+
+ present: function(element) {
+ return $(element).value != '';
+ },
+
+ activate: function(element) {
+ element = $(element);
+ try {
+ element.focus();
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
+ !['button', 'reset', 'submit'].include(element.type)))
+ element.select();
+ } catch (e) { }
+ return element;
+ },
+
+ disable: function(element) {
+ element = $(element);
+ element.blur();
+ element.disabled = true;
+ return element;
+ },
+
+ enable: function(element) {
+ element = $(element);
+ element.disabled = false;
+ return element;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+ input: function(element, value) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element, value);
+ default:
+ return Form.Element.Serializers.textarea(element, value);
+ }
+ },
+
+ inputSelector: function(element, value) {
+ if (Object.isUndefined(value)) return element.checked ? element.value : null;
+ else element.checked = !!value;
+ },
+
+ textarea: function(element, value) {
+ if (Object.isUndefined(value)) return element.value;
+ else element.value = value;
+ },
+
+ select: function(element, index) {
+ if (Object.isUndefined(index))
+ return this[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ else {
+ var opt, value, single = !Object.isArray(index);
+ for (var i = 0, length = element.length; i < length; i++) {
+ opt = element.options[i];
+ value = this.optionValue(opt);
+ if (single) {
+ if (value == index) {
+ opt.selected = true;
+ return;
+ }
+ }
+ else opt.selected = index.include(value);
+ }
+ }
+ },
+
+ selectOne: function(element) {
+ var index = element.selectedIndex;
+ return index >= 0 ? this.optionValue(element.options[index]) : null;
+ },
+
+ selectMany: function(element) {
+ var values, length = element.length;
+ if (!length) return null;
+
+ for (var i = 0, values = []; i < length; i++) {
+ var opt = element.options[i];
+ if (opt.selected) values.push(this.optionValue(opt));
+ }
+ return values;
+ },
+
+ optionValue: function(opt) {
+ // extend element because hasAttribute may not be native
+ return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+ initialize: function($super, element, frequency, callback) {
+ $super(callback, frequency);
+ this.element = $(element);
+ this.lastValue = this.getValue();
+ },
+
+ execute: function() {
+ var value = this.getValue();
+ if (Object.isString(this.lastValue) && Object.isString(value) ?
+ this.lastValue != value : String(this.lastValue) != String(value)) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+});
+
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = Class.create({
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ Form.getElements(this.element).each(this.registerCallback, this);
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ default:
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+});
+
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) var Event = { };
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+ KEY_HOME: 36,
+ KEY_END: 35,
+ KEY_PAGEUP: 33,
+ KEY_PAGEDOWN: 34,
+ KEY_INSERT: 45,
+
+ cache: { },
+
+ relatedTarget: function(event) {
+ var element;
+ switch(event.type) {
+ case 'mouseover': element = event.fromElement; break;
+ case 'mouseout': element = event.toElement; break;
+ default: return null;
+ }
+ return Element.extend(element);
+ }
+});
+
+Event.Methods = (function() {
+ var isButton;
+
+ if (Prototype.Browser.IE) {
+ var buttonMap = { 0: 1, 1: 4, 2: 2 };
+ isButton = function(event, code) {
+ return event.button == buttonMap[code];
+ };
+
+ } else if (Prototype.Browser.WebKit) {
+ isButton = function(event, code) {
+ switch (code) {
+ case 0: return event.which == 1 && !event.metaKey;
+ case 1: return event.which == 1 && event.metaKey;
+ default: return false;
+ }
+ };
+
+ } else {
+ isButton = function(event, code) {
+ return event.which ? (event.which === code + 1) : (event.button === code);
+ };
+ }
+
+ return {
+ isLeftClick: function(event) { return isButton(event, 0) },
+ isMiddleClick: function(event) { return isButton(event, 1) },
+ isRightClick: function(event) { return isButton(event, 2) },
+
+ element: function(event) {
+ var node = Event.extend(event).target;
+ return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
+ },
+
+ findElement: function(event, expression) {
+ var element = Event.element(event);
+ if (!expression) return element;
+ var elements = [element].concat(element.ancestors());
+ return Selector.findElement(elements, expression, 0);
+ },
+
+ pointer: function(event) {
+ return {
+ x: event.pageX || (event.clientX +
+ (document.documentElement.scrollLeft || document.body.scrollLeft)),
+ y: event.pageY || (event.clientY +
+ (document.documentElement.scrollTop || document.body.scrollTop))
+ };
+ },
+
+ pointerX: function(event) { return Event.pointer(event).x },
+ pointerY: function(event) { return Event.pointer(event).y },
+
+ stop: function(event) {
+ Event.extend(event);
+ event.preventDefault();
+ event.stopPropagation();
+ event.stopped = true;
+ }
+ };
+})();
+
+Event.extend = (function() {
+ var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+ m[name] = Event.Methods[name].methodize();
+ return m;
+ });
+
+ if (Prototype.Browser.IE) {
+ Object.extend(methods, {
+ stopPropagation: function() { this.cancelBubble = true },
+ preventDefault: function() { this.returnValue = false },
+ inspect: function() { return "[object Event]" }
+ });
+
+ return function(event) {
+ if (!event) return false;
+ if (event._extendedByPrototype) return event;
+
+ event._extendedByPrototype = Prototype.emptyFunction;
+ var pointer = Event.pointer(event);
+ Object.extend(event, {
+ target: event.srcElement,
+ relatedTarget: Event.relatedTarget(event),
+ pageX: pointer.x,
+ pageY: pointer.y
+ });
+ return Object.extend(event, methods);
+ };
+
+ } else {
+ Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
+ Object.extend(Event.prototype, methods);
+ return Prototype.K;
+ }
+})();
+
+Object.extend(Event, (function() {
+ var cache = Event.cache;
+
+ function getEventID(element) {
+ if (element._prototypeEventID) return element._prototypeEventID[0];
+ arguments.callee.id = arguments.callee.id || 1;
+ return element._prototypeEventID = [++arguments.callee.id];
+ }
+
+ function getDOMEventName(eventName) {
+ if (eventName && eventName.include(':')) return "dataavailable";
+ return eventName;
+ }
+
+ function getCacheForID(id) {
+ return cache[id] = cache[id] || { };
+ }
+
+ function getWrappersForEventName(id, eventName) {
+ var c = getCacheForID(id);
+ return c[eventName] = c[eventName] || [];
+ }
+
+ function createWrapper(element, eventName, handler) {
+ var id = getEventID(element);
+ var c = getWrappersForEventName(id, eventName);
+ if (c.pluck("handler").include(handler)) return false;
+
+ var wrapper = function(event) {
+ if (!Event || !Event.extend ||
+ (event.eventName && event.eventName != eventName))
+ return false;
+
+ Event.extend(event);
+ handler.call(element, event);
+ };
+
+ wrapper.handler = handler;
+ c.push(wrapper);
+ return wrapper;
+ }
+
+ function findWrapper(id, eventName, handler) {
+ var c = getWrappersForEventName(id, eventName);
+ return c.find(function(wrapper) { return wrapper.handler == handler });
+ }
+
+ function destroyWrapper(id, eventName, handler) {
+ var c = getCacheForID(id);
+ if (!c[eventName]) return false;
+ c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
+ }
+
+ function destroyCache() {
+ for (var id in cache)
+ for (var eventName in cache[id])
+ cache[id][eventName] = null;
+ }
+
+ if (window.attachEvent) {
+ window.attachEvent("onunload", destroyCache);
+ }
+
+ return {
+ observe: function(element, eventName, handler) {
+ element = $(element);
+ var name = getDOMEventName(eventName);
+
+ var wrapper = createWrapper(element, eventName, handler);
+ if (!wrapper) return element;
+
+ if (element.addEventListener) {
+ element.addEventListener(name, wrapper, false);
+ } else {
+ element.attachEvent("on" + name, wrapper);
+ }
+
+ return element;
+ },
+
+ stopObserving: function(element, eventName, handler) {
+ element = $(element);
+ var id = getEventID(element), name = getDOMEventName(eventName);
+
+ if (!handler && eventName) {
+ getWrappersForEventName(id, eventName).each(function(wrapper) {
+ element.stopObserving(eventName, wrapper.handler);
+ });
+ return element;
+
+ } else if (!eventName) {
+ Object.keys(getCacheForID(id)).each(function(eventName) {
+ element.stopObserving(eventName);
+ });
+ return element;
+ }
+
+ var wrapper = findWrapper(id, eventName, handler);
+ if (!wrapper) return element;
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, wrapper, false);
+ } else {
+ element.detachEvent("on" + name, wrapper);
+ }
+
+ destroyWrapper(id, eventName, handler);
+
+ return element;
+ },
+
+ fire: function(element, eventName, memo) {
+ element = $(element);
+ if (element == document && document.createEvent && !element.dispatchEvent)
+ element = document.documentElement;
+
+ var event;
+ if (document.createEvent) {
+ event = document.createEvent("HTMLEvents");
+ event.initEvent("dataavailable", true, true);
+ } else {
+ event = document.createEventObject();
+ event.eventType = "ondataavailable";
+ }
+
+ event.eventName = eventName;
+ event.memo = memo || { };
+
+ if (document.createEvent) {
+ element.dispatchEvent(event);
+ } else {
+ element.fireEvent(event.eventType, event);
+ }
+
+ return Event.extend(event);
+ }
+ };
+})());
+
+Object.extend(Event, Event.Methods);
+
+Element.addMethods({
+ fire: Event.fire,
+ observe: Event.observe,
+ stopObserving: Event.stopObserving
+});
+
+Object.extend(document, {
+ fire: Element.Methods.fire.methodize(),
+ observe: Element.Methods.observe.methodize(),
+ stopObserving: Element.Methods.stopObserving.methodize(),
+ loaded: false
+});
+
+(function() {
+ /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+ Matthias Miller, Dean Edwards and John Resig. */
+
+ var timer;
+
+ function fireContentLoadedEvent() {
+ if (document.loaded) return;
+ if (timer) window.clearInterval(timer);
+ document.fire("dom:loaded");
+ document.loaded = true;
+ }
+
+ if (document.addEventListener) {
+ if (Prototype.Browser.WebKit) {
+ timer = window.setInterval(function() {
+ if (/loaded|complete/.test(document.readyState))
+ fireContentLoadedEvent();
+ }, 0);
+
+ Event.observe(window, "load", fireContentLoadedEvent);
+
+ } else {
+ document.addEventListener("DOMContentLoaded",
+ fireContentLoadedEvent, false);
+ }
+
+ } else {
+ document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
+ $("__onDOMContentLoaded").onreadystatechange = function() {
+ if (this.readyState == "complete") {
+ this.onreadystatechange = null;
+ fireContentLoadedEvent();
+ }
+ };
+ }
+})();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.Methods.childOf = Element.Methods.descendantOf;
+
+var Insertion = {
+ Before: function(element, content) {
+ return Element.insert(element, {before:content});
+ },
+
+ Top: function(element, content) {
+ return Element.insert(element, {top:content});
+ },
+
+ Bottom: function(element, content) {
+ return Element.insert(element, {bottom:content});
+ },
+
+ After: function(element, content) {
+ return Element.insert(element, {after:content});
+ }
+};
+
+var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+// This should be moved to script.aculo.us; notice the deprecated methods
+// further below, that map to the newer Element methods.
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = Element.cumulativeScrollOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
+ },
+
+ // Deprecation layer -- use newer Element methods now (1.5.2).
+
+ cumulativeOffset: Element.Methods.cumulativeOffset,
+
+ positionedOffset: Element.Methods.positionedOffset,
+
+ absolutize: function(element) {
+ Position.prepare();
+ return Element.absolutize(element);
+ },
+
+ relativize: function(element) {
+ Position.prepare();
+ return Element.relativize(element);
+ },
+
+ realOffset: Element.Methods.cumulativeScrollOffset,
+
+ offsetParent: Element.Methods.getOffsetParent,
+
+ page: Element.Methods.viewportOffset,
+
+ clone: function(source, target, options) {
+ options = options || { };
+ return Element.clonePosition(target, source, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
+ function iter(name) {
+ return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
+ }
+
+ instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
+ function(element, className) {
+ className = className.toString().strip();
+ var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
+ return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
+ } : function(element, className) {
+ className = className.toString().strip();
+ var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
+ if (!classNames && !className) return elements;
+
+ var nodes = $(element).getElementsByTagName('*');
+ className = ' ' + className + ' ';
+
+ for (var i = 0, child, cn; child = nodes[i]; i++) {
+ if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
+ (classNames && classNames.all(function(name) {
+ return !name.toString().blank() && cn.include(' ' + name + ' ');
+ }))))
+ elements.push(Element.extend(child));
+ }
+ return elements;
+ };
+
+ return function(className, parentElement) {
+ return $(parentElement || document.body).getElementsByClassName(className);
+ };
+}(Element.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set($A(this).concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set($A(this).without(classNameToRemove).join(' '));
+ },
+
+ toString: function() {
+ return $A(this).join(' ');
+ }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+
+/*--------------------------------------------------------------------------*/
+
+Element.addMethods();
\ No newline at end of file
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/builder.js
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/builder.js (rev 0)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/builder.js 2008-02-11 02:47:55 UTC (rev 9930)
@@ -0,0 +1,136 @@
+// script.aculo.us builder.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+var Builder = {
+ NODEMAP: {
+ AREA: 'map',
+ CAPTION: 'table',
+ COL: 'table',
+ COLGROUP: 'table',
+ LEGEND: 'fieldset',
+ OPTGROUP: 'select',
+ OPTION: 'select',
+ PARAM: 'object',
+ TBODY: 'table',
+ TD: 'table',
+ TFOOT: 'table',
+ TH: 'table',
+ THEAD: 'table',
+ TR: 'table'
+ },
+ // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
+ // due to a Firefox bug
+ node: function(elementName) {
+ elementName = elementName.toUpperCase();
+
+ // try innerHTML approach
+ var parentTag = this.NODEMAP[elementName] || 'div';
+ var parentElement = document.createElement(parentTag);
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+ parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
+ } catch(e) {}
+ var element = parentElement.firstChild || null;
+
+ // see if browser added wrapping tags
+ if(element && (element.tagName.toUpperCase() != elementName))
+ element = element.getElementsByTagName(elementName)[0];
+
+ // fallback to createElement approach
+ if(!element) element = document.createElement(elementName);
+
+ // abort if nothing could be created
+ if(!element) return;
+
+ // attributes (or text)
+ if(arguments[1])
+ if(this._isStringOrNumber(arguments[1]) ||
+ (arguments[1] instanceof Array) ||
+ arguments[1].tagName) {
+ this._children(element, arguments[1]);
+ } else {
+ var attrs = this._attributes(arguments[1]);
+ if(attrs.length) {
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+ parentElement.innerHTML = "<" +elementName + " " +
+ attrs + "></" + elementName + ">";
+ } catch(e) {}
+ element = parentElement.firstChild || null;
+ // workaround firefox 1.0.X bug
+ if(!element) {
+ element = document.createElement(elementName);
+ for(attr in arguments[1])
+ element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
+ }
+ if(element.tagName.toUpperCase() != elementName)
+ element = parentElement.getElementsByTagName(elementName)[0];
+ }
+ }
+
+ // text, or array of children
+ if(arguments[2])
+ this._children(element, arguments[2]);
+
+ return element;
+ },
+ _text: function(text) {
+ return document.createTextNode(text);
+ },
+
+ ATTR_MAP: {
+ 'className': 'class',
+ 'htmlFor': 'for'
+ },
+
+ _attributes: function(attributes) {
+ var attrs = [];
+ for(attribute in attributes)
+ attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
+ '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'"') + '"');
+ return attrs.join(" ");
+ },
+ _children: function(element, children) {
+ if(children.tagName) {
+ element.appendChild(children);
+ return;
+ }
+ if(typeof children=='object') { // array can hold nodes and text
+ children.flatten().each( function(e) {
+ if(typeof e=='object')
+ element.appendChild(e)
+ else
+ if(Builder._isStringOrNumber(e))
+ element.appendChild(Builder._text(e));
+ });
+ } else
+ if(Builder._isStringOrNumber(children))
+ element.appendChild(Builder._text(children));
+ },
+ _isStringOrNumber: function(param) {
+ return(typeof param=='string' || typeof param=='number');
+ },
+ build: function(html) {
+ var element = this.node('div');
+ $(element).update(html.strip());
+ return element.down();
+ },
+ dump: function(scope) {
+ if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
+
+ var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
+ "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
+ "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
+ "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
+ "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
+ "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
+
+ tags.each( function(tag){
+ scope[tag] = function() {
+ return Builder.node.apply(Builder, [tag].concat($A(arguments)));
+ }
+ });
+ }
+}
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/controls.js
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/controls.js (rev 0)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/controls.js 2008-02-11 02:47:55 UTC (rev 9930)
@@ -0,0 +1,965 @@
+// script.aculo.us controls.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+// Richard Livsey
+// Rahul Bhargava
+// Rob Wills
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// Autocompleter.Base handles all the autocompletion functionality
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least,
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most
+// useful when one of the tokens is \n (a newline), as it
+// allows smart autocompletion after linebreaks.
+
+if(typeof Effect == 'undefined')
+ throw("controls.js requires including script.aculo.us' effects.js library");
+
+var Autocompleter = { }
+Autocompleter.Base = Class.create({
+ baseInitialize: function(element, update, options) {
+ element = $(element)
+ this.element = element;
+ this.update = $(update);
+ this.hasFocus = false;
+ this.changed = false;
+ this.active = false;
+ this.index = 0;
+ this.entryCount = 0;
+ this.oldElementValue = this.element.value;
+
+ if(this.setOptions)
+ this.setOptions(options);
+ else
+ this.options = options || { };
+
+ this.options.paramName = this.options.paramName || this.element.name;
+ this.options.tokens = this.options.tokens || [];
+ this.options.frequency = this.options.frequency || 0.4;
+ this.options.minChars = this.options.minChars || 1;
+ this.options.onShow = this.options.onShow ||
+ function(element, update){
+ if(!update.style.position || update.style.position=='absolute') {
+ update.style.position = 'absolute';
+ Position.clone(element, update, {
+ setHeight: false,
+ offsetTop: element.offsetHeight
+ });
+ }
+ Effect.Appear(update,{duration:0.15});
+ };
+ this.options.onHide = this.options.onHide ||
+ function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+ if(typeof(this.options.tokens) == 'string')
+ this.options.tokens = new Array(this.options.tokens);
+ // Force carriage returns as token delimiters anyway
+ if (!this.options.tokens.include('\n'))
+ this.options.tokens.push('\n');
+
+ this.observer = null;
+
+ this.element.setAttribute('autocomplete','off');
+
+ Element.hide(this.update);
+
+ Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
+ Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
+ },
+
+ show: function() {
+ if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+ if(!this.iefix &&
+ (Prototype.Browser.IE) &&
+ (Element.getStyle(this.update, 'position')=='absolute')) {
+ new Insertion.After(this.update,
+ '<iframe id="' + this.update.id + '_iefix" '+
+ 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+ 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+ this.iefix = $(this.update.id+'_iefix');
+ }
+ if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+ },
+
+ fixIEOverlapping: function() {
+ Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
+ this.iefix.style.zIndex = 1;
+ this.update.style.zIndex = 2;
+ Element.show(this.iefix);
+ },
+
+ hide: function() {
+ this.stopIndicator();
+ if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+ if(this.iefix) Element.hide(this.iefix);
+ },
+
+ startIndicator: function() {
+ if(this.options.indicator) Element.show(this.options.indicator);
+ },
+
+ stopIndicator: function() {
+ if(this.options.indicator) Element.hide(this.options.indicator);
+ },
+
+ onKeyPress: function(event) {
+ if(this.active)
+ switch(event.keyCode) {
+ case Event.KEY_TAB:
+ case Event.KEY_RETURN:
+ this.selectEntry();
+ Event.stop(event);
+ case Event.KEY_ESC:
+ this.hide();
+ this.active = false;
+ Event.stop(event);
+ return;
+ case Event.KEY_LEFT:
+ case Event.KEY_RIGHT:
+ return;
+ case Event.KEY_UP:
+ this.markPrevious();
+ this.render();
+ Event.stop(event);
+ return;
+ case Event.KEY_DOWN:
+ this.markNext();
+ this.render();
+ Event.stop(event);
+ return;
+ }
+ else
+ if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
+ (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
+
+ this.changed = true;
+ this.hasFocus = true;
+
+ if(this.observer) clearTimeout(this.observer);
+ this.observer =
+ setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+ },
+
+ activate: function() {
+ this.changed = false;
+ this.hasFocus = true;
+ this.getUpdatedChoices();
+ },
+
+ onHover: function(event) {
+ var element = Event.findElement(event, 'LI');
+ if(this.index != element.autocompleteIndex)
+ {
+ this.index = element.autocompleteIndex;
+ this.render();
+ }
+ Event.stop(event);
+ },
+
+ onClick: function(event) {
+ var element = Event.findElement(event, 'LI');
+ this.index = element.autocompleteIndex;
+ this.selectEntry();
+ this.hide();
+ },
+
+ onBlur: function(event) {
+ // needed to make click events working
+ setTimeout(this.hide.bind(this), 250);
+ this.hasFocus = false;
+ this.active = false;
+ },
+
+ render: function() {
+ if(this.entryCount > 0) {
+ for (var i = 0; i < this.entryCount; i++)
+ this.index==i ?
+ Element.addClassName(this.getEntry(i),"selected") :
+ Element.removeClassName(this.getEntry(i),"selected");
+ if(this.hasFocus) {
+ this.show();
+ this.active = true;
+ }
+ } else {
+ this.active = false;
+ this.hide();
+ }
+ },
+
+ markPrevious: function() {
+ if(this.index > 0) this.index--
+ else this.index = this.entryCount-1;
+ this.getEntry(this.index).scrollIntoView(true);
+ },
+
+ markNext: function() {
+ if(this.index < this.entryCount-1) this.index++
+ else this.index = 0;
+ this.getEntry(this.index).scrollIntoView(false);
+ },
+
+ getEntry: function(index) {
+ return this.update.firstChild.childNodes[index];
+ },
+
+ getCurrentEntry: function() {
+ return this.getEntry(this.index);
+ },
+
+ selectEntry: function() {
+ this.active = false;
+ this.updateElement(this.getCurrentEntry());
+ },
+
+ updateElement: function(selectedElement) {
+ if (this.options.updateElement) {
+ this.options.updateElement(selectedElement);
+ return;
+ }
+ var value = '';
+ if (this.options.select) {
+ var nodes = $(selectedElement).select('.' + this.options.select) || [];
+ if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+ } else
+ value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+
+ var bounds = this.getTokenBounds();
+ if (bounds[0] != -1) {
+ var newValue = this.element.value.substr(0, bounds[0]);
+ var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
+ if (whitespace)
+ newValue += whitespace[0];
+ this.element.value = newValue + value + this.element.value.substr(bounds[1]);
+ } else {
+ this.element.value = value;
+ }
+ this.oldElementValue = this.element.value;
+ this.element.focus();
+
+ if (this.options.afterUpdateElement)
+ this.options.afterUpdateElement(this.element, selectedElement);
+ },
+
+ updateChoices: function(choices) {
+ if(!this.changed && this.hasFocus) {
+ this.update.innerHTML = choices;
+ Element.cleanWhitespace(this.update);
+ Element.cleanWhitespace(this.update.down());
+
+ if(this.update.firstChild && this.update.down().childNodes) {
+ this.entryCount =
+ this.update.down().childNodes.length;
+ for (var i = 0; i < this.entryCount; i++) {
+ var entry = this.getEntry(i);
+ entry.autocompleteIndex = i;
+ this.addObservers(entry);
+ }
+ } else {
+ this.entryCount = 0;
+ }
+
+ this.stopIndicator();
+ this.index = 0;
+
+ if(this.entryCount==1 && this.options.autoSelect) {
+ this.selectEntry();
+ this.hide();
+ } else {
+ this.render();
+ }
+ }
+ },
+
+ addObservers: function(element) {
+ Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+ Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+ },
+
+ onObserverEvent: function() {
+ this.changed = false;
+ this.tokenBounds = null;
+ if(this.getToken().length>=this.options.minChars) {
+ this.getUpdatedChoices();
+ } else {
+ this.active = false;
+ this.hide();
+ }
+ this.oldElementValue = this.element.value;
+ },
+
+ getToken: function() {
+ var bounds = this.getTokenBounds();
+ return this.element.value.substring(bounds[0], bounds[1]).strip();
+ },
+
+ getTokenBounds: function() {
+ if (null != this.tokenBounds) return this.tokenBounds;
+ var value = this.element.value;
+ if (value.strip().empty()) return [-1, 0];
+ var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
+ var offset = (diff == this.oldElementValue.length ? 1 : 0);
+ var prevTokenPos = -1, nextTokenPos = value.length;
+ var tp;
+ for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
+ tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
+ if (tp > prevTokenPos) prevTokenPos = tp;
+ tp = value.indexOf(this.options.tokens[index], diff + offset);
+ if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
+ }
+ return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
+ }
+});
+
+Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
+ var boundary = Math.min(newS.length, oldS.length);
+ for (var index = 0; index < boundary; ++index)
+ if (newS[index] != oldS[index])
+ return index;
+ return boundary;
+};
+
+Ajax.Autocompleter = Class.create(Autocompleter.Base, {
+ initialize: function(element, update, url, options) {
+ this.baseInitialize(element, update, options);
+ this.options.asynchronous = true;
+ this.options.onComplete = this.onComplete.bind(this);
+ this.options.defaultParams = this.options.parameters || null;
+ this.url = url;
+ },
+
+ getUpdatedChoices: function() {
+ this.startIndicator();
+
+ var entry = encodeURIComponent(this.options.paramName) + '=' +
+ encodeURIComponent(this.getToken());
+
+ this.options.parameters = this.options.callback ?
+ this.options.callback(this.element, entry) : entry;
+
+ if(this.options.defaultParams)
+ this.options.parameters += '&' + this.options.defaultParams;
+
+ new Ajax.Request(this.url, this.options);
+ },
+
+ onComplete: function(request) {
+ this.updateChoices(request.responseText);
+ }
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+// text only at the beginning of strings in the
+// autocomplete array. Defaults to true, which will
+// match text at the beginning of any *word* in the
+// strings in the autocomplete array. If you want to
+// search anywhere in the string, additionally set
+// the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+// a partial match (unlike minChars, which defines
+// how many characters are required to do any match
+// at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+// Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector'
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create(Autocompleter.Base, {
+ initialize: function(element, update, array, options) {
+ this.baseInitialize(element, update, options);
+ this.options.array = array;
+ },
+
+ getUpdatedChoices: function() {
+ this.updateChoices(this.options.selector(this));
+ },
+
+ setOptions: function(options) {
+ this.options = Object.extend({
+ choices: 10,
+ partialSearch: true,
+ partialChars: 2,
+ ignoreCase: true,
+ fullSearch: false,
+ selector: function(instance) {
+ var ret = []; // Beginning matches
+ var partial = []; // Inside matches
+ var entry = instance.getToken();
+ var count = 0;
+
+ for (var i = 0; i < instance.options.array.length &&
+ ret.length < instance.options.choices ; i++) {
+
+ var elem = instance.options.array[i];
+ var foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase()) :
+ elem.indexOf(entry);
+
+ while (foundPos != -1) {
+ if (foundPos == 0 && elem.length != entry.length) {
+ ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
+ elem.substr(entry.length) + "</li>");
+ break;
+ } else if (entry.length >= instance.options.partialChars &&
+ instance.options.partialSearch && foundPos != -1) {
+ if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+ partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+ elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+ foundPos + entry.length) + "</li>");
+ break;
+ }
+ }
+
+ foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
+ elem.indexOf(entry, foundPos + 1);
+
+ }
+ }
+ if (partial.length)
+ ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+ return "<ul>" + ret.join('') + "</ul>";
+ }
+ }, options || { });
+ }
+});
+
+// AJAX in-place editor and collection editor
+// Full rewrite by Christophe Porteneuve <tdd(a)tddsworld.com> (April 2007).
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+ setTimeout(function() {
+ Field.activate(field);
+ }, 1);
+}
+
+Ajax.InPlaceEditor = Class.create({
+ initialize: function(element, url, options) {
+ this.url = url;
+ this.element = element = $(element);
+ this.prepareOptions();
+ this._controls = { };
+ arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
+ Object.extend(this.options, options || { });
+ if (!this.options.formId && this.element.id) {
+ this.options.formId = this.element.id + '-inplaceeditor';
+ if ($(this.options.formId))
+ this.options.formId = '';
+ }
+ if (this.options.externalControl)
+ this.options.externalControl = $(this.options.externalControl);
+ if (!this.options.externalControl)
+ this.options.externalControlOnly = false;
+ this._originalBackground = this.element.getStyle('background-color') || 'transparent';
+ this.element.title = this.options.clickToEditText;
+ this._boundCancelHandler = this.handleFormCancellation.bind(this);
+ this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
+ this._boundFailureHandler = this.handleAJAXFailure.bind(this);
+ this._boundSubmitHandler = this.handleFormSubmission.bind(this);
+ this._boundWrapperHandler = this.wrapUp.bind(this);
+ this.registerListeners();
+ },
+ checkForEscapeOrReturn: function(e) {
+ if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
+ if (Event.KEY_ESC == e.keyCode)
+ this.handleFormCancellation(e);
+ else if (Event.KEY_RETURN == e.keyCode)
+ this.handleFormSubmission(e);
+ },
+ createControl: function(mode, handler, extraClasses) {
+ var control = this.options[mode + 'Control'];
+ var text = this.options[mode + 'Text'];
+ if ('button' == control) {
+ var btn = document.createElement('input');
+ btn.type = 'submit';
+ btn.value = text;
+ btn.className = 'editor_' + mode + '_button';
+ if ('cancel' == mode)
+ btn.onclick = this._boundCancelHandler;
+ this._form.appendChild(btn);
+ this._controls[mode] = btn;
+ } else if ('link' == control) {
+ var link = document.createElement('a');
+ link.href = '#';
+ link.appendChild(document.createTextNode(text));
+ link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
+ link.className = 'editor_' + mode + '_link';
+ if (extraClasses)
+ link.className += ' ' + extraClasses;
+ this._form.appendChild(link);
+ this._controls[mode] = link;
+ }
+ },
+ createEditField: function() {
+ var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
+ var fld;
+ if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
+ fld = document.createElement('input');
+ fld.type = 'text';
+ var size = this.options.size || this.options.cols || 0;
+ if (0 < size) fld.size = size;
+ } else {
+ fld = document.createElement('textarea');
+ fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
+ fld.cols = this.options.cols || 40;
+ }
+ fld.name = this.options.paramName;
+ fld.value = text; // No HTML breaks conversion anymore
+ fld.className = 'editor_field';
+ if (this.options.submitOnBlur)
+ fld.onblur = this._boundSubmitHandler;
+ this._controls.editor = fld;
+ if (this.options.loadTextURL)
+ this.loadExternalText();
+ this._form.appendChild(this._controls.editor);
+ },
+ createForm: function() {
+ var ipe = this;
+ function addText(mode, condition) {
+ var text = ipe.options['text' + mode + 'Controls'];
+ if (!text || condition === false) return;
+ ipe._form.appendChild(document.createTextNode(text));
+ };
+ this._form = $(document.createElement('form'));
+ this._form.id = this.options.formId;
+ this._form.addClassName(this.options.formClassName);
+ this._form.onsubmit = this._boundSubmitHandler;
+ this.createEditField();
+ if ('textarea' == this._controls.editor.tagName.toLowerCase())
+ this._form.appendChild(document.createElement('br'));
+ if (this.options.onFormCustomization)
+ this.options.onFormCustomization(this, this._form);
+ addText('Before', this.options.okControl || this.options.cancelControl);
+ this.createControl('ok', this._boundSubmitHandler);
+ addText('Between', this.options.okControl && this.options.cancelControl);
+ this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
+ addText('After', this.options.okControl || this.options.cancelControl);
+ },
+ destroy: function() {
+ if (this._oldInnerHTML)
+ this.element.innerHTML = this._oldInnerHTML;
+ this.leaveEditMode();
+ this.unregisterListeners();
+ },
+ enterEditMode: function(e) {
+ if (this._saving || this._editing) return;
+ this._editing = true;
+ this.triggerCallback('onEnterEditMode');
+ if (this.options.externalControl)
+ this.options.externalControl.hide();
+ this.element.hide();
+ this.createForm();
+ this.element.parentNode.insertBefore(this._form, this.element);
+ if (!this.options.loadTextURL)
+ this.postProcessEditField();
+ if (e) Event.stop(e);
+ },
+ enterHover: function(e) {
+ if (this.options.hoverClassName)
+ this.element.addClassName(this.options.hoverClassName);
+ if (this._saving) return;
+ this.triggerCallback('onEnterHover');
+ },
+ getText: function() {
+ return this.element.innerHTML;
+ },
+ handleAJAXFailure: function(transport) {
+ this.triggerCallback('onFailure', transport);
+ if (this._oldInnerHTML) {
+ this.element.innerHTML = this._oldInnerHTML;
+ this._oldInnerHTML = null;
+ }
+ },
+ handleFormCancellation: function(e) {
+ this.wrapUp();
+ if (e) Event.stop(e);
+ },
+ handleFormSubmission: function(e) {
+ var form = this._form;
+ var value = $F(this._controls.editor);
+ this.prepareSubmission();
+ var params = this.options.callback(form, value) || '';
+ if (Object.isString(params))
+ params = params.toQueryParams();
+ params.editorId = this.element.id;
+ if (this.options.htmlResponse) {
+ var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: params,
+ onComplete: this._boundWrapperHandler,
+ onFailure: this._boundFailureHandler
+ });
+ new Ajax.Updater({ success: this.element }, this.url, options);
+ } else {
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: params,
+ onComplete: this._boundWrapperHandler,
+ onFailure: this._boundFailureHandler
+ });
+ new Ajax.Request(this.url, options);
+ }
+ if (e) Event.stop(e);
+ },
+ leaveEditMode: function() {
+ this.element.removeClassName(this.options.savingClassName);
+ this.removeForm();
+ this.leaveHover();
+ this.element.style.backgroundColor = this._originalBackground;
+ this.element.show();
+ if (this.options.externalControl)
+ this.options.externalControl.show();
+ this._saving = false;
+ this._editing = false;
+ this._oldInnerHTML = null;
+ this.triggerCallback('onLeaveEditMode');
+ },
+ leaveHover: function(e) {
+ if (this.options.hoverClassName)
+ this.element.removeClassName(this.options.hoverClassName);
+ if (this._saving) return;
+ this.triggerCallback('onLeaveHover');
+ },
+ loadExternalText: function() {
+ this._form.addClassName(this.options.loadingClassName);
+ this._controls.editor.disabled = true;
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
+ onComplete: Prototype.emptyFunction,
+ onSuccess: function(transport) {
+ this._form.removeClassName(this.options.loadingClassName);
+ var text = transport.responseText;
+ if (this.options.stripLoadedTextTags)
+ text = text.stripTags();
+ this._controls.editor.value = text;
+ this._controls.editor.disabled = false;
+ this.postProcessEditField();
+ }.bind(this),
+ onFailure: this._boundFailureHandler
+ });
+ new Ajax.Request(this.options.loadTextURL, options);
+ },
+ postProcessEditField: function() {
+ var fpc = this.options.fieldPostCreation;
+ if (fpc)
+ $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
+ },
+ prepareOptions: function() {
+ this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
+ Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
+ [this._extraDefaultOptions].flatten().compact().each(function(defs) {
+ Object.extend(this.options, defs);
+ }.bind(this));
+ },
+ prepareSubmission: function() {
+ this._saving = true;
+ this.removeForm();
+ this.leaveHover();
+ this.showSaving();
+ },
+ registerListeners: function() {
+ this._listeners = { };
+ var listener;
+ $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
+ listener = this[pair.value].bind(this);
+ this._listeners[pair.key] = listener;
+ if (!this.options.externalControlOnly)
+ this.element.observe(pair.key, listener);
+ if (this.options.externalControl)
+ this.options.externalControl.observe(pair.key, listener);
+ }.bind(this));
+ },
+ removeForm: function() {
+ if (!this._form) return;
+ this._form.remove();
+ this._form = null;
+ this._controls = { };
+ },
+ showSaving: function() {
+ this._oldInnerHTML = this.element.innerHTML;
+ this.element.innerHTML = this.options.savingText;
+ this.element.addClassName(this.options.savingClassName);
+ this.element.style.backgroundColor = this._originalBackground;
+ this.element.show();
+ },
+ triggerCallback: function(cbName, arg) {
+ if ('function' == typeof this.options[cbName]) {
+ this.options[cbName](this, arg);
+ }
+ },
+ unregisterListeners: function() {
+ $H(this._listeners).each(function(pair) {
+ if (!this.options.externalControlOnly)
+ this.element.stopObserving(pair.key, pair.value);
+ if (this.options.externalControl)
+ this.options.externalControl.stopObserving(pair.key, pair.value);
+ }.bind(this));
+ },
+ wrapUp: function(transport) {
+ this.leaveEditMode();
+ // Can't use triggerCallback due to backward compatibility: requires
+ // binding + direct element
+ this._boundComplete(transport, this.element);
+ }
+});
+
+Object.extend(Ajax.InPlaceEditor.prototype, {
+ dispose: Ajax.InPlaceEditor.prototype.destroy
+});
+
+Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
+ initialize: function($super, element, url, options) {
+ this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
+ $super(element, url, options);
+ },
+
+ createEditField: function() {
+ var list = document.createElement('select');
+ list.name = this.options.paramName;
+ list.size = 1;
+ this._controls.editor = list;
+ this._collection = this.options.collection || [];
+ if (this.options.loadCollectionURL)
+ this.loadCollection();
+ else
+ this.checkForExternalText();
+ this._form.appendChild(this._controls.editor);
+ },
+
+ loadCollection: function() {
+ this._form.addClassName(this.options.loadingClassName);
+ this.showLoadingText(this.options.loadingCollectionText);
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
+ onComplete: Prototype.emptyFunction,
+ onSuccess: function(transport) {
+ var js = transport.responseText.strip();
+ if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
+ throw 'Server returned an invalid collection representation.';
+ this._collection = eval(js);
+ this.checkForExternalText();
+ }.bind(this),
+ onFailure: this.onFailure
+ });
+ new Ajax.Request(this.options.loadCollectionURL, options);
+ },
+
+ showLoadingText: function(text) {
+ this._controls.editor.disabled = true;
+ var tempOption = this._controls.editor.firstChild;
+ if (!tempOption) {
+ tempOption = document.createElement('option');
+ tempOption.value = '';
+ this._controls.editor.appendChild(tempOption);
+ tempOption.selected = true;
+ }
+ tempOption.update((text || '').stripScripts().stripTags());
+ },
+
+ checkForExternalText: function() {
+ this._text = this.getText();
+ if (this.options.loadTextURL)
+ this.loadExternalText();
+ else
+ this.buildOptionList();
+ },
+
+ loadExternalText: function() {
+ this.showLoadingText(this.options.loadingText);
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
+ onComplete: Prototype.emptyFunction,
+ onSuccess: function(transport) {
+ this._text = transport.responseText.strip();
+ this.buildOptionList();
+ }.bind(this),
+ onFailure: this.onFailure
+ });
+ new Ajax.Request(this.options.loadTextURL, options);
+ },
+
+ buildOptionList: function() {
+ this._form.removeClassName(this.options.loadingClassName);
+ this._collection = this._collection.map(function(entry) {
+ return 2 === entry.length ? entry : [entry, entry].flatten();
+ });
+ var marker = ('value' in this.options) ? this.options.value : this._text;
+ var textFound = this._collection.any(function(entry) {
+ return entry[0] == marker;
+ }.bind(this));
+ this._controls.editor.update('');
+ var option;
+ this._collection.each(function(entry, index) {
+ option = document.createElement('option');
+ option.value = entry[0];
+ option.selected = textFound ? entry[0] == marker : 0 == index;
+ option.appendChild(document.createTextNode(entry[1]));
+ this._controls.editor.appendChild(option);
+ }.bind(this));
+ this._controls.editor.disabled = false;
+ Field.scrollFreeActivate(this._controls.editor);
+ }
+});
+
+//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
+//**** This only exists for a while, in order to let ****
+//**** users adapt to the new API. Read up on the new ****
+//**** API and convert your code to it ASAP! ****
+
+Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
+ if (!options) return;
+ function fallback(name, expr) {
+ if (name in options || expr === undefined) return;
+ options[name] = expr;
+ };
+ fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
+ options.cancelLink == options.cancelButton == false ? false : undefined)));
+ fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
+ options.okLink == options.okButton == false ? false : undefined)));
+ fallback('highlightColor', options.highlightcolor);
+ fallback('highlightEndColor', options.highlightendcolor);
+};
+
+Object.extend(Ajax.InPlaceEditor, {
+ DefaultOptions: {
+ ajaxOptions: { },
+ autoRows: 3, // Use when multi-line w/ rows == 1
+ cancelControl: 'link', // 'link'|'button'|false
+ cancelText: 'cancel',
+ clickToEditText: 'Click to edit',
+ externalControl: null, // id|elt
+ externalControlOnly: false,
+ fieldPostCreation: 'activate', // 'activate'|'focus'|false
+ formClassName: 'inplaceeditor-form',
+ formId: null, // id|elt
+ highlightColor: '#ffff99',
+ highlightEndColor: '#ffffff',
+ hoverClassName: '',
+ htmlResponse: true,
+ loadingClassName: 'inplaceeditor-loading',
+ loadingText: 'Loading...',
+ okControl: 'button', // 'link'|'button'|false
+ okText: 'ok',
+ paramName: 'value',
+ rows: 1, // If 1 and multi-line, uses autoRows
+ savingClassName: 'inplaceeditor-saving',
+ savingText: 'Saving...',
+ size: 0,
+ stripLoadedTextTags: false,
+ submitOnBlur: false,
+ textAfterControls: '',
+ textBeforeControls: '',
+ textBetweenControls: ''
+ },
+ DefaultCallbacks: {
+ callback: function(form) {
+ return Form.serialize(form);
+ },
+ onComplete: function(transport, element) {
+ // For backward compatibility, this one is bound to the IPE, and passes
+ // the element directly. It was too often customized, so we don't break it.
+ new Effect.Highlight(element, {
+ startcolor: this.options.highlightColor, keepBackgroundImage: true });
+ },
+ onEnterEditMode: null,
+ onEnterHover: function(ipe) {
+ ipe.element.style.backgroundColor = ipe.options.highlightColor;
+ if (ipe._effect)
+ ipe._effect.cancel();
+ },
+ onFailure: function(transport, ipe) {
+ alert('Error communication with the server: ' + transport.responseText.stripTags());
+ },
+ onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
+ onLeaveEditMode: null,
+ onLeaveHover: function(ipe) {
+ ipe._effect = new Effect.Highlight(ipe.element, {
+ startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
+ restorecolor: ipe._originalBackground, keepBackgroundImage: true
+ });
+ }
+ },
+ Listeners: {
+ click: 'enterEditMode',
+ keydown: 'checkForEscapeOrReturn',
+ mouseover: 'enterHover',
+ mouseout: 'leaveHover'
+ }
+});
+
+Ajax.InPlaceCollectionEditor.DefaultOptions = {
+ loadingCollectionText: 'Loading options...'
+};
+
+// Delayed observer, like Form.Element.Observer,
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create({
+ initialize: function(element, delay, callback) {
+ this.delay = delay || 0.5;
+ this.element = $(element);
+ this.callback = callback;
+ this.timer = null;
+ this.lastValue = $F(this.element);
+ Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+ },
+ delayedListener: function(event) {
+ if(this.lastValue == $F(this.element)) return;
+ if(this.timer) clearTimeout(this.timer);
+ this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+ this.lastValue = $F(this.element);
+ },
+ onTimerEvent: function() {
+ this.timer = null;
+ this.callback(this.element, $F(this.element));
+ }
+});
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/dragdrop.js
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/dragdrop.js (rev 0)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/dragdrop.js 2008-02-11 02:47:55 UTC (rev 9930)
@@ -0,0 +1,974 @@
+// script.aculo.us dragdrop.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi(a)oriontransfer.co.nz)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+if(Object.isUndefined(Effect))
+ throw("dragdrop.js requires including script.aculo.us' effects.js library");
+
+var Droppables = {
+ drops: [],
+
+ remove: function(element) {
+ this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+ },
+
+ add: function(element) {
+ element = $(element);
+ var options = Object.extend({
+ greedy: true,
+ hoverclass: null,
+ tree: false
+ }, arguments[1] || { });
+
+ // cache containers
+ if(options.containment) {
+ options._containers = [];
+ var containment = options.containment;
+ if(Object.isArray(containment)) {
+ containment.each( function(c) { options._containers.push($(c)) });
+ } else {
+ options._containers.push($(containment));
+ }
+ }
+
+ if(options.accept) options.accept = [options.accept].flatten();
+
+ Element.makePositioned(element); // fix IE
+ options.element = element;
+
+ this.drops.push(options);
+ },
+
+ findDeepestChild: function(drops) {
+ deepest = drops[0];
+
+ for (i = 1; i < drops.length; ++i)
+ if (Element.isParent(drops[i].element, deepest.element))
+ deepest = drops[i];
+
+ return deepest;
+ },
+
+ isContained: function(element, drop) {
+ var containmentNode;
+ if(drop.tree) {
+ containmentNode = element.treeNode;
+ } else {
+ containmentNode = element.parentNode;
+ }
+ return drop._containers.detect(function(c) { return containmentNode == c });
+ },
+
+ isAffected: function(point, element, drop) {
+ return (
+ (drop.element!=element) &&
+ ((!drop._containers) ||
+ this.isContained(element, drop)) &&
+ ((!drop.accept) ||
+ (Element.classNames(element).detect(
+ function(v) { return drop.accept.include(v) } ) )) &&
+ Position.within(drop.element, point[0], point[1]) );
+ },
+
+ deactivate: function(drop) {
+ if(drop.hoverclass)
+ Element.removeClassName(drop.element, drop.hoverclass);
+ this.last_active = null;
+ },
+
+ activate: function(drop) {
+ if(drop.hoverclass)
+ Element.addClassName(drop.element, drop.hoverclass);
+ this.last_active = drop;
+ },
+
+ show: function(point, element) {
+ if(!this.drops.length) return;
+ var drop, affected = [];
+
+ this.drops.each( function(drop) {
+ if(Droppables.isAffected(point, element, drop))
+ affected.push(drop);
+ });
+
+ if(affected.length>0)
+ drop = Droppables.findDeepestChild(affected);
+
+ if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
+ if (drop) {
+ Position.within(drop.element, point[0], point[1]);
+ if(drop.onHover)
+ drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+
+ if (drop != this.last_active) Droppables.activate(drop);
+ }
+ },
+
+ fire: function(event, element) {
+ if(!this.last_active) return;
+ Position.prepare();
+
+ if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+ if (this.last_active.onDrop) {
+ this.last_active.onDrop(element, this.last_active.element, event);
+ return true;
+ }
+ },
+
+ reset: function() {
+ if(this.last_active)
+ this.deactivate(this.last_active);
+ }
+}
+
+var Draggables = {
+ drags: [],
+ observers: [],
+
+ register: function(draggable) {
+ if(this.drags.length == 0) {
+ this.eventMouseUp = this.endDrag.bindAsEventListener(this);
+ this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+ this.eventKeypress = this.keyPress.bindAsEventListener(this);
+
+ Event.observe(document, "mouseup", this.eventMouseUp);
+ Event.observe(document, "mousemove", this.eventMouseMove);
+ Event.observe(document, "keypress", this.eventKeypress);
+ }
+ this.drags.push(draggable);
+ },
+
+ unregister: function(draggable) {
+ this.drags = this.drags.reject(function(d) { return d==draggable });
+ if(this.drags.length == 0) {
+ Event.stopObserving(document, "mouseup", this.eventMouseUp);
+ Event.stopObserving(document, "mousemove", this.eventMouseMove);
+ Event.stopObserving(document, "keypress", this.eventKeypress);
+ }
+ },
+
+ activate: function(draggable) {
+ if(draggable.options.delay) {
+ this._timeout = setTimeout(function() {
+ Draggables._timeout = null;
+ window.focus();
+ Draggables.activeDraggable = draggable;
+ }.bind(this), draggable.options.delay);
+ } else {
+ window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+ this.activeDraggable = draggable;
+ }
+ },
+
+ deactivate: function() {
+ this.activeDraggable = null;
+ },
+
+ updateDrag: function(event) {
+ if(!this.activeDraggable) return;
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ // Mozilla-based browsers fire successive mousemove events with
+ // the same coordinates, prevent needless redrawing (moz bug?)
+ if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+ this._lastPointer = pointer;
+
+ this.activeDraggable.updateDrag(event, pointer);
+ },
+
+ endDrag: function(event) {
+ if(this._timeout) {
+ clearTimeout(this._timeout);
+ this._timeout = null;
+ }
+ if(!this.activeDraggable) return;
+ this._lastPointer = null;
+ this.activeDraggable.endDrag(event);
+ this.activeDraggable = null;
+ },
+
+ keyPress: function(event) {
+ if(this.activeDraggable)
+ this.activeDraggable.keyPress(event);
+ },
+
+ addObserver: function(observer) {
+ this.observers.push(observer);
+ this._cacheObserverCallbacks();
+ },
+
+ removeObserver: function(element) { // element instead of observer fixes mem leaks
+ this.observers = this.observers.reject( function(o) { return o.element==element });
+ this._cacheObserverCallbacks();
+ },
+
+ notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
+ if(this[eventName+'Count'] > 0)
+ this.observers.each( function(o) {
+ if(o[eventName]) o[eventName](eventName, draggable, event);
+ });
+ if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
+ },
+
+ _cacheObserverCallbacks: function() {
+ ['onStart','onEnd','onDrag'].each( function(eventName) {
+ Draggables[eventName+'Count'] = Draggables.observers.select(
+ function(o) { return o[eventName]; }
+ ).length;
+ });
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create({
+ initialize: function(element) {
+ var defaults = {
+ handle: false,
+ reverteffect: function(element, top_offset, left_offset) {
+ var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+ new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
+ queue: {scope:'_draggable', position:'end'}
+ });
+ },
+ endeffect: function(element) {
+ var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
+ new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
+ queue: {scope:'_draggable', position:'end'},
+ afterFinish: function(){
+ Draggable._dragging[element] = false
+ }
+ });
+ },
+ zindex: 1000,
+ revert: false,
+ quiet: false,
+ scroll: false,
+ scrollSensitivity: 20,
+ scrollSpeed: 15,
+ snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
+ delay: 0
+ };
+
+ if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
+ Object.extend(defaults, {
+ starteffect: function(element) {
+ element._opacity = Element.getOpacity(element);
+ Draggable._dragging[element] = true;
+ new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
+ }
+ });
+
+ var options = Object.extend(defaults, arguments[1] || { });
+
+ this.element = $(element);
+
+ if(options.handle && Object.isString(options.handle))
+ this.handle = this.element.down('.'+options.handle, 0);
+
+ if(!this.handle) this.handle = $(options.handle);
+ if(!this.handle) this.handle = this.element;
+
+ if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
+ options.scroll = $(options.scroll);
+ this._isScrollChild = Element.childOf(this.element, options.scroll);
+ }
+
+ Element.makePositioned(this.element); // fix IE
+
+ this.options = options;
+ this.dragging = false;
+
+ this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+ Event.observe(this.handle, "mousedown", this.eventMouseDown);
+
+ Draggables.register(this);
+ },
+
+ destroy: function() {
+ Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+ Draggables.unregister(this);
+ },
+
+ currentDelta: function() {
+ return([
+ parseInt(Element.getStyle(this.element,'left') || '0'),
+ parseInt(Element.getStyle(this.element,'top') || '0')]);
+ },
+
+ initDrag: function(event) {
+ if(!Object.isUndefined(Draggable._dragging[this.element]) &&
+ Draggable._dragging[this.element]) return;
+ if(Event.isLeftClick(event)) {
+ // abort on form elements, fixes a Firefox issue
+ var src = Event.element(event);
+ if((tag_name = src.tagName.toUpperCase()) && (
+ tag_name=='INPUT' ||
+ tag_name=='SELECT' ||
+ tag_name=='OPTION' ||
+ tag_name=='BUTTON' ||
+ tag_name=='TEXTAREA')) return;
+
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ var pos = Position.cumulativeOffset(this.element);
+ this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+
+ Draggables.activate(this);
+ Event.stop(event);
+ }
+ },
+
+ startDrag: function(event) {
+ this.dragging = true;
+ if(!this.delta)
+ this.delta = this.currentDelta();
+
+ if(this.options.zindex) {
+ this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+ this.element.style.zIndex = this.options.zindex;
+ }
+
+ if(this.options.ghosting) {
+ this._clone = this.element.cloneNode(true);
+ this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
+ if (!this.element._originallyAbsolute)
+ Position.absolutize(this.element);
+ this.element.parentNode.insertBefore(this._clone, this.element);
+ }
+
+ if(this.options.scroll) {
+ if (this.options.scroll == window) {
+ var where = this._getWindowScroll(this.options.scroll);
+ this.originalScrollLeft = where.left;
+ this.originalScrollTop = where.top;
+ } else {
+ this.originalScrollLeft = this.options.scroll.scrollLeft;
+ this.originalScrollTop = this.options.scroll.scrollTop;
+ }
+ }
+
+ Draggables.notify('onStart', this, event);
+
+ if(this.options.starteffect) this.options.starteffect(this.element);
+ },
+
+ updateDrag: function(event, pointer) {
+ if(!this.dragging) this.startDrag(event);
+
+ if(!this.options.quiet){
+ Position.prepare();
+ Droppables.show(pointer, this.element);
+ }
+
+ Draggables.notify('onDrag', this, event);
+
+ this.draw(pointer);
+ if(this.options.change) this.options.change(this);
+
+ if(this.options.scroll) {
+ this.stopScrolling();
+
+ var p;
+ if (this.options.scroll == window) {
+ with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
+ } else {
+ p = Position.page(this.options.scroll);
+ p[0] += this.options.scroll.scrollLeft + Position.deltaX;
+ p[1] += this.options.scroll.scrollTop + Position.deltaY;
+ p.push(p[0]+this.options.scroll.offsetWidth);
+ p.push(p[1]+this.options.scroll.offsetHeight);
+ }
+ var speed = [0,0];
+ if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
+ if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
+ if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
+ if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
+ this.startScrolling(speed);
+ }
+
+ // fix AppleWebKit rendering
+ if(Prototype.Browser.WebKit) window.scrollBy(0,0);
+
+ Event.stop(event);
+ },
+
+ finishDrag: function(event, success) {
+ this.dragging = false;
+
+ if(this.options.quiet){
+ Position.prepare();
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ Droppables.show(pointer, this.element);
+ }
+
+ if(this.options.ghosting) {
+ if (!this.element._originallyAbsolute)
+ Position.relativize(this.element);
+ delete this.element._originallyAbsolute;
+ Element.remove(this._clone);
+ this._clone = null;
+ }
+
+ var dropped = false;
+ if(success) {
+ dropped = Droppables.fire(event, this.element);
+ if (!dropped) dropped = false;
+ }
+ if(dropped && this.options.onDropped) this.options.onDropped(this.element);
+ Draggables.notify('onEnd', this, event);
+
+ var revert = this.options.revert;
+ if(revert && Object.isFunction(revert)) revert = revert(this.element);
+
+ var d = this.currentDelta();
+ if(revert && this.options.reverteffect) {
+ if (dropped == 0 || revert != 'failure')
+ this.options.reverteffect(this.element,
+ d[1]-this.delta[1], d[0]-this.delta[0]);
+ } else {
+ this.delta = d;
+ }
+
+ if(this.options.zindex)
+ this.element.style.zIndex = this.originalZ;
+
+ if(this.options.endeffect)
+ this.options.endeffect(this.element);
+
+ Draggables.deactivate(this);
+ Droppables.reset();
+ },
+
+ keyPress: function(event) {
+ if(event.keyCode!=Event.KEY_ESC) return;
+ this.finishDrag(event, false);
+ Event.stop(event);
+ },
+
+ endDrag: function(event) {
+ if(!this.dragging) return;
+ this.stopScrolling();
+ this.finishDrag(event, true);
+ Event.stop(event);
+ },
+
+ draw: function(point) {
+ var pos = Position.cumulativeOffset(this.element);
+ if(this.options.ghosting) {
+ var r = Position.realOffset(this.element);
+ pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
+ }
+
+ var d = this.currentDelta();
+ pos[0] -= d[0]; pos[1] -= d[1];
+
+ if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
+ pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
+ pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
+ }
+
+ var p = [0,1].map(function(i){
+ return (point[i]-pos[i]-this.offset[i])
+ }.bind(this));
+
+ if(this.options.snap) {
+ if(Object.isFunction(this.options.snap)) {
+ p = this.options.snap(p[0],p[1],this);
+ } else {
+ if(Object.isArray(this.options.snap)) {
+ p = p.map( function(v, i) {
+ return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
+ } else {
+ p = p.map( function(v) {
+ return (v/this.options.snap).round()*this.options.snap }.bind(this))
+ }
+ }}
+
+ var style = this.element.style;
+ if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+ style.left = p[0] + "px";
+ if((!this.options.constraint) || (this.options.constraint=='vertical'))
+ style.top = p[1] + "px";
+
+ if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+ },
+
+ stopScrolling: function() {
+ if(this.scrollInterval) {
+ clearInterval(this.scrollInterval);
+ this.scrollInterval = null;
+ Draggables._lastScrollPointer = null;
+ }
+ },
+
+ startScrolling: function(speed) {
+ if(!(speed[0] || speed[1])) return;
+ this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
+ this.lastScrolled = new Date();
+ this.scrollInterval = setInterval(this.scroll.bind(this), 10);
+ },
+
+ scroll: function() {
+ var current = new Date();
+ var delta = current - this.lastScrolled;
+ this.lastScrolled = current;
+ if(this.options.scroll == window) {
+ with (this._getWindowScroll(this.options.scroll)) {
+ if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
+ var d = delta / 1000;
+ this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
+ }
+ }
+ } else {
+ this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
+ this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
+ }
+
+ Position.prepare();
+ Droppables.show(Draggables._lastPointer, this.element);
+ Draggables.notify('onDrag', this);
+ if (this._isScrollChild) {
+ Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+ Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+ Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+ if (Draggables._lastScrollPointer[0] < 0)
+ Draggables._lastScrollPointer[0] = 0;
+ if (Draggables._lastScrollPointer[1] < 0)
+ Draggables._lastScrollPointer[1] = 0;
+ this.draw(Draggables._lastScrollPointer);
+ }
+
+ if(this.options.change) this.options.change(this);
+ },
+
+ _getWindowScroll: function(w) {
+ var T, L, W, H;
+ with (w.document) {
+ if (w.document.documentElement && documentElement.scrollTop) {
+ T = documentElement.scrollTop;
+ L = documentElement.scrollLeft;
+ } else if (w.document.body) {
+ T = body.scrollTop;
+ L = body.scrollLeft;
+ }
+ if (w.innerWidth) {
+ W = w.innerWidth;
+ H = w.innerHeight;
+ } else if (w.document.documentElement && documentElement.clientWidth) {
+ W = documentElement.clientWidth;
+ H = documentElement.clientHeight;
+ } else {
+ W = body.offsetWidth;
+ H = body.offsetHeight
+ }
+ }
+ return { top: T, left: L, width: W, height: H };
+ }
+});
+
+Draggable._dragging = { };
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create({
+ initialize: function(element, observer) {
+ this.element = $(element);
+ this.observer = observer;
+ this.lastValue = Sortable.serialize(this.element);
+ },
+
+ onStart: function() {
+ this.lastValue = Sortable.serialize(this.element);
+ },
+
+ onEnd: function() {
+ Sortable.unmark();
+ if(this.lastValue != Sortable.serialize(this.element))
+ this.observer(this.element)
+ }
+});
+
+var Sortable = {
+ SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
+
+ sortables: { },
+
+ _findRootElement: function(element) {
+ while (element.tagName.toUpperCase() != "BODY") {
+ if(element.id && Sortable.sortables[element.id]) return element;
+ element = element.parentNode;
+ }
+ },
+
+ options: function(element) {
+ element = Sortable._findRootElement($(element));
+ if(!element) return;
+ return Sortable.sortables[element.id];
+ },
+
+ destroy: function(element){
+ var s = Sortable.options(element);
+
+ if(s) {
+ Draggables.removeObserver(s.element);
+ s.droppables.each(function(d){ Droppables.remove(d) });
+ s.draggables.invoke('destroy');
+
+ delete Sortable.sortables[s.element.id];
+ }
+ },
+
+ create: function(element) {
+ element = $(element);
+ var options = Object.extend({
+ element: element,
+ tag: 'li', // assumes li children, override with tag: 'tagname'
+ dropOnEmpty: false,
+ tree: false,
+ treeTag: 'ul',
+ overlap: 'vertical', // one of 'vertical', 'horizontal'
+ constraint: 'vertical', // one of 'vertical', 'horizontal', false
+ containment: element, // also takes array of elements (or id's); or false
+ handle: false, // or a CSS class
+ only: false,
+ delay: 0,
+ hoverclass: null,
+ ghosting: false,
+ quiet: false,
+ scroll: false,
+ scrollSensitivity: 20,
+ scrollSpeed: 15,
+ format: this.SERIALIZE_RULE,
+
+ // these take arrays of elements or ids and can be
+ // used for better initialization performance
+ elements: false,
+ handles: false,
+
+ onChange: Prototype.emptyFunction,
+ onUpdate: Prototype.emptyFunction
+ }, arguments[1] || { });
+
+ // clear any old sortable with same element
+ this.destroy(element);
+
+ // build options for the draggables
+ var options_for_draggable = {
+ revert: true,
+ quiet: options.quiet,
+ scroll: options.scroll,
+ scrollSpeed: options.scrollSpeed,
+ scrollSensitivity: options.scrollSensitivity,
+ delay: options.delay,
+ ghosting: options.ghosting,
+ constraint: options.constraint,
+ handle: options.handle };
+
+ if(options.starteffect)
+ options_for_draggable.starteffect = options.starteffect;
+
+ if(options.reverteffect)
+ options_for_draggable.reverteffect = options.reverteffect;
+ else
+ if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+ element.style.top = 0;
+ element.style.left = 0;
+ };
+
+ if(options.endeffect)
+ options_for_draggable.endeffect = options.endeffect;
+
+ if(options.zindex)
+ options_for_draggable.zindex = options.zindex;
+
+ // build options for the droppables
+ var options_for_droppable = {
+ overlap: options.overlap,
+ containment: options.containment,
+ tree: options.tree,
+ hoverclass: options.hoverclass,
+ onHover: Sortable.onHover
+ }
+
+ var options_for_tree = {
+ onHover: Sortable.onEmptyHover,
+ overlap: options.overlap,
+ containment: options.containment,
+ hoverclass: options.hoverclass
+ }
+
+ // fix for gecko engine
+ Element.cleanWhitespace(element);
+
+ options.draggables = [];
+ options.droppables = [];
+
+ // drop on empty handling
+ if(options.dropOnEmpty || options.tree) {
+ Droppables.add(element, options_for_tree);
+ options.droppables.push(element);
+ }
+
+ (options.elements || this.findElements(element, options) || []).each( function(e,i) {
+ var handle = options.handles ? $(options.handles[i]) :
+ (options.handle ? $(e).select('.' + options.handle)[0] : e);
+ options.draggables.push(
+ new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+ Droppables.add(e, options_for_droppable);
+ if(options.tree) e.treeNode = element;
+ options.droppables.push(e);
+ });
+
+ if(options.tree) {
+ (Sortable.findTreeElements(element, options) || []).each( function(e) {
+ Droppables.add(e, options_for_tree);
+ e.treeNode = element;
+ options.droppables.push(e);
+ });
+ }
+
+ // keep reference
+ this.sortables[element.id] = options;
+
+ // for onupdate
+ Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+ },
+
+ // return all suitable-for-sortable elements in a guaranteed order
+ findElements: function(element, options) {
+ return Element.findChildren(
+ element, options.only, options.tree ? true : false, options.tag);
+ },
+
+ findTreeElements: function(element, options) {
+ return Element.findChildren(
+ element, options.only, options.tree ? true : false, options.treeTag);
+ },
+
+ onHover: function(element, dropon, overlap) {
+ if(Element.isParent(dropon, element)) return;
+
+ if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
+ return;
+ } else if(overlap>0.5) {
+ Sortable.mark(dropon, 'before');
+ if(dropon.previousSibling != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, dropon);
+ if(dropon.parentNode!=oldParentNode)
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon.parentNode).onChange(element);
+ }
+ } else {
+ Sortable.mark(dropon, 'after');
+ var nextElement = dropon.nextSibling || null;
+ if(nextElement != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, nextElement);
+ if(dropon.parentNode!=oldParentNode)
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon.parentNode).onChange(element);
+ }
+ }
+ },
+
+ onEmptyHover: function(element, dropon, overlap) {
+ var oldParentNode = element.parentNode;
+ var droponOptions = Sortable.options(dropon);
+
+ if(!Element.isParent(dropon, element)) {
+ var index;
+
+ var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
+ var child = null;
+
+ if(children) {
+ var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
+
+ for (index = 0; index < children.length; index += 1) {
+ if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
+ offset -= Element.offsetSize (children[index], droponOptions.overlap);
+ } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
+ child = index + 1 < children.length ? children[index + 1] : null;
+ break;
+ } else {
+ child = children[index];
+ break;
+ }
+ }
+ }
+
+ dropon.insertBefore(element, child);
+
+ Sortable.options(oldParentNode).onChange(element);
+ droponOptions.onChange(element);
+ }
+ },
+
+ unmark: function() {
+ if(Sortable._marker) Sortable._marker.hide();
+ },
+
+ mark: function(dropon, position) {
+ // mark on ghosting only
+ var sortable = Sortable.options(dropon.parentNode);
+ if(sortable && !sortable.ghosting) return;
+
+ if(!Sortable._marker) {
+ Sortable._marker =
+ ($('dropmarker') || Element.extend(document.createElement('DIV'))).
+ hide().addClassName('dropmarker').setStyle({position:'absolute'});
+ document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+ }
+ var offsets = Position.cumulativeOffset(dropon);
+ Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
+
+ if(position=='after')
+ if(sortable.overlap == 'horizontal')
+ Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
+ else
+ Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
+
+ Sortable._marker.show();
+ },
+
+ _tree: function(element, options, parent) {
+ var children = Sortable.findElements(element, options) || [];
+
+ for (var i = 0; i < children.length; ++i) {
+ var match = children[i].id.match(options.format);
+
+ if (!match) continue;
+
+ var child = {
+ id: encodeURIComponent(match ? match[1] : null),
+ element: element,
+ parent: parent,
+ children: [],
+ position: parent.children.length,
+ container: $(children[i]).down(options.treeTag)
+ }
+
+ /* Get the element containing the children and recurse over it */
+ if (child.container)
+ this._tree(child.container, options, child)
+
+ parent.children.push (child);
+ }
+
+ return parent;
+ },
+
+ tree: function(element) {
+ element = $(element);
+ var sortableOptions = this.options(element);
+ var options = Object.extend({
+ tag: sortableOptions.tag,
+ treeTag: sortableOptions.treeTag,
+ only: sortableOptions.only,
+ name: element.id,
+ format: sortableOptions.format
+ }, arguments[1] || { });
+
+ var root = {
+ id: null,
+ parent: null,
+ children: [],
+ container: element,
+ position: 0
+ }
+
+ return Sortable._tree(element, options, root);
+ },
+
+ /* Construct a [i] index for a particular node */
+ _constructIndex: function(node) {
+ var index = '';
+ do {
+ if (node.id) index = '[' + node.position + ']' + index;
+ } while ((node = node.parent) != null);
+ return index;
+ },
+
+ sequence: function(element) {
+ element = $(element);
+ var options = Object.extend(this.options(element), arguments[1] || { });
+
+ return $(this.findElements(element, options) || []).map( function(item) {
+ return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
+ });
+ },
+
+ setSequence: function(element, new_sequence) {
+ element = $(element);
+ var options = Object.extend(this.options(element), arguments[2] || { });
+
+ var nodeMap = { };
+ this.findElements(element, options).each( function(n) {
+ if (n.id.match(options.format))
+ nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
+ n.parentNode.removeChild(n);
+ });
+
+ new_sequence.each(function(ident) {
+ var n = nodeMap[ident];
+ if (n) {
+ n[1].appendChild(n[0]);
+ delete nodeMap[ident];
+ }
+ });
+ },
+
+ serialize: function(element) {
+ element = $(element);
+ var options = Object.extend(Sortable.options(element), arguments[1] || { });
+ var name = encodeURIComponent(
+ (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
+
+ if (options.tree) {
+ return Sortable.tree(element, arguments[1]).children.map( function (item) {
+ return [name + Sortable._constructIndex(item) + "[id]=" +
+ encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
+ }).flatten().join('&');
+ } else {
+ return Sortable.sequence(element, arguments[1]).map( function(item) {
+ return name + "[]=" + encodeURIComponent(item);
+ }).join('&');
+ }
+ }
+}
+
+// Returns true if child is contained within element
+Element.isParent = function(child, element) {
+ if (!child.parentNode || child == element) return false;
+ if (child.parentNode == element) return true;
+ return Element.isParent(child.parentNode, element);
+}
+
+Element.findChildren = function(element, only, recursive, tagName) {
+ if(!element.hasChildNodes()) return null;
+ tagName = tagName.toUpperCase();
+ if(only) only = [only].flatten();
+ var elements = [];
+ $A(element.childNodes).each( function(e) {
+ if(e.tagName && e.tagName.toUpperCase()==tagName &&
+ (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
+ elements.push(e);
+ if(recursive) {
+ var grandchildren = Element.findChildren(e, only, recursive, tagName);
+ if(grandchildren) elements.push(grandchildren);
+ }
+ });
+
+ return (elements.length>0 ? elements.flatten() : []);
+}
+
+Element.offsetSize = function (element, type) {
+ return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
+}
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/effects.js
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/effects.js (rev 0)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/effects.js 2008-02-11 02:47:55 UTC (rev 9930)
@@ -0,0 +1,1122 @@
+// script.aculo.us effects.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+// Justin Palmer (http://encytemedia.com/)
+// Mark Pilgrim (http://diveintomark.org/)
+// Martin Bialasinki
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// converts rgb() and #xxx to #xxxxxx format,
+// returns self (or first argument) if not convertable
+String.prototype.parseColor = function() {
+ var color = '#';
+ if (this.slice(0,4) == 'rgb(') {
+ var cols = this.slice(4,this.length-1).split(',');
+ var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
+ } else {
+ if (this.slice(0,1) == '#') {
+ if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
+ if (this.length==7) color = this.toLowerCase();
+ }
+ }
+ return (color.length==7 ? color : (arguments[0] || this));
+};
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {
+ return $A($(element).childNodes).collect( function(node) {
+ return (node.nodeType==3 ? node.nodeValue :
+ (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+ }).flatten().join('');
+};
+
+Element.collectTextNodesIgnoreClass = function(element, className) {
+ return $A($(element).childNodes).collect( function(node) {
+ return (node.nodeType==3 ? node.nodeValue :
+ ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
+ Element.collectTextNodesIgnoreClass(node, className) : ''));
+ }).flatten().join('');
+};
+
+Element.setContentZoom = function(element, percent) {
+ element = $(element);
+ element.setStyle({fontSize: (percent/100) + 'em'});
+ if (Prototype.Browser.WebKit) window.scrollBy(0,0);
+ return element;
+};
+
+Element.getInlineOpacity = function(element){
+ return $(element).style.opacity || '';
+};
+
+Element.forceRerendering = function(element) {
+ try {
+ element = $(element);
+ var n = document.createTextNode(' ');
+ element.appendChild(n);
+ element.removeChild(n);
+ } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+ _elementDoesNotExistError: {
+ name: 'ElementDoesNotExistError',
+ message: 'The specified DOM element does not exist, but is required for this effect to operate'
+ },
+ Transitions: {
+ linear: Prototype.K,
+ sinoidal: function(pos) {
+ return (-Math.cos(pos*Math.PI)/2) + 0.5;
+ },
+ reverse: function(pos) {
+ return 1-pos;
+ },
+ flicker: function(pos) {
+ var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+ return pos > 1 ? 1 : pos;
+ },
+ wobble: function(pos) {
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+ },
+ pulse: function(pos, pulses) {
+ pulses = pulses || 5;
+ return (
+ ((pos % (1/pulses)) * pulses).round() == 0 ?
+ ((pos * pulses * 2) - (pos * pulses * 2).floor()) :
+ 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
+ );
+ },
+ spring: function(pos) {
+ return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
+ },
+ none: function(pos) {
+ return 0;
+ },
+ full: function(pos) {
+ return 1;
+ }
+ },
+ DefaultOptions: {
+ duration: 1.0, // seconds
+ fps: 100, // 100= assume 66fps max.
+ sync: false, // true for combining
+ from: 0.0,
+ to: 1.0,
+ delay: 0.0,
+ queue: 'parallel'
+ },
+ tagifyText: function(element) {
+ var tagifyStyle = 'position:relative';
+ if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
+
+ element = $(element);
+ $A(element.childNodes).each( function(child) {
+ if (child.nodeType==3) {
+ child.nodeValue.toArray().each( function(character) {
+ element.insertBefore(
+ new Element('span', {style: tagifyStyle}).update(
+ character == ' ' ? String.fromCharCode(160) : character),
+ child);
+ });
+ Element.remove(child);
+ }
+ });
+ },
+ multiple: function(element, effect) {
+ var elements;
+ if (((typeof element == 'object') ||
+ Object.isFunction(element)) &&
+ (element.length))
+ elements = element;
+ else
+ elements = $(element).childNodes;
+
+ var options = Object.extend({
+ speed: 0.1,
+ delay: 0.0
+ }, arguments[2] || { });
+ var masterDelay = options.delay;
+
+ $A(elements).each( function(element, index) {
+ new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+ });
+ },
+ PAIRS: {
+ 'slide': ['SlideDown','SlideUp'],
+ 'blind': ['BlindDown','BlindUp'],
+ 'appear': ['Appear','Fade']
+ },
+ toggle: function(element, effect) {
+ element = $(element);
+ effect = (effect || 'appear').toLowerCase();
+ var options = Object.extend({
+ queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+ }, arguments[2] || { });
+ Effect[element.visible() ?
+ Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+ }
+};
+
+Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create(Enumerable, {
+ initialize: function() {
+ this.effects = [];
+ this.interval = null;
+ },
+ _each: function(iterator) {
+ this.effects._each(iterator);
+ },
+ add: function(effect) {
+ var timestamp = new Date().getTime();
+
+ var position = Object.isString(effect.options.queue) ?
+ effect.options.queue : effect.options.queue.position;
+
+ switch(position) {
+ case 'front':
+ // move unstarted effects after this effect
+ this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+ e.startOn += effect.finishOn;
+ e.finishOn += effect.finishOn;
+ });
+ break;
+ case 'with-last':
+ timestamp = this.effects.pluck('startOn').max() || timestamp;
+ break;
+ case 'end':
+ // start effect after last queued effect has finished
+ timestamp = this.effects.pluck('finishOn').max() || timestamp;
+ break;
+ }
+
+ effect.startOn += timestamp;
+ effect.finishOn += timestamp;
+
+ if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+ this.effects.push(effect);
+
+ if (!this.interval)
+ this.interval = setInterval(this.loop.bind(this), 15);
+ },
+ remove: function(effect) {
+ this.effects = this.effects.reject(function(e) { return e==effect });
+ if (this.effects.length == 0) {
+ clearInterval(this.interval);
+ this.interval = null;
+ }
+ },
+ loop: function() {
+ var timePos = new Date().getTime();
+ for(var i=0, len=this.effects.length;i<len;i++)
+ this.effects[i] && this.effects[i].loop(timePos);
+ }
+});
+
+Effect.Queues = {
+ instances: $H(),
+ get: function(queueName) {
+ if (!Object.isString(queueName)) return queueName;
+
+ return this.instances.get(queueName) ||
+ this.instances.set(queueName, new Effect.ScopedQueue());
+ }
+};
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.Base = Class.create({
+ position: null,
+ start: function(options) {
+ function codeForEvent(options,eventName){
+ return (
+ (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
+ (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
+ );
+ }
+ if (options && options.transition === false) options.transition = Effect.Transitions.linear;
+ this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
+ this.currentFrame = 0;
+ this.state = 'idle';
+ this.startOn = this.options.delay*1000;
+ this.finishOn = this.startOn+(this.options.duration*1000);
+ this.fromToDelta = this.options.to-this.options.from;
+ this.totalTime = this.finishOn-this.startOn;
+ this.totalFrames = this.options.fps*this.options.duration;
+
+ eval('this.render = function(pos){ '+
+ 'if (this.state=="idle"){this.state="running";'+
+ codeForEvent(this.options,'beforeSetup')+
+ (this.setup ? 'this.setup();':'')+
+ codeForEvent(this.options,'afterSetup')+
+ '};if (this.state=="running"){'+
+ 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
+ 'this.position=pos;'+
+ codeForEvent(this.options,'beforeUpdate')+
+ (this.update ? 'this.update(pos);':'')+
+ codeForEvent(this.options,'afterUpdate')+
+ '}}');
+
+ this.event('beforeStart');
+ if (!this.options.sync)
+ Effect.Queues.get(Object.isString(this.options.queue) ?
+ 'global' : this.options.queue.scope).add(this);
+ },
+ loop: function(timePos) {
+ if (timePos >= this.startOn) {
+ if (timePos >= this.finishOn) {
+ this.render(1.0);
+ this.cancel();
+ this.event('beforeFinish');
+ if (this.finish) this.finish();
+ this.event('afterFinish');
+ return;
+ }
+ var pos = (timePos - this.startOn) / this.totalTime,
+ frame = (pos * this.totalFrames).round();
+ if (frame > this.currentFrame) {
+ this.render(pos);
+ this.currentFrame = frame;
+ }
+ }
+ },
+ cancel: function() {
+ if (!this.options.sync)
+ Effect.Queues.get(Object.isString(this.options.queue) ?
+ 'global' : this.options.queue.scope).remove(this);
+ this.state = 'finished';
+ },
+ event: function(eventName) {
+ if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+ if (this.options[eventName]) this.options[eventName](this);
+ },
+ inspect: function() {
+ var data = $H();
+ for(property in this)
+ if (!Object.isFunction(this[property])) data.set(property, this[property]);
+ return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
+ }
+});
+
+Effect.Parallel = Class.create(Effect.Base, {
+ initialize: function(effects) {
+ this.effects = effects || [];
+ this.start(arguments[1]);
+ },
+ update: function(position) {
+ this.effects.invoke('render', position);
+ },
+ finish: function(position) {
+ this.effects.each( function(effect) {
+ effect.render(1.0);
+ effect.cancel();
+ effect.event('beforeFinish');
+ if (effect.finish) effect.finish(position);
+ effect.event('afterFinish');
+ });
+ }
+});
+
+Effect.Tween = Class.create(Effect.Base, {
+ initialize: function(object, from, to) {
+ object = Object.isString(object) ? $(object) : object;
+ var args = $A(arguments), method = args.last(),
+ options = args.length == 5 ? args[3] : null;
+ this.method = Object.isFunction(method) ? method.bind(object) :
+ Object.isFunction(object[method]) ? object[method].bind(object) :
+ function(value) { object[method] = value };
+ this.start(Object.extend({ from: from, to: to }, options || { }));
+ },
+ update: function(position) {
+ this.method(position);
+ }
+});
+
+Effect.Event = Class.create(Effect.Base, {
+ initialize: function() {
+ this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
+ },
+ update: Prototype.emptyFunction
+});
+
+Effect.Opacity = Class.create(Effect.Base, {
+ initialize: function(element) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ // make this work on IE on elements without 'layout'
+ if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+ this.element.setStyle({zoom: 1});
+ var options = Object.extend({
+ from: this.element.getOpacity() || 0.0,
+ to: 1.0
+ }, arguments[1] || { });
+ this.start(options);
+ },
+ update: function(position) {
+ this.element.setOpacity(position);
+ }
+});
+
+Effect.Move = Class.create(Effect.Base, {
+ initialize: function(element) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({
+ x: 0,
+ y: 0,
+ mode: 'relative'
+ }, arguments[1] || { });
+ this.start(options);
+ },
+ setup: function() {
+ this.element.makePositioned();
+ this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+ this.originalTop = parseFloat(this.element.getStyle('top') || '0');
+ if (this.options.mode == 'absolute') {
+ this.options.x = this.options.x - this.originalLeft;
+ this.options.y = this.options.y - this.originalTop;
+ }
+ },
+ update: function(position) {
+ this.element.setStyle({
+ left: (this.options.x * position + this.originalLeft).round() + 'px',
+ top: (this.options.y * position + this.originalTop).round() + 'px'
+ });
+ }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+ return new Effect.Move(element,
+ Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
+};
+
+Effect.Scale = Class.create(Effect.Base, {
+ initialize: function(element, percent) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({
+ scaleX: true,
+ scaleY: true,
+ scaleContent: true,
+ scaleFromCenter: false,
+ scaleMode: 'box', // 'box' or 'contents' or { } with provided values
+ scaleFrom: 100.0,
+ scaleTo: percent
+ }, arguments[2] || { });
+ this.start(options);
+ },
+ setup: function() {
+ this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+ this.elementPositioning = this.element.getStyle('position');
+
+ this.originalStyle = { };
+ ['top','left','width','height','fontSize'].each( function(k) {
+ this.originalStyle[k] = this.element.style[k];
+ }.bind(this));
+
+ this.originalTop = this.element.offsetTop;
+ this.originalLeft = this.element.offsetLeft;
+
+ var fontSize = this.element.getStyle('font-size') || '100%';
+ ['em','px','%','pt'].each( function(fontSizeType) {
+ if (fontSize.indexOf(fontSizeType)>0) {
+ this.fontSize = parseFloat(fontSize);
+ this.fontSizeType = fontSizeType;
+ }
+ }.bind(this));
+
+ this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+
+ this.dims = null;
+ if (this.options.scaleMode=='box')
+ this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+ if (/^content/.test(this.options.scaleMode))
+ this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+ if (!this.dims)
+ this.dims = [this.options.scaleMode.originalHeight,
+ this.options.scaleMode.originalWidth];
+ },
+ update: function(position) {
+ var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+ if (this.options.scaleContent && this.fontSize)
+ this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+ this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+ },
+ finish: function(position) {
+ if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+ },
+ setDimensions: function(height, width) {
+ var d = { };
+ if (this.options.scaleX) d.width = width.round() + 'px';
+ if (this.options.scaleY) d.height = height.round() + 'px';
+ if (this.options.scaleFromCenter) {
+ var topd = (height - this.dims[0])/2;
+ var leftd = (width - this.dims[1])/2;
+ if (this.elementPositioning == 'absolute') {
+ if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
+ if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+ } else {
+ if (this.options.scaleY) d.top = -topd + 'px';
+ if (this.options.scaleX) d.left = -leftd + 'px';
+ }
+ }
+ this.element.setStyle(d);
+ }
+});
+
+Effect.Highlight = Class.create(Effect.Base, {
+ initialize: function(element) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
+ this.start(options);
+ },
+ setup: function() {
+ // Prevent executing on elements not in the layout flow
+ if (this.element.getStyle('display')=='none') { this.cancel(); return; }
+ // Disable background image during the effect
+ this.oldStyle = { };
+ if (!this.options.keepBackgroundImage) {
+ this.oldStyle.backgroundImage = this.element.getStyle('background-image');
+ this.element.setStyle({backgroundImage: 'none'});
+ }
+ if (!this.options.endcolor)
+ this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+ if (!this.options.restorecolor)
+ this.options.restorecolor = this.element.getStyle('background-color');
+ // init color calculations
+ this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+ this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+ },
+ update: function(position) {
+ this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+ return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
+ },
+ finish: function() {
+ this.element.setStyle(Object.extend(this.oldStyle, {
+ backgroundColor: this.options.restorecolor
+ }));
+ }
+});
+
+Effect.ScrollTo = function(element) {
+ var options = arguments[1] || { },
+ scrollOffsets = document.viewport.getScrollOffsets(),
+ elementOffsets = $(element).cumulativeOffset(),
+ max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();
+
+ if (options.offset) elementOffsets[1] += options.offset;
+
+ return new Effect.Tween(null,
+ scrollOffsets.top,
+ elementOffsets[1] > max ? max : elementOffsets[1],
+ options,
+ function(p){ scrollTo(scrollOffsets.left, p.round()) }
+ );
+};
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+ element = $(element);
+ var oldOpacity = element.getInlineOpacity();
+ var options = Object.extend({
+ from: element.getOpacity() || 1.0,
+ to: 0.0,
+ afterFinishInternal: function(effect) {
+ if (effect.options.to!=0) return;
+ effect.element.hide().setStyle({opacity: oldOpacity});
+ }
+ }, arguments[1] || { });
+ return new Effect.Opacity(element,options);
+};
+
+Effect.Appear = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+ to: 1.0,
+ // force Safari to render floated elements properly
+ afterFinishInternal: function(effect) {
+ effect.element.forceRerendering();
+ },
+ beforeSetup: function(effect) {
+ effect.element.setOpacity(effect.options.from).show();
+ }}, arguments[1] || { });
+ return new Effect.Opacity(element,options);
+};
+
+Effect.Puff = function(element) {
+ element = $(element);
+ var oldStyle = {
+ opacity: element.getInlineOpacity(),
+ position: element.getStyle('position'),
+ top: element.style.top,
+ left: element.style.left,
+ width: element.style.width,
+ height: element.style.height
+ };
+ return new Effect.Parallel(
+ [ new Effect.Scale(element, 200,
+ { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
+ Object.extend({ duration: 1.0,
+ beforeSetupInternal: function(effect) {
+ Position.absolutize(effect.effects[0].element)
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide().setStyle(oldStyle); }
+ }, arguments[1] || { })
+ );
+};
+
+Effect.BlindUp = function(element) {
+ element = $(element);
+ element.makeClipping();
+ return new Effect.Scale(element, 0,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ restoreAfterFinish: true,
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping();
+ }
+ }, arguments[1] || { })
+ );
+};
+
+Effect.BlindDown = function(element) {
+ element = $(element);
+ var elementDimensions = element.getDimensions();
+ return new Effect.Scale(element, 100, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ scaleFrom: 0,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) {
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.undoClipping();
+ }
+ }, arguments[1] || { }));
+};
+
+Effect.SwitchOff = function(element) {
+ element = $(element);
+ var oldOpacity = element.getInlineOpacity();
+ return new Effect.Appear(element, Object.extend({
+ duration: 0.4,
+ from: 0,
+ transition: Effect.Transitions.flicker,
+ afterFinishInternal: function(effect) {
+ new Effect.Scale(effect.element, 1, {
+ duration: 0.3, scaleFromCenter: true,
+ scaleX: false, scaleContent: false, restoreAfterFinish: true,
+ beforeSetup: function(effect) {
+ effect.element.makePositioned().makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
+ }
+ })
+ }
+ }, arguments[1] || { }));
+};
+
+Effect.DropOut = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: element.getStyle('top'),
+ left: element.getStyle('left'),
+ opacity: element.getInlineOpacity() };
+ return new Effect.Parallel(
+ [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+ Object.extend(
+ { duration: 0.5,
+ beforeSetup: function(effect) {
+ effect.effects[0].element.makePositioned();
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
+ }
+ }, arguments[1] || { }));
+};
+
+Effect.Shake = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ distance: 20,
+ duration: 0.5
+ }, arguments[1] || {});
+ var distance = parseFloat(options.distance);
+ var split = parseFloat(options.duration) / 10.0;
+ var oldStyle = {
+ top: element.getStyle('top'),
+ left: element.getStyle('left') };
+ return new Effect.Move(element,
+ { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+ effect.element.undoPositioned().setStyle(oldStyle);
+ }}) }}) }}) }}) }}) }});
+};
+
+Effect.SlideDown = function(element) {
+ element = $(element).cleanWhitespace();
+ // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+ var oldInnerBottom = element.down().getStyle('bottom');
+ var elementDimensions = element.getDimensions();
+ return new Effect.Scale(element, 100, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ scaleFrom: window.opera ? 0 : 1,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) {
+ effect.element.makePositioned();
+ effect.element.down().makePositioned();
+ if (window.opera) effect.element.setStyle({top: ''});
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
+ },
+ afterUpdateInternal: function(effect) {
+ effect.element.down().setStyle({bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.undoClipping().undoPositioned();
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
+ }, arguments[1] || { })
+ );
+};
+
+Effect.SlideUp = function(element) {
+ element = $(element).cleanWhitespace();
+ var oldInnerBottom = element.down().getStyle('bottom');
+ var elementDimensions = element.getDimensions();
+ return new Effect.Scale(element, window.opera ? 0 : 1,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ scaleMode: 'box',
+ scaleFrom: 100,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) {
+ effect.element.makePositioned();
+ effect.element.down().makePositioned();
+ if (window.opera) effect.element.setStyle({top: ''});
+ effect.element.makeClipping().show();
+ },
+ afterUpdateInternal: function(effect) {
+ effect.element.down().setStyle({bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping().undoPositioned();
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
+ }
+ }, arguments[1] || { })
+ );
+};
+
+// Bug in opera makes the TD containing this element expand for a instance after finish
+Effect.Squish = function(element) {
+ return new Effect.Scale(element, window.opera ? 1 : 0, {
+ restoreAfterFinish: true,
+ beforeSetup: function(effect) {
+ effect.element.makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping();
+ }
+ });
+};
+
+Effect.Grow = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ direction: 'center',
+ moveTransition: Effect.Transitions.sinoidal,
+ scaleTransition: Effect.Transitions.sinoidal,
+ opacityTransition: Effect.Transitions.full
+ }, arguments[1] || { });
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ height: element.style.height,
+ width: element.style.width,
+ opacity: element.getInlineOpacity() };
+
+ var dims = element.getDimensions();
+ var initialMoveX, initialMoveY;
+ var moveX, moveY;
+
+ switch (options.direction) {
+ case 'top-left':
+ initialMoveX = initialMoveY = moveX = moveY = 0;
+ break;
+ case 'top-right':
+ initialMoveX = dims.width;
+ initialMoveY = moveY = 0;
+ moveX = -dims.width;
+ break;
+ case 'bottom-left':
+ initialMoveX = moveX = 0;
+ initialMoveY = dims.height;
+ moveY = -dims.height;
+ break;
+ case 'bottom-right':
+ initialMoveX = dims.width;
+ initialMoveY = dims.height;
+ moveX = -dims.width;
+ moveY = -dims.height;
+ break;
+ case 'center':
+ initialMoveX = dims.width / 2;
+ initialMoveY = dims.height / 2;
+ moveX = -dims.width / 2;
+ moveY = -dims.height / 2;
+ break;
+ }
+
+ return new Effect.Move(element, {
+ x: initialMoveX,
+ y: initialMoveY,
+ duration: 0.01,
+ beforeSetup: function(effect) {
+ effect.element.hide().makeClipping().makePositioned();
+ },
+ afterFinishInternal: function(effect) {
+ new Effect.Parallel(
+ [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+ new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+ new Effect.Scale(effect.element, 100, {
+ scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
+ sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+ ], Object.extend({
+ beforeSetup: function(effect) {
+ effect.effects[0].element.setStyle({height: '0px'}).show();
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
+ }
+ }, options)
+ )
+ }
+ });
+};
+
+Effect.Shrink = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ direction: 'center',
+ moveTransition: Effect.Transitions.sinoidal,
+ scaleTransition: Effect.Transitions.sinoidal,
+ opacityTransition: Effect.Transitions.none
+ }, arguments[1] || { });
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ height: element.style.height,
+ width: element.style.width,
+ opacity: element.getInlineOpacity() };
+
+ var dims = element.getDimensions();
+ var moveX, moveY;
+
+ switch (options.direction) {
+ case 'top-left':
+ moveX = moveY = 0;
+ break;
+ case 'top-right':
+ moveX = dims.width;
+ moveY = 0;
+ break;
+ case 'bottom-left':
+ moveX = 0;
+ moveY = dims.height;
+ break;
+ case 'bottom-right':
+ moveX = dims.width;
+ moveY = dims.height;
+ break;
+ case 'center':
+ moveX = dims.width / 2;
+ moveY = dims.height / 2;
+ break;
+ }
+
+ return new Effect.Parallel(
+ [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+ new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+ new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+ ], Object.extend({
+ beforeStartInternal: function(effect) {
+ effect.effects[0].element.makePositioned().makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
+ }, options)
+ );
+};
+
+Effect.Pulsate = function(element) {
+ element = $(element);
+ var options = arguments[1] || { };
+ var oldOpacity = element.getInlineOpacity();
+ var transition = options.transition || Effect.Transitions.sinoidal;
+ var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
+ reverser.bind(transition);
+ return new Effect.Opacity(element,
+ Object.extend(Object.extend({ duration: 2.0, from: 0,
+ afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+ }, options), {transition: reverser}));
+};
+
+Effect.Fold = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ width: element.style.width,
+ height: element.style.height };
+ element.makeClipping();
+ return new Effect.Scale(element, 5, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ afterFinishInternal: function(effect) {
+ new Effect.Scale(element, 1, {
+ scaleContent: false,
+ scaleY: false,
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping().setStyle(oldStyle);
+ } });
+ }}, arguments[1] || { }));
+};
+
+Effect.Morph = Class.create(Effect.Base, {
+ initialize: function(element) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({
+ style: { }
+ }, arguments[1] || { });
+
+ if (!Object.isString(options.style)) this.style = $H(options.style);
+ else {
+ if (options.style.include(':'))
+ this.style = options.style.parseStyle();
+ else {
+ this.element.addClassName(options.style);
+ this.style = $H(this.element.getStyles());
+ this.element.removeClassName(options.style);
+ var css = this.element.getStyles();
+ this.style = this.style.reject(function(style) {
+ return style.value == css[style.key];
+ });
+ options.afterFinishInternal = function(effect) {
+ effect.element.addClassName(effect.options.style);
+ effect.transforms.each(function(transform) {
+ effect.element.style[transform.style] = '';
+ });
+ }
+ }
+ }
+ this.start(options);
+ },
+
+ setup: function(){
+ function parseColor(color){
+ if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+ color = color.parseColor();
+ return $R(0,2).map(function(i){
+ return parseInt( color.slice(i*2+1,i*2+3), 16 )
+ });
+ }
+ this.transforms = this.style.map(function(pair){
+ var property = pair[0], value = pair[1], unit = null;
+
+ if (value.parseColor('#zzzzzz') != '#zzzzzz') {
+ value = value.parseColor();
+ unit = 'color';
+ } else if (property == 'opacity') {
+ value = parseFloat(value);
+ if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+ this.element.setStyle({zoom: 1});
+ } else if (Element.CSS_LENGTH.test(value)) {
+ var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
+ value = parseFloat(components[1]);
+ unit = (components.length == 3) ? components[2] : null;
+ }
+
+ var originalValue = this.element.getStyle(property);
+ return {
+ style: property.camelize(),
+ originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
+ targetValue: unit=='color' ? parseColor(value) : value,
+ unit: unit
+ };
+ }.bind(this)).reject(function(transform){
+ return (
+ (transform.originalValue == transform.targetValue) ||
+ (
+ transform.unit != 'color' &&
+ (isNaN(transform.originalValue) || isNaN(transform.targetValue))
+ )
+ )
+ });
+ },
+ update: function(position) {
+ var style = { }, transform, i = this.transforms.length;
+ while(i--)
+ style[(transform = this.transforms[i]).style] =
+ transform.unit=='color' ? '#'+
+ (Math.round(transform.originalValue[0]+
+ (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
+ (Math.round(transform.originalValue[1]+
+ (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
+ (Math.round(transform.originalValue[2]+
+ (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
+ (transform.originalValue +
+ (transform.targetValue - transform.originalValue) * position).toFixed(3) +
+ (transform.unit === null ? '' : transform.unit);
+ this.element.setStyle(style, true);
+ }
+});
+
+Effect.Transform = Class.create({
+ initialize: function(tracks){
+ this.tracks = [];
+ this.options = arguments[1] || { };
+ this.addTracks(tracks);
+ },
+ addTracks: function(tracks){
+ tracks.each(function(track){
+ track = $H(track);
+ var data = track.values().first();
+ this.tracks.push($H({
+ ids: track.keys().first(),
+ effect: Effect.Morph,
+ options: { style: data }
+ }));
+ }.bind(this));
+ return this;
+ },
+ play: function(){
+ return new Effect.Parallel(
+ this.tracks.map(function(track){
+ var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
+ var elements = [$(ids) || $$(ids)].flatten();
+ return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
+ }).flatten(),
+ this.options
+ );
+ }
+});
+
+Element.CSS_PROPERTIES = $w(
+ 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
+ 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
+ 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
+ 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
+ 'fontSize fontWeight height left letterSpacing lineHeight ' +
+ 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
+ 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
+ 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
+ 'right textIndent top width wordSpacing zIndex');
+
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.__parseStyleElement = document.createElement('div');
+String.prototype.parseStyle = function(){
+ var style, styleRules = $H();
+ if (Prototype.Browser.WebKit)
+ style = new Element('div',{style:this}).style;
+ else {
+ String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
+ style = String.__parseStyleElement.childNodes[0].style;
+ }
+
+ Element.CSS_PROPERTIES.each(function(property){
+ if (style[property]) styleRules.set(property, style[property]);
+ });
+
+ if (Prototype.Browser.IE && this.include('opacity'))
+ styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
+
+ return styleRules;
+};
+
+if (document.defaultView && document.defaultView.getComputedStyle) {
+ Element.getStyles = function(element) {
+ var css = document.defaultView.getComputedStyle($(element), null);
+ return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
+ styles[property] = css[property];
+ return styles;
+ });
+ };
+} else {
+ Element.getStyles = function(element) {
+ element = $(element);
+ var css = element.currentStyle, styles;
+ styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
+ results[property] = css[property];
+ return results;
+ });
+ if (!styles.opacity) styles.opacity = element.getOpacity();
+ return styles;
+ };
+};
+
+Effect.Methods = {
+ morph: function(element, style) {
+ element = $(element);
+ new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
+ return element;
+ },
+ visualEffect: function(element, effect, options) {
+ element = $(element)
+ var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
+ new Effect[klass](element, options);
+ return element;
+ },
+ highlight: function(element, options) {
+ element = $(element);
+ new Effect.Highlight(element, options);
+ return element;
+ }
+};
+
+$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
+ 'pulsate shake puff squish switchOff dropOut').each(
+ function(effect) {
+ Effect.Methods[effect] = function(element, options){
+ element = $(element);
+ Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
+ return element;
+ }
+ }
+);
+
+$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
+ function(f) { Effect.Methods[f] = Element[f]; }
+);
+
+Element.addMethods(Effect.Methods);
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/scriptaculous.js
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/scriptaculous.js (rev 0)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/scriptaculous.js 2008-02-11 02:47:55 UTC (rev 9930)
@@ -0,0 +1,58 @@
+// script.aculo.us scriptaculous.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+var Scriptaculous = {
+ Version: '1.8.1',
+ require: function(libraryName) {
+ // inserting via DOM fails in Safari 2.0, so brute force approach
+ document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
+ },
+ REQUIRED_PROTOTYPE: '1.6.0',
+ load: function() {
+ function convertVersionString(versionString){
+ var r = versionString.split('.');
+ return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
+ }
+
+ if((typeof Prototype=='undefined') ||
+ (typeof Element == 'undefined') ||
+ (typeof Element.Methods=='undefined') ||
+ (convertVersionString(Prototype.Version) <
+ convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
+ throw("script.aculo.us requires the Prototype JavaScript framework >= " +
+ Scriptaculous.REQUIRED_PROTOTYPE);
+
+ $A(document.getElementsByTagName("script")).findAll( function(s) {
+ return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
+ }).each( function(s) {
+ var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
+ var includes = s.src.match(/\?.*load=([a-z,]*)/);
+ (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
+ function(include) { Scriptaculous.require(path+include+'.js') });
+ });
+ }
+}
+
+Scriptaculous.load();
\ No newline at end of file
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/slider.js
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/slider.js (rev 0)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/slider.js 2008-02-11 02:47:55 UTC (rev 9930)
@@ -0,0 +1,275 @@
+// script.aculo.us slider.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Marty Haught, Thomas Fuchs
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+if (!Control) var Control = { };
+
+// options:
+// axis: 'vertical', or 'horizontal' (default)
+//
+// callbacks:
+// onChange(value)
+// onSlide(value)
+Control.Slider = Class.create({
+ initialize: function(handle, track, options) {
+ var slider = this;
+
+ if (Object.isArray(handle)) {
+ this.handles = handle.collect( function(e) { return $(e) });
+ } else {
+ this.handles = [$(handle)];
+ }
+
+ this.track = $(track);
+ this.options = options || { };
+
+ this.axis = this.options.axis || 'horizontal';
+ this.increment = this.options.increment || 1;
+ this.step = parseInt(this.options.step || '1');
+ this.range = this.options.range || $R(0,1);
+
+ this.value = 0; // assure backwards compat
+ this.values = this.handles.map( function() { return 0 });
+ this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
+ this.options.startSpan = $(this.options.startSpan || null);
+ this.options.endSpan = $(this.options.endSpan || null);
+
+ this.restricted = this.options.restricted || false;
+
+ this.maximum = this.options.maximum || this.range.end;
+ this.minimum = this.options.minimum || this.range.start;
+
+ // Will be used to align the handle onto the track, if necessary
+ this.alignX = parseInt(this.options.alignX || '0');
+ this.alignY = parseInt(this.options.alignY || '0');
+
+ this.trackLength = this.maximumOffset() - this.minimumOffset();
+
+ this.handleLength = this.isVertical() ?
+ (this.handles[0].offsetHeight != 0 ?
+ this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) :
+ (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth :
+ this.handles[0].style.width.replace(/px$/,""));
+
+ this.active = false;
+ this.dragging = false;
+ this.disabled = false;
+
+ if (this.options.disabled) this.setDisabled();
+
+ // Allowed values array
+ this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
+ if (this.allowedValues) {
+ this.minimum = this.allowedValues.min();
+ this.maximum = this.allowedValues.max();
+ }
+
+ this.eventMouseDown = this.startDrag.bindAsEventListener(this);
+ this.eventMouseUp = this.endDrag.bindAsEventListener(this);
+ this.eventMouseMove = this.update.bindAsEventListener(this);
+
+ // Initialize handles in reverse (make sure first handle is active)
+ this.handles.each( function(h,i) {
+ i = slider.handles.length-1-i;
+ slider.setValue(parseFloat(
+ (Object.isArray(slider.options.sliderValue) ?
+ slider.options.sliderValue[i] : slider.options.sliderValue) ||
+ slider.range.start), i);
+ h.makePositioned().observe("mousedown", slider.eventMouseDown);
+ });
+
+ this.track.observe("mousedown", this.eventMouseDown);
+ document.observe("mouseup", this.eventMouseUp);
+ document.observe("mousemove", this.eventMouseMove);
+
+ this.initialized = true;
+ },
+ dispose: function() {
+ var slider = this;
+ Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
+ Event.stopObserving(document, "mouseup", this.eventMouseUp);
+ Event.stopObserving(document, "mousemove", this.eventMouseMove);
+ this.handles.each( function(h) {
+ Event.stopObserving(h, "mousedown", slider.eventMouseDown);
+ });
+ },
+ setDisabled: function(){
+ this.disabled = true;
+ },
+ setEnabled: function(){
+ this.disabled = false;
+ },
+ getNearestValue: function(value){
+ if (this.allowedValues){
+ if (value >= this.allowedValues.max()) return(this.allowedValues.max());
+ if (value <= this.allowedValues.min()) return(this.allowedValues.min());
+
+ var offset = Math.abs(this.allowedValues[0] - value);
+ var newValue = this.allowedValues[0];
+ this.allowedValues.each( function(v) {
+ var currentOffset = Math.abs(v - value);
+ if (currentOffset <= offset){
+ newValue = v;
+ offset = currentOffset;
+ }
+ });
+ return newValue;
+ }
+ if (value > this.range.end) return this.range.end;
+ if (value < this.range.start) return this.range.start;
+ return value;
+ },
+ setValue: function(sliderValue, handleIdx){
+ if (!this.active) {
+ this.activeHandleIdx = handleIdx || 0;
+ this.activeHandle = this.handles[this.activeHandleIdx];
+ this.updateStyles();
+ }
+ handleIdx = handleIdx || this.activeHandleIdx || 0;
+ if (this.initialized && this.restricted) {
+ if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
+ sliderValue = this.values[handleIdx-1];
+ if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
+ sliderValue = this.values[handleIdx+1];
+ }
+ sliderValue = this.getNearestValue(sliderValue);
+ this.values[handleIdx] = sliderValue;
+ this.value = this.values[0]; // assure backwards compat
+
+ this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
+ this.translateToPx(sliderValue);
+
+ this.drawSpans();
+ if (!this.dragging || !this.event) this.updateFinished();
+ },
+ setValueBy: function(delta, handleIdx) {
+ this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
+ handleIdx || this.activeHandleIdx || 0);
+ },
+ translateToPx: function(value) {
+ return Math.round(
+ ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
+ (value - this.range.start)) + "px";
+ },
+ translateToValue: function(offset) {
+ return ((offset/(this.trackLength-this.handleLength) *
+ (this.range.end-this.range.start)) + this.range.start);
+ },
+ getRange: function(range) {
+ var v = this.values.sortBy(Prototype.K);
+ range = range || 0;
+ return $R(v[range],v[range+1]);
+ },
+ minimumOffset: function(){
+ return(this.isVertical() ? this.alignY : this.alignX);
+ },
+ maximumOffset: function(){
+ return(this.isVertical() ?
+ (this.track.offsetHeight != 0 ? this.track.offsetHeight :
+ this.track.style.height.replace(/px$/,"")) - this.alignY :
+ (this.track.offsetWidth != 0 ? this.track.offsetWidth :
+ this.track.style.width.replace(/px$/,"")) - this.alignX);
+ },
+ isVertical: function(){
+ return (this.axis == 'vertical');
+ },
+ drawSpans: function() {
+ var slider = this;
+ if (this.spans)
+ $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
+ if (this.options.startSpan)
+ this.setSpan(this.options.startSpan,
+ $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
+ if (this.options.endSpan)
+ this.setSpan(this.options.endSpan,
+ $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
+ },
+ setSpan: function(span, range) {
+ if (this.isVertical()) {
+ span.style.top = this.translateToPx(range.start);
+ span.style.height = this.translateToPx(range.end - range.start + this.range.start);
+ } else {
+ span.style.left = this.translateToPx(range.start);
+ span.style.width = this.translateToPx(range.end - range.start + this.range.start);
+ }
+ },
+ updateStyles: function() {
+ this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
+ Element.addClassName(this.activeHandle, 'selected');
+ },
+ startDrag: function(event) {
+ if (Event.isLeftClick(event)) {
+ if (!this.disabled){
+ this.active = true;
+
+ var handle = Event.element(event);
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ var track = handle;
+ if (track==this.track) {
+ var offsets = Position.cumulativeOffset(this.track);
+ this.event = event;
+ this.setValue(this.translateToValue(
+ (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
+ ));
+ var offsets = Position.cumulativeOffset(this.activeHandle);
+ this.offsetX = (pointer[0] - offsets[0]);
+ this.offsetY = (pointer[1] - offsets[1]);
+ } else {
+ // find the handle (prevents issues with Safari)
+ while((this.handles.indexOf(handle) == -1) && handle.parentNode)
+ handle = handle.parentNode;
+
+ if (this.handles.indexOf(handle)!=-1) {
+ this.activeHandle = handle;
+ this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
+ this.updateStyles();
+
+ var offsets = Position.cumulativeOffset(this.activeHandle);
+ this.offsetX = (pointer[0] - offsets[0]);
+ this.offsetY = (pointer[1] - offsets[1]);
+ }
+ }
+ }
+ Event.stop(event);
+ }
+ },
+ update: function(event) {
+ if (this.active) {
+ if (!this.dragging) this.dragging = true;
+ this.draw(event);
+ if (Prototype.Browser.WebKit) window.scrollBy(0,0);
+ Event.stop(event);
+ }
+ },
+ draw: function(event) {
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ var offsets = Position.cumulativeOffset(this.track);
+ pointer[0] -= this.offsetX + offsets[0];
+ pointer[1] -= this.offsetY + offsets[1];
+ this.event = event;
+ this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
+ if (this.initialized && this.options.onSlide)
+ this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
+ },
+ endDrag: function(event) {
+ if (this.active && this.dragging) {
+ this.finishDrag(event, true);
+ Event.stop(event);
+ }
+ this.active = false;
+ this.dragging = false;
+ },
+ finishDrag: function(event, success) {
+ this.active = false;
+ this.dragging = false;
+ this.updateFinished();
+ },
+ updateFinished: function() {
+ if (this.initialized && this.options.onChange)
+ this.options.onChange(this.values.length>1 ? this.values : this.value, this);
+ this.event = null;
+ }
+});
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/sound.js
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/sound.js (rev 0)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/sound.js 2008-02-11 02:47:55 UTC (rev 9930)
@@ -0,0 +1,55 @@
+// script.aculo.us sound.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// Based on code created by Jules Gravinese (http://www.webveteran.com/)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+Sound = {
+ tracks: {},
+ _enabled: true,
+ template:
+ new Template('<embed style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>'),
+ enable: function(){
+ Sound._enabled = true;
+ },
+ disable: function(){
+ Sound._enabled = false;
+ },
+ play: function(url){
+ if(!Sound._enabled) return;
+ var options = Object.extend({
+ track: 'global', url: url, replace: false
+ }, arguments[1] || {});
+
+ if(options.replace && this.tracks[options.track]) {
+ $R(0, this.tracks[options.track].id).each(function(id){
+ var sound = $('sound_'+options.track+'_'+id);
+ sound.Stop && sound.Stop();
+ sound.remove();
+ })
+ this.tracks[options.track] = null;
+ }
+
+ if(!this.tracks[options.track])
+ this.tracks[options.track] = { id: 0 }
+ else
+ this.tracks[options.track].id++;
+
+ options.id = this.tracks[options.track].id;
+ $$('body')[0].insert(
+ Prototype.Browser.IE ? new Element('bgsound',{
+ id: 'sound_'+options.track+'_'+options.id,
+ src: options.url, loop: 1, autostart: true
+ }) : Sound.template.evaluate(options));
+ }
+};
+
+if(Prototype.Browser.Gecko && navigator.userAgent.indexOf("Win") > 0){
+ if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 }))
+ Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>')
+ else
+ Sound.play = function(){}
+}
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/unittest.js
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/unittest.js (rev 0)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/scriptaculous/unittest.js 2008-02-11 02:47:55 UTC (rev 9930)
@@ -0,0 +1,568 @@
+// script.aculo.us unittest.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
+// (c) 2005-2007 Michael Schuerig (http://www.schuerig.de/michael/)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// experimental, Firefox-only
+Event.simulateMouse = function(element, eventName) {
+ var options = Object.extend({
+ pointerX: 0,
+ pointerY: 0,
+ buttons: 0,
+ ctrlKey: false,
+ altKey: false,
+ shiftKey: false,
+ metaKey: false
+ }, arguments[2] || {});
+ var oEvent = document.createEvent("MouseEvents");
+ oEvent.initMouseEvent(eventName, true, true, document.defaultView,
+ options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
+ options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, $(element));
+
+ if(this.mark) Element.remove(this.mark);
+ this.mark = document.createElement('div');
+ this.mark.appendChild(document.createTextNode(" "));
+ document.body.appendChild(this.mark);
+ this.mark.style.position = 'absolute';
+ this.mark.style.top = options.pointerY + "px";
+ this.mark.style.left = options.pointerX + "px";
+ this.mark.style.width = "5px";
+ this.mark.style.height = "5px;";
+ this.mark.style.borderTop = "1px solid red;"
+ this.mark.style.borderLeft = "1px solid red;"
+
+ if(this.step)
+ alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
+
+ $(element).dispatchEvent(oEvent);
+};
+
+// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
+// You need to downgrade to 1.0.4 for now to get this working
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
+Event.simulateKey = function(element, eventName) {
+ var options = Object.extend({
+ ctrlKey: false,
+ altKey: false,
+ shiftKey: false,
+ metaKey: false,
+ keyCode: 0,
+ charCode: 0
+ }, arguments[2] || {});
+
+ var oEvent = document.createEvent("KeyEvents");
+ oEvent.initKeyEvent(eventName, true, true, window,
+ options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
+ options.keyCode, options.charCode );
+ $(element).dispatchEvent(oEvent);
+};
+
+Event.simulateKeys = function(element, command) {
+ for(var i=0; i<command.length; i++) {
+ Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
+ }
+};
+
+var Test = {}
+Test.Unit = {};
+
+// security exception workaround
+Test.Unit.inspect = Object.inspect;
+
+Test.Unit.Logger = Class.create();
+Test.Unit.Logger.prototype = {
+ initialize: function(log) {
+ this.log = $(log);
+ if (this.log) {
+ this._createLogTable();
+ }
+ },
+ start: function(testName) {
+ if (!this.log) return;
+ this.testName = testName;
+ this.lastLogLine = document.createElement('tr');
+ this.statusCell = document.createElement('td');
+ this.nameCell = document.createElement('td');
+ this.nameCell.className = "nameCell";
+ this.nameCell.appendChild(document.createTextNode(testName));
+ this.messageCell = document.createElement('td');
+ this.lastLogLine.appendChild(this.statusCell);
+ this.lastLogLine.appendChild(this.nameCell);
+ this.lastLogLine.appendChild(this.messageCell);
+ this.loglines.appendChild(this.lastLogLine);
+ },
+ finish: function(status, summary) {
+ if (!this.log) return;
+ this.lastLogLine.className = status;
+ this.statusCell.innerHTML = status;
+ this.messageCell.innerHTML = this._toHTML(summary);
+ this.addLinksToResults();
+ },
+ message: function(message) {
+ if (!this.log) return;
+ this.messageCell.innerHTML = this._toHTML(message);
+ },
+ summary: function(summary) {
+ if (!this.log) return;
+ this.logsummary.innerHTML = this._toHTML(summary);
+ },
+ _createLogTable: function() {
+ this.log.innerHTML =
+ '<div id="logsummary"></div>' +
+ '<table id="logtable">' +
+ '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
+ '<tbody id="loglines"></tbody>' +
+ '</table>';
+ this.logsummary = $('logsummary')
+ this.loglines = $('loglines');
+ },
+ _toHTML: function(txt) {
+ return txt.escapeHTML().replace(/\n/g,"<br/>");
+ },
+ addLinksToResults: function(){
+ $$("tr.failed .nameCell").each( function(td){ // todo: limit to children of this.log
+ td.title = "Run only this test"
+ Event.observe(td, 'click', function(){ window.location.search = "?tests=" + td.innerHTML;});
+ });
+ $$("tr.passed .nameCell").each( function(td){ // todo: limit to children of this.log
+ td.title = "Run all tests"
+ Event.observe(td, 'click', function(){ window.location.search = "";});
+ });
+ }
+}
+
+Test.Unit.Runner = Class.create();
+Test.Unit.Runner.prototype = {
+ initialize: function(testcases) {
+ this.options = Object.extend({
+ testLog: 'testlog'
+ }, arguments[1] || {});
+ this.options.resultsURL = this.parseResultsURLQueryParameter();
+ this.options.tests = this.parseTestsQueryParameter();
+ if (this.options.testLog) {
+ this.options.testLog = $(this.options.testLog) || null;
+ }
+ if(this.options.tests) {
+ this.tests = [];
+ for(var i = 0; i < this.options.tests.length; i++) {
+ if(/^test/.test(this.options.tests[i])) {
+ this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
+ }
+ }
+ } else {
+ if (this.options.test) {
+ this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
+ } else {
+ this.tests = [];
+ for(var testcase in testcases) {
+ if(/^test/.test(testcase)) {
+ this.tests.push(
+ new Test.Unit.Testcase(
+ this.options.context ? ' -> ' + this.options.titles[testcase] : testcase,
+ testcases[testcase], testcases["setup"], testcases["teardown"]
+ ));
+ }
+ }
+ }
+ }
+ this.currentTest = 0;
+ this.logger = new Test.Unit.Logger(this.options.testLog);
+ setTimeout(this.runTests.bind(this), 1000);
+ },
+ parseResultsURLQueryParameter: function() {
+ return window.location.search.parseQuery()["resultsURL"];
+ },
+ parseTestsQueryParameter: function(){
+ if (window.location.search.parseQuery()["tests"]){
+ return window.location.search.parseQuery()["tests"].split(',');
+ };
+ },
+ // Returns:
+ // "ERROR" if there was an error,
+ // "FAILURE" if there was a failure, or
+ // "SUCCESS" if there was neither
+ getResult: function() {
+ var hasFailure = false;
+ for(var i=0;i<this.tests.length;i++) {
+ if (this.tests[i].errors > 0) {
+ return "ERROR";
+ }
+ if (this.tests[i].failures > 0) {
+ hasFailure = true;
+ }
+ }
+ if (hasFailure) {
+ return "FAILURE";
+ } else {
+ return "SUCCESS";
+ }
+ },
+ postResults: function() {
+ if (this.options.resultsURL) {
+ new Ajax.Request(this.options.resultsURL,
+ { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
+ }
+ },
+ runTests: function() {
+ var test = this.tests[this.currentTest];
+ if (!test) {
+ // finished!
+ this.postResults();
+ this.logger.summary(this.summary());
+ return;
+ }
+ if(!test.isWaiting) {
+ this.logger.start(test.name);
+ }
+ test.run();
+ if(test.isWaiting) {
+ this.logger.message("Waiting for " + test.timeToWait + "ms");
+ setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
+ } else {
+ this.logger.finish(test.status(), test.summary());
+ this.currentTest++;
+ // tail recursive, hopefully the browser will skip the stackframe
+ this.runTests();
+ }
+ },
+ summary: function() {
+ var assertions = 0;
+ var failures = 0;
+ var errors = 0;
+ var messages = [];
+ for(var i=0;i<this.tests.length;i++) {
+ assertions += this.tests[i].assertions;
+ failures += this.tests[i].failures;
+ errors += this.tests[i].errors;
+ }
+ return (
+ (this.options.context ? this.options.context + ': ': '') +
+ this.tests.length + " tests, " +
+ assertions + " assertions, " +
+ failures + " failures, " +
+ errors + " errors");
+ }
+}
+
+Test.Unit.Assertions = Class.create();
+Test.Unit.Assertions.prototype = {
+ initialize: function() {
+ this.assertions = 0;
+ this.failures = 0;
+ this.errors = 0;
+ this.messages = [];
+ },
+ summary: function() {
+ return (
+ this.assertions + " assertions, " +
+ this.failures + " failures, " +
+ this.errors + " errors" + "\n" +
+ this.messages.join("\n"));
+ },
+ pass: function() {
+ this.assertions++;
+ },
+ fail: function(message) {
+ this.failures++;
+ this.messages.push("Failure: " + message);
+ },
+ info: function(message) {
+ this.messages.push("Info: " + message);
+ },
+ error: function(error) {
+ this.errors++;
+ this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
+ },
+ status: function() {
+ if (this.failures > 0) return 'failed';
+ if (this.errors > 0) return 'error';
+ return 'passed';
+ },
+ assert: function(expression) {
+ var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
+ try { expression ? this.pass() :
+ this.fail(message); }
+ catch(e) { this.error(e); }
+ },
+ assertEqual: function(expected, actual) {
+ var message = arguments[2] || "assertEqual";
+ try { (expected == actual) ? this.pass() :
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
+ '", actual "' + Test.Unit.inspect(actual) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertInspect: function(expected, actual) {
+ var message = arguments[2] || "assertInspect";
+ try { (expected == actual.inspect()) ? this.pass() :
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
+ '", actual "' + Test.Unit.inspect(actual) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertEnumEqual: function(expected, actual) {
+ var message = arguments[2] || "assertEnumEqual";
+ try { $A(expected).length == $A(actual).length &&
+ expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
+ this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) +
+ ', actual ' + Test.Unit.inspect(actual)); }
+ catch(e) { this.error(e); }
+ },
+ assertNotEqual: function(expected, actual) {
+ var message = arguments[2] || "assertNotEqual";
+ try { (expected != actual) ? this.pass() :
+ this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertIdentical: function(expected, actual) {
+ var message = arguments[2] || "assertIdentical";
+ try { (expected === actual) ? this.pass() :
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
+ '", actual "' + Test.Unit.inspect(actual) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertNotIdentical: function(expected, actual) {
+ var message = arguments[2] || "assertNotIdentical";
+ try { !(expected === actual) ? this.pass() :
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
+ '", actual "' + Test.Unit.inspect(actual) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertNull: function(obj) {
+ var message = arguments[1] || 'assertNull'
+ try { (obj==null) ? this.pass() :
+ this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertMatch: function(expected, actual) {
+ var message = arguments[2] || 'assertMatch';
+ var regex = new RegExp(expected);
+ try { (regex.exec(actual)) ? this.pass() :
+ this.fail(message + ' : regex: "' + Test.Unit.inspect(expected) + ' did not match: ' + Test.Unit.inspect(actual) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertHidden: function(element) {
+ var message = arguments[1] || 'assertHidden';
+ this.assertEqual("none", element.style.display, message);
+ },
+ assertNotNull: function(object) {
+ var message = arguments[1] || 'assertNotNull';
+ this.assert(object != null, message);
+ },
+ assertType: function(expected, actual) {
+ var message = arguments[2] || 'assertType';
+ try {
+ (actual.constructor == expected) ? this.pass() :
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
+ '", actual "' + (actual.constructor) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertNotOfType: function(expected, actual) {
+ var message = arguments[2] || 'assertNotOfType';
+ try {
+ (actual.constructor != expected) ? this.pass() :
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
+ '", actual "' + (actual.constructor) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertInstanceOf: function(expected, actual) {
+ var message = arguments[2] || 'assertInstanceOf';
+ try {
+ (actual instanceof expected) ? this.pass() :
+ this.fail(message + ": object was not an instance of the expected type"); }
+ catch(e) { this.error(e); }
+ },
+ assertNotInstanceOf: function(expected, actual) {
+ var message = arguments[2] || 'assertNotInstanceOf';
+ try {
+ !(actual instanceof expected) ? this.pass() :
+ this.fail(message + ": object was an instance of the not expected type"); }
+ catch(e) { this.error(e); }
+ },
+ assertRespondsTo: function(method, obj) {
+ var message = arguments[2] || 'assertRespondsTo';
+ try {
+ (obj[method] && typeof obj[method] == 'function') ? this.pass() :
+ this.fail(message + ": object doesn't respond to [" + method + "]"); }
+ catch(e) { this.error(e); }
+ },
+ assertReturnsTrue: function(method, obj) {
+ var message = arguments[2] || 'assertReturnsTrue';
+ try {
+ var m = obj[method];
+ if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
+ m() ? this.pass() :
+ this.fail(message + ": method returned false"); }
+ catch(e) { this.error(e); }
+ },
+ assertReturnsFalse: function(method, obj) {
+ var message = arguments[2] || 'assertReturnsFalse';
+ try {
+ var m = obj[method];
+ if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
+ !m() ? this.pass() :
+ this.fail(message + ": method returned true"); }
+ catch(e) { this.error(e); }
+ },
+ assertRaise: function(exceptionName, method) {
+ var message = arguments[2] || 'assertRaise';
+ try {
+ method();
+ this.fail(message + ": exception expected but none was raised"); }
+ catch(e) {
+ ((exceptionName == null) || (e.name==exceptionName)) ? this.pass() : this.error(e);
+ }
+ },
+ assertElementsMatch: function() {
+ var expressions = $A(arguments), elements = $A(expressions.shift());
+ if (elements.length != expressions.length) {
+ this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions');
+ return false;
+ }
+ elements.zip(expressions).all(function(pair, index) {
+ var element = $(pair.first()), expression = pair.last();
+ if (element.match(expression)) return true;
+ this.fail('assertElementsMatch: (in index ' + index + ') expected ' + expression.inspect() + ' but got ' + element.inspect());
+ }.bind(this)) && this.pass();
+ },
+ assertElementMatches: function(element, expression) {
+ this.assertElementsMatch([element], expression);
+ },
+ benchmark: function(operation, iterations) {
+ var startAt = new Date();
+ (iterations || 1).times(operation);
+ var timeTaken = ((new Date())-startAt);
+ this.info((arguments[2] || 'Operation') + ' finished ' +
+ iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+ return timeTaken;
+ },
+ _isVisible: function(element) {
+ element = $(element);
+ if(!element.parentNode) return true;
+ this.assertNotNull(element);
+ if(element.style && Element.getStyle(element, 'display') == 'none')
+ return false;
+
+ return this._isVisible(element.parentNode);
+ },
+ assertNotVisible: function(element) {
+ this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
+ },
+ assertVisible: function(element) {
+ this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
+ },
+ benchmark: function(operation, iterations) {
+ var startAt = new Date();
+ (iterations || 1).times(operation);
+ var timeTaken = ((new Date())-startAt);
+ this.info((arguments[2] || 'Operation') + ' finished ' +
+ iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+ return timeTaken;
+ }
+}
+
+Test.Unit.Testcase = Class.create();
+Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
+ initialize: function(name, test, setup, teardown) {
+ Test.Unit.Assertions.prototype.initialize.bind(this)();
+ this.name = name;
+
+ if(typeof test == 'string') {
+ test = test.gsub(/(\.should[^\(]+\()/,'#{0}this,');
+ test = test.gsub(/(\.should[^\(]+)\(this,\)/,'#{1}(this)');
+ this.test = function() {
+ eval('with(this){'+test+'}');
+ }
+ } else {
+ this.test = test || function() {};
+ }
+
+ this.setup = setup || function() {};
+ this.teardown = teardown || function() {};
+ this.isWaiting = false;
+ this.timeToWait = 1000;
+ },
+ wait: function(time, nextPart) {
+ this.isWaiting = true;
+ this.test = nextPart;
+ this.timeToWait = time;
+ },
+ run: function() {
+ try {
+ try {
+ if (!this.isWaiting) this.setup.bind(this)();
+ this.isWaiting = false;
+ this.test.bind(this)();
+ } finally {
+ if(!this.isWaiting) {
+ this.teardown.bind(this)();
+ }
+ }
+ }
+ catch(e) { this.error(e); }
+ }
+});
+
+// *EXPERIMENTAL* BDD-style testing to please non-technical folk
+// This draws many ideas from RSpec http://rspec.rubyforge.org/
+
+Test.setupBDDExtensionMethods = function(){
+ var METHODMAP = {
+ shouldEqual: 'assertEqual',
+ shouldNotEqual: 'assertNotEqual',
+ shouldEqualEnum: 'assertEnumEqual',
+ shouldBeA: 'assertType',
+ shouldNotBeA: 'assertNotOfType',
+ shouldBeAn: 'assertType',
+ shouldNotBeAn: 'assertNotOfType',
+ shouldBeNull: 'assertNull',
+ shouldNotBeNull: 'assertNotNull',
+
+ shouldBe: 'assertReturnsTrue',
+ shouldNotBe: 'assertReturnsFalse',
+ shouldRespondTo: 'assertRespondsTo'
+ };
+ var makeAssertion = function(assertion, args, object) {
+ this[assertion].apply(this,(args || []).concat([object]));
+ }
+
+ Test.BDDMethods = {};
+ $H(METHODMAP).each(function(pair) {
+ Test.BDDMethods[pair.key] = function() {
+ var args = $A(arguments);
+ var scope = args.shift();
+ makeAssertion.apply(scope, [pair.value, args, this]); };
+ });
+
+ [Array.prototype, String.prototype, Number.prototype, Boolean.prototype].each(
+ function(p){ Object.extend(p, Test.BDDMethods) }
+ );
+}
+
+Test.context = function(name, spec, log){
+ Test.setupBDDExtensionMethods();
+
+ var compiledSpec = {};
+ var titles = {};
+ for(specName in spec) {
+ switch(specName){
+ case "setup":
+ case "teardown":
+ compiledSpec[specName] = spec[specName];
+ break;
+ default:
+ var testName = 'test'+specName.gsub(/\s+/,'-').camelize();
+ var body = spec[specName].toString().split('\n').slice(1);
+ if(/^\{/.test(body[0])) body = body.slice(1);
+ body.pop();
+ body = body.map(function(statement){
+ return statement.strip()
+ });
+ compiledSpec[testName] = body.join('\n');
+ titles[testName] = specName;
+ }
+ }
+ new Test.Unit.Runner(compiledSpec, { titles: titles, testLog: log || 'testlog', context: name });
+};
\ No newline at end of file
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/shadowbox-prototype.js
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/shadowbox-prototype.js (rev 0)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/shadowbox-prototype.js 2008-02-11 02:47:55 UTC (rev 9930)
@@ -0,0 +1,178 @@
+/**
+ * An adapter for the Shadowbox media viewer and the Prototype + Scriptaculous
+ * JavaScript framework.
+ *
+ * This file is part of Shadowbox.
+ *
+ * Shadowbox is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Shadowbox 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Shadowbox. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Michael J. I. Jackson <mjijackson(a)gmail.com>
+ * @copyright 2007 Michael J. I. Jackson
+ * @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU LGPL 3.0
+ * @version SVN: $Id: shadowbox-prototype.js 55 2008-01-29 09:42:41Z mjijackson $
+ */
+
+if(typeof Prototype == 'undefined'){
+ throw 'Unable to load Shadowbox, Prototype framework not found.';
+}
+if(typeof Effect == 'undefined'){
+ throw 'Unable to load Shadowbox, Scriptaculous effects framework not found.';
+}
+
+// create the Shadowbox object first
+var Shadowbox = {};
+
+Shadowbox.lib = {
+
+ /**
+ * Gets the value of the style on the given element.
+ *
+ * @param {HTMLElement} el The DOM element
+ * @param {String} style The name of the style (e.g. margin-top)
+ * @return {mixed} The value of the given style
+ * @public
+ */
+ getStyle: function(el, style){
+ return Element.getStyle(el, style);
+ },
+
+ /**
+ * Sets the style on the given element to the given value. May be an
+ * object to specify multiple values.
+ *
+ * @param {HTMLElement} el The DOM element
+ * @param {String/Object} style The name of the style to set if a
+ * string, or an object of name =>
+ * value pairs
+ * @param {String} value The value to set the given style to
+ * @return void
+ * @public
+ */
+ setStyle: function(el, style, value){
+ if(typeof style != 'object'){
+ var temp = {};
+ temp[style] = value;
+ style = temp;
+ }
+ Element.setStyle(el, style);
+ },
+
+ /**
+ * Gets a reference to the given element.
+ *
+ * @param {String/HTMLElement} el The element to fetch
+ * @return {HTMLElement} A reference to the element
+ * @public
+ */
+ get: function(el){
+ return $(el);
+ },
+
+ /**
+ * Removes an element from the DOM.
+ *
+ * @param {HTMLElement} el The element to remove
+ * @return void
+ * @public
+ */
+ remove: function(el){
+ Element.remove(el);
+ },
+
+ /**
+ * Gets the target of the given event. The event object passed will be
+ * the same object that is passed to listeners registered with
+ * addEvent().
+ *
+ * @param {mixed} e The event object
+ * @return {HTMLElement} The event's target element
+ * @public
+ */
+ getTarget: function(e){
+ return Event.element(e);
+ },
+
+ /**
+ * Prevents the event's default behavior. The event object passed will
+ * be the same object that is passed to listeners registered with
+ * addEvent().
+ *
+ * @param {mixed} e The event object
+ * @return void
+ * @public
+ */
+ preventDefault: function(e){
+ Event.stop(e);
+ },
+
+ /**
+ * Adds an event listener to the given element. It is expected that this
+ * function will be passed the event as its first argument.
+ *
+ * @param {HTMLElement} el The DOM element to listen to
+ * @param {String} name The name of the event to register
+ * (i.e. 'click', 'scroll', etc.)
+ * @param {Function} handler The event handler function
+ * @return void
+ * @public
+ */
+ addEvent: function(el, name, handler){
+ Event.observe(el, name, handler);
+ },
+
+ /**
+ * Removes an event listener from the given element.
+ *
+ * @param {HTMLElement} el The DOM element to stop listening to
+ * @param {String} name The name of the event to stop
+ * listening for (i.e. 'click')
+ * @param {Function} handler The event handler function
+ * @return void
+ * @public
+ */
+ removeEvent: function(el, name, handler){
+ Event.stopObserving(el, name, handler);
+ },
+
+ /**
+ * Animates numerous styles of the given element. The second parameter
+ * of this function will be an object of the type that is expected by
+ * YAHOO.util.Anim. See http://developer.yahoo.com/yui/docs/YAHOO.util.Anim.html
+ * for more information.
+ *
+ * @param {HTMLElement} el The DOM element to animate
+ * @param {Object} obj The animation attributes/parameters
+ * @param {Number} duration The duration of the animation
+ * (in seconds)
+ * @param {Function} callback A callback function to call when
+ * the animation completes
+ * @return void
+ * @public
+ */
+ animate: function(el, obj, duration, callback){
+ var o = {};
+ for(var p in obj){
+ for(var p in obj){
+ o[p] = String(obj[p].to);
+ if(p != 'opacity') o[p] += 'px';
+ }
+ }
+ return new Effect.Morph(el, {
+ afterFinish: callback,
+ duration: duration,
+ style: o
+ });
+ }
+
+};
Added: modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/shadowbox.js
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/shadowbox.js (rev 0)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/js/shadowbox.js 2008-02-11 02:47:55 UTC (rev 9930)
@@ -0,0 +1,2453 @@
+/**
+ * A media-viewer script for web pages that allows content to be viewed without
+ * navigating away from the original linking page.
+ *
+ * This file is part of Shadowbox.
+ *
+ * Shadowbox is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Shadowbox 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Shadowbox. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Michael J. I. Jackson <mjijackson(a)gmail.com>
+ * @copyright 2007 Michael J. I. Jackson
+ * @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU LGPL 3.0
+ * @version SVN: $Id: shadowbox.js 63 2008-02-06 14:28:42Z mjijackson $
+ */
+
+if(typeof Shadowbox == 'undefined'){
+ throw 'Unable to load Shadowbox, no base library adapter found.';
+}
+
+/**
+ * The Shadowbox class. Used to display different media on a web page using a
+ * Lightbox-like effect.
+ *
+ * Useful resources:
+ * - http://www.alistapart.com/articles/byebyeembed
+ * - http://www.w3.org/TR/html401/struct/objects.html
+ * - http://www.dyn-web.com/dhtml/iframes/
+ * - http://support.microsoft.com/kb/316992
+ * - http://www.apple.com/quicktime/player/specs.html
+ * - http://www.howtocreate.co.uk/wrongWithIE/?chapter=navigator.plugins
+ *
+ * @class Shadowbox
+ * @author Michael J. I. Jackson <mjijackson(a)gmail.com>
+ * @singleton
+ * @todo Find a way to tell when movies and iframes are loaded
+ * @todo Incorporate slideshow functionality
+ */
+(function(){
+
+ /**
+ * The current version of Shadowbox.
+ *
+ * @property {String} version
+ * @private
+ */
+ var version = '1.0b7';
+
+ /**
+ * Contains the default options for Shadowbox. This object is almost
+ * entirely customizable.
+ *
+ * @property {Object} options
+ * @private
+ */
+ var options = {
+
+ /**
+ * The path to the image to display while loading.
+ *
+ * @var {String} loadingImage
+ */
+ loadingImage: 'images/loading.gif',
+
+ /**
+ * Enable animations.
+ *
+ * @var {Boolean} animate
+ */
+ animate: true,
+
+ /**
+ * Specifies the sequence of the height and width animations. May be
+ * 'wh' (width then height), 'hw' (height then width), or 'sync' (both
+ * at the same time). Of course this will only work if animate is true.
+ *
+ * @var {String} animSequence
+ */
+ animSequence: 'wh',
+
+ /**
+ * The path to flvplayer.swf.
+ *
+ * @var {String} flvPlayer
+ */
+ flvPlayer: 'flvplayer.swf',
+
+ /**
+ * The background color and opacity of the overlay. Note: When viewing
+ * movie files on FF Mac, the default background image will be used
+ * because that browser has problems displaying movies above layers
+ * that aren't 100% opaque.
+ *
+ * @var {String} overlayColor
+ */
+ overlayColor: '#000',
+
+ /**
+ * The background opacity to use for the overlay.
+ *
+ * @var {Number} overlayOpacity
+ */
+ overlayOpacity: 0.85,
+
+ /**
+ * A background image to use for browsers such as FF Mac that don't
+ * support displaying movie content over backgrounds that aren't 100%
+ * opaque.
+ *
+ * @var {String} overlayBgImage
+ */
+ overlayBgImage: 'images/overlay-85.png',
+
+ /**
+ * Automatically play movies.
+ *
+ * @var {Boolean} autoplayMovies
+ */
+ autoplayMovies: true,
+
+ /**
+ * Enable movie controllers on movie players.
+ *
+ * @var {Boolean} showMovieControls
+ */
+ showMovieControls: true,
+
+ /**
+ * The duration of the resizing animations (in seconds).
+ *
+ * @var {Number} resizeDuration
+ */
+ resizeDuration: 0.35,
+
+ /**
+ * The duration of the overlay fade animation (in seconds).
+ *
+ * @var {Number} fadeDuration
+ */
+ fadeDuration: 0.35,
+
+ /**
+ * Show the navigation controls.
+ *
+ * @var {Boolean} displayNav
+ */
+ displayNav: true,
+
+ /**
+ * Enable continuous galleries. When this is true, users will be able
+ * to skip to the first gallery image from the last using next and vice
+ * versa.
+ *
+ * @var {Boolean} continuous
+ */
+ continuous: false,
+
+ /**
+ * Display the gallery counter.
+ *
+ * @var {Boolean} displayCounter
+ */
+ displayCounter: true,
+
+ /**
+ * This option may be either 'default' or 'skip'. The default counter is
+ * a simple '1 of 5' message. The skip counter displays a link for each
+ * piece in the gallery that enables a user to skip directly to any
+ * piece.
+ *
+ * @var {String} counterType
+ */
+ counterType: 'default',
+
+ /**
+ * The amount of padding to maintain around the viewport edge (in
+ * pixels). This only applies when the image is very large and takes up
+ * the entire viewport.
+ *
+ * @var {Number} viewportPadding
+ */
+ viewportPadding: 20,
+
+ /**
+ * How to handle images that are too large for the viewport. 'resize'
+ * will resize the image while preserving aspect ratio and display it at
+ * the smaller resolution. 'drag' will display the image at its native
+ * resolution but it will be draggable within the Shadowbox. 'none' will
+ * display the image at its native resolution but it may be cropped.
+ *
+ * @var {String} handleLgImages
+ */
+ handleLgImages: 'resize',
+
+ /**
+ * The initial height of Shadowbox (in pixels).
+ *
+ * @var {Number} initialHeight
+ */
+ initialHeight: 160,
+
+ /**
+ * The initial width of Shadowbox (in pixels).
+ *
+ * @var {Number} initialWidth
+ */
+ initialWidth: 320,
+
+ /**
+ * Enable keyboard control. Note: If you disable the keys, you may want
+ * to change the visual styles for the navigation elements that suggest
+ * keyboard shortcuts.
+ *
+ * @var {Boolean} enableKeys
+ */
+ enableKeys: true,
+
+ /**
+ * The keys used to control Shadowbox. Note: In order to use these,
+ * enableKeys must be true. Key values or key codes may be used.
+ *
+ * @var {Array}
+ */
+ keysClose: ['c', 'q', 27], // c, q, or esc
+ keysNext: ['n', 39], // n or right arrow
+ keysPrev: ['p', 37], // p or left arrow
+
+ /**
+ * Hook functions that will be fired at various stages in the script
+ * execution. The single parameter passed to the function will be a link
+ * (DOM) element. In the case of onOpen, it will be the link element
+ * that was clicked. In onClose, it will be the link element corresponding
+ * to the last gallery piece that was displayed.
+ *
+ * @var {Function}
+ */
+ onOpen: null,
+ onFinish: null,
+ onClose: null,
+
+ /**
+ * The mode to use when handling unsupported media. May be either
+ * 'remove' or 'link'. If it is 'remove', the unsupported gallery item
+ * will merely be removed from the gallery. If it is the only item in
+ * the gallery, the link will simply be followed. If it is 'link', a
+ * link will be provided to the appropriate plugin page in place of the
+ * gallery element.
+ *
+ * @var {String} handleUnsupported
+ */
+ handleUnsupported: 'link',
+
+ /**
+ * Skips calling Shadowbox.setup() in init(). This means that it must
+ * be called later manually.
+ *
+ * @var {Boolean} skipSetup
+ */
+ skipSetup: false,
+
+ /**
+ * Text messages to use for Shadowbox. These are provided so they may be
+ * translated into different languages.
+ *
+ * @var {Object} text
+ */
+ text: {
+
+ cancel: 'Cancel',
+
+ loading: 'loading',
+
+ close: '<span class="shortcut">C</span>lose',
+
+ next: '<span class="shortcut">N</span>ext',
+
+ prev: '<span class="shortcut">P</span>revious',
+
+ errors: {
+ single: 'You must install the <a href="{0}">{1}</a> browser plugin to view this content.',
+ shared: 'You must install both the <a href="{0}">{1}</a> and <a href="{2}">{3}</a> browser plugins to view this content.',
+ either: 'You must install either the <a href="{0}">{1}</a> or the <a href="{2}">{3}</a> browser plugin to view this content.'
+ }
+
+ },
+
+ /**
+ * An object containing names of plugins and links to their respective
+ * download pages.
+ *
+ * @var {Object} errors
+ */
+ errors: {
+
+ fla: {
+ name: 'Flash',
+ url: 'http://www.adobe.com/products/flashplayer/'
+ },
+
+ qt: {
+ name: 'QuickTime',
+ url: 'http://www.apple.com/quicktime/download/'
+ },
+
+ wmp: {
+ name: 'Windows Media Player',
+ url: 'http://www.microsoft.com/windows/windowsmedia/'
+ },
+
+ f4m: {
+ name: 'Flip4Mac',
+ url: 'http://www.flip4mac.com/wmv_download.htm'
+ }
+
+ },
+
+ /**
+ * The HTML markup to use for Shadowbox. Note: The script depends on
+ * most of these elements being present, so don't modify this variable
+ * unless you know what you're doing.
+ *
+ * @var {Object} skin
+ */
+ skin: {
+
+ main: '<div id="shadowbox_overlay"></div>' +
+ '<div id="shadowbox_container">' +
+ '<div id="shadowbox">' +
+ '<div id="shadowbox_title">' +
+ '<div id="shadowbox_title_inner"></div>' +
+ '</div>' +
+ '<div id="shadowbox_body">' +
+ '<div id="shadowbox_body_inner"></div>' +
+ '<div id="shadowbox_loading"></div>' +
+ '</div>' +
+ '<div id="shadowbox_toolbar">' +
+ '<div id="shadowbox_toolbar_inner"></div>' +
+ '</div>' +
+ '</div>' +
+ '</div>',
+
+ loading: '<img src="{0}" alt="{1}" />' +
+ '<span><a href="javascript:Shadowbox.close();">{2}</a></span>',
+
+ counter: '<div id="shadowbox_counter">{0}</div>',
+
+ close: '<div id="shadowbox_nav_close">' +
+ '<a href="javascript:Shadowbox.close();">{0}</a>' +
+ '</div>',
+
+ next: '<div id="shadowbox_nav_next">' +
+ '<a href="javascript:Shadowbox.next();">{0}</a>' +
+ '</div>',
+
+ prev: '<div id="shadowbox_nav_previous">' +
+ '<a href="javascript:Shadowbox.previous();">{0}</a>' +
+ '</div>'
+
+ },
+
+ /**
+ * An object containing arrays of all supported file extensions. Each
+ * property of this object contains an array. If this object is to be
+ * modified, it must be done before calling init().
+ *
+ * - img: Supported image file extensions
+ * - qt: Movie file extensions supported by QuickTime
+ * - wmp: Movie file extensions supported by Windows Media Player
+ * - qtwmp: Movie file extensions supported by both QuickTime and Windows Media Player
+ * - iframe: File extensions that will be display in an iframe
+ *
+ * @var {Object} ext
+ */
+ ext: {
+ img: ['png', 'jpg', 'jpeg', 'gif', 'bmp'],
+ qt: ['dv', 'mov', 'moov', 'movie', 'mp4'],
+ wmp: ['asf', 'wm', 'wmv'],
+ qtwmp: ['avi', 'mpg', 'mpeg'],
+ iframe: ['asp', 'aspx', 'cgi', 'cfm', 'htm', 'html', 'pl', 'php',
+ 'php3', 'php4', 'php5', 'phtml', 'rb', 'rhtml', 'shtml',
+ 'txt', 'vbs']
+ }
+
+ };
+
+ /**
+ * Stores the default set of options in case a custom set of options is used
+ * on a link-by-link basis so we can restore them later.
+ *
+ * @property {Object} default_options
+ * @private
+ */
+ var default_options = null;
+
+ /**
+ * Shorthand for Shadowbox.lib.
+ *
+ * @property {Object} SL
+ * @private
+ */
+ var SL = Shadowbox.lib;
+
+ /**
+ * An object containing some regular expressions we'll need later. Compiled
+ * up front for speed.
+ *
+ * @property {Object} RE
+ * @private
+ */
+ var RE = {
+ resize: /(img|swf|flv)/, // file types to resize
+ swf: /\.swf\s*$/i, // swf file extension
+ flv: /\.flv\s*$/i, // flv file extension
+ domain: /:\/\/(.*?)[:\/]/, // domain prefix
+ inline: /#(.+)$/, // inline element id
+ rel: /^shadowbox/i, // rel attribute format
+ gallery: /^shadowbox\[(.*?)\]/i, // rel attribute format for gallery link
+ unsupported: /^unsupported-(\w+)/, // unsupported media type
+ param: /\s*([a-z_]*?)\s*=\s*(.+)\s*/, // rel string parameter
+ empty: /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i // elements that don't have children
+ };
+
+ /**
+ * A cache of options for links that have been set up for use with
+ * Shadowbox.
+ *
+ * @property {Array} cache
+ * @private
+ */
+ var cache = [];
+
+ /**
+ * An array of pieces currently being viewed. In the case of non-gallery
+ * pieces, this will only hold one object.
+ *
+ * @property {Array} current_gallery
+ * @private
+ */
+ var current_gallery;
+
+ /**
+ * The array index of the current_gallery that is currently being viewed.
+ *
+ * @property {Number} current
+ * @private
+ */
+ var current;
+
+ /**
+ * Keeps track of the current optimal height of the box. We use this so that
+ * if the user resizes the browser window to get a better view, and we're
+ * currently at a size smaller than the optimal, we can resize easily.
+ *
+ * @see resizeContent()
+ * @property {Number} optimal_height
+ * @private
+ */
+ var optimal_height = options.initialHeight;
+
+ /**
+ * Keeps track of the current optimal width of the box. See optimal_height
+ * explanation (above).
+ *
+ * @property {Number} optimal_width
+ * @private
+ */
+ var optimal_width = options.initialWidth;
+
+ /**
+ * Keeps track of the current height of the box. This is useful in drag
+ * calculations.
+ *
+ * @property {Number} current_height
+ * @private
+ */
+ var current_height = 0;
+
+ /**
+ * Keeps track of the current width of the box. Useful in drag calculations.
+ *
+ * @property {Number} current_width
+ * @private
+ */
+ var current_width = 0;
+
+ /**
+ * Resource used to preload images. It's class-level so that when a new
+ * image is requested, the same resource can be reassigned, cancelling
+ * the original's callback.
+ *
+ * @property {HTMLElement} preloader
+ * @private
+ */
+ var preloader;
+
+ /**
+ * Keeps track of whether or not Shadowbox has been initialized. We never
+ * want to initialize twice.
+ *
+ * @property {Boolean} initialized
+ * @private
+ */
+ var initialized = false;
+
+ /**
+ * Keeps track of whether or not Shadowbox is activated.
+ *
+ * @property {Boolean} activated
+ * @private
+ */
+ var activated = false;
+
+ /**
+ * Keeps track of 4 floating values (x, y, start_x, & start_y) that are used
+ * in the drag calculations.
+ *
+ * @property {Object} drag
+ * @private
+ */
+ var drag;
+
+ /**
+ * Holds the draggable element so we don't have to fetch it every time
+ * the mouse moves.
+ *
+ * @property {HTMLElement} draggable
+ * @private
+ */
+ var draggable;
+
+ /**
+ * Keeps track of whether or not we're currently using the overlay
+ * background image to display the current gallery. We do this because we
+ * use different methods for fading the overlay in and out. The color fill
+ * overlay fades in and out nicely, but the image overlay stutters. By
+ * keeping track of the type of overlay in use, we don't have to check again
+ * what type of overlay we're using when it's time to get rid of it later.
+ *
+ * @property {Boolean} overlay_img_needed
+ * @private
+ */
+ var overlay_img_needed;
+
+ /**
+ * These parameters for simple browser detection. Used in Ext.js.
+ *
+ * @ignore
+ */
+ var ua = navigator.userAgent.toLowerCase();
+ var isStrict = document.compatMode == 'CSS1Compat',
+ isOpera = ua.indexOf("opera") > -1,
+ isIE = ua.indexOf('msie') > -1,
+ isIE7 = ua.indexOf('msie 7') > -1,
+ isBorderBox = isIE && !isStrict,
+ isSafari = (/webkit|khtml/).test(ua),
+ isSafari3 = isSafari && !!(document.evaluate),
+ isGecko = !isSafari && ua.indexOf('gecko') > -1,
+ isWindows = (ua.indexOf('windows') != -1 || ua.indexOf('win32') != -1),
+ isMac = (ua.indexOf('macintosh') != -1 || ua.indexOf('mac os x') != -1),
+ isLinux = (ua.indexOf('linux') != -1);
+
+ /**
+ * Do we need to hack the position to make Shadowbox appear fixed? We could
+ * hack this using CSS, but let's just get over all the hacks and let IE6
+ * users get what they deserve! Down with hacks! Hmm...now that I think
+ * about it, I should just flash all kinds of alerts and annoying popups on
+ * their screens, and then redirect them to some foreign spyware site that
+ * will upload a nasty virus...
+ *
+ * @property {Boolean} absolute_pos
+ * @private
+ */
+ var absolute_pos = isIE && !isIE7;
+
+ /**
+ * Contains plugin support information. Each property of this object is a
+ * boolean indicating whether that plugin is supported.
+ *
+ * - fla: Flash player
+ * - qt: QuickTime player
+ * - wmp: Windows Media player
+ * - f4m: Flip4Mac plugin
+ *
+ * @property {Object} plugins
+ * @private
+ */
+ var plugins = null;
+
+ // detect plugin support
+ if(navigator.plugins && navigator.plugins.length){
+ var detectPlugin = function(plugin_name){
+ var detected = false;
+ for (var i = 0, len = navigator.plugins.length; i < len; ++i){
+ if(navigator.plugins[i].name.indexOf(plugin_name) > -1){
+ detected = true;
+ break;
+ }
+ }
+ return detected;
+ };
+ var f4m = detectPlugin('Flip4Mac');
+ var plugins = {
+ fla: detectPlugin('Shockwave Flash'),
+ qt: detectPlugin('QuickTime'),
+ wmp: !f4m && detectPlugin('Windows Media'), // if it's Flip4Mac, it's not really WMP
+ f4m: f4m
+ };
+ }else{
+ var detectPlugin = function(plugin_name){
+ var detected = false;
+ try {
+ var axo = new ActiveXObject(plugin_name);
+ if(axo){
+ detected = true;
+ }
+ } catch (e) {}
+ return detected;
+ };
+ var plugins = {
+ fla: detectPlugin('ShockwaveFlash.ShockwaveFlash'),
+ qt: detectPlugin('QuickTime.QuickTime'),
+ wmp: detectPlugin('wmplayer.ocx'),
+ f4m: false
+ };
+ }
+
+ /**
+ * Applies all properties of e to o. This function is recursive so that if
+ * any properties of e are themselves objects, those objects will be applied
+ * to objects with the same key that may exist in o.
+ *
+ * @param {Object} o The original object
+ * @param {Object} e The extension object
+ * @return {Object} The original object with all properties
+ * of the extension object applied (deep)
+ * @private
+ */
+ var apply = function(o, e){
+ if(o && e && typeof e == 'object'){
+ for (var p in e) o[p] = e[p];
+ }
+ return o;
+ };
+
+ /**
+ * Gets the height of the viewport in pixels. Note: This function includes
+ * scrollbars in Safari 3.
+ *
+ * @return {Number} The height of the viewport
+ * @public
+ * @static
+ */
+ SL.getViewportHeight = function(){
+ var height = window.innerHeight; // Safari
+ var mode = document.compatMode;
+ if((mode || isIE) && !isOpera){
+ height = isStrict ? document.documentElement.clientHeight : document.body.clientHeight;
+ }
+ return height;
+ };
+
+ /**
+ * Gets the width of the viewport in pixels. Note: This function includes
+ * scrollbars in Safari 3.
+ *
+ * @return {Number} The width of the viewport
+ * @public
+ * @static
+ */
+ SL.getViewportWidth = function(){
+ var width = window.innerWidth; // Safari
+ var mode = document.compatMode;
+ if(mode || isIE){
+ width = isStrict ? document.documentElement.clientWidth : document.body.clientWidth;
+ }
+ return width;
+ };
+
+ /**
+ * Gets the height of the document (body and its margins) in pixels.
+ *
+ * @return {Number} The height of the document
+ * @public
+ * @static
+ */
+ SL.getDocumentHeight = function(){
+ var scrollHeight = isStrict ? document.documentElement.scrollHeight : document.body.scrollHeight;
+ return Math.max(scrollHeight, SL.getViewportHeight());
+ };
+
+ /**
+ * Gets the width of the document (body and its margins) in pixels.
+ *
+ * @return {Number} The width of the document
+ * @public
+ * @static
+ */
+ SL.getDocumentWidth = function(){
+ var scrollWidth = isStrict ? document.documentElement.scrollWidth : document.body.scrollWidth;
+ return Math.max(scrollWidth, SL.getViewportWidth());
+ };
+
+ /**
+ * A utility function used by the fade functions to clear the opacity
+ * style setting of the given element. Required in some cases for IE.
+ * Based on Ext.Element's clearOpacity.
+ *
+ * @param {HTMLElement} el The DOM element
+ * @return void
+ * @private
+ */
+ var clearOpacity = function(el){
+ if(isIE){
+ if(typeof el.style.filter == 'string' && (/alpha/i).test(el.style.filter)){
+ el.style.filter = '';
+ }
+ }else{
+ el.style.opacity = '';
+ el.style['-moz-opacity'] = '';
+ el.style['-khtml-opacity'] = '';
+ }
+ };
+
+ /**
+ * Fades the given element from 0 to the specified opacity.
+ *
+ * @param {HTMLElement} el The DOM element to fade
+ * @param {Number} endingOpacity The final opacity to animate to
+ * @param {Number} duration The duration of the animation
+ * (in seconds)
+ * @param {Function} callback A callback function to call
+ * when the animation completes
+ * @return void
+ * @private
+ */
+ var fadeIn = function(el, endingOpacity, duration, callback){
+ if(options.animate){
+ SL.setStyle(el, 'opacity', 0);
+ el.style.visibility = 'visible';
+ SL.animate(el, {
+ opacity: { to: endingOpacity }
+ }, duration, function(){
+ if(endingOpacity == 1) clearOpacity(el);
+ if(typeof callback == 'function') callback();
+ });
+ }else{
+ if(endingOpacity == 1){
+ clearOpacity(el);
+ }else{
+ SL.setStyle(el, 'opacity', endingOpacity);
+ }
+ el.style.visibility = 'visible';
+ if(typeof callback == 'function') callback();
+ }
+ };
+
+ /**
+ * Fades the given element from its current opacity to 0.
+ *
+ * @param {HTMLElement} el The DOM element to fade
+ * @param {Number} duration The duration of the fade animation
+ * @param {Function} callback A callback function to call when
+ * the animation completes
+ * @return void
+ * @private
+ */
+ var fadeOut = function(el, duration, callback){
+ var cb = function(){
+ el.style.visibility = 'hidden';
+ clearOpacity(el);
+ if(typeof callback == 'function') callback();
+ };
+ if(options.animate){
+ SL.animate(el, {
+ opacity: { to: 0 }
+ }, duration, cb);
+ }else{
+ cb();
+ }
+ };
+
+ /**
+ * Appends an HTML fragment to the given element.
+ *
+ * @param {String/HTMLElement} el The element to append to
+ * @param {String} html The HTML fragment to use
+ * @return {HTMLElement} The newly appended element
+ * @private
+ */
+ var appendHTML = function(el, html){
+ el = SL.get(el);
+ if(el.insertAdjacentHTML){
+ el.insertAdjacentHTML('BeforeEnd', html);
+ return el.lastChild;
+ }
+ if(el.lastChild){
+ var range = el.ownerDocument.createRange();
+ range.setStartAfter(el.lastChild);
+ var frag = range.createContextualFragment(html);
+ el.appendChild(frag);
+ return el.lastChild;
+ }else{
+ el.innerHTML = html;
+ return el.lastChild;
+ }
+ };
+
+ /**
+ * Overwrites the HTML of the given element.
+ *
+ * @param {String/HTMLElement} el The element to overwrite
+ * @param {String} html The new HTML to use
+ * @return {HTMLElement} The new firstChild element
+ * @private
+ */
+ var overwriteHTML = function(el, html){
+ el = SL.get(el);
+ el.innerHTML = html;
+ return el.firstChild;
+ };
+
+ /**
+ * Gets either the offsetHeight or the height of the given element plus
+ * padding and borders (when offsetHeight is not available). Based on
+ * Ext.Element's getComputedHeight.
+ *
+ * @return {Number} The computed height of the element
+ * @private
+ */
+ var getComputedHeight = function(el){
+ var h = Math.max(el.offsetHeight, el.clientHeight);
+ if(!h){
+ h = parseInt(SL.getStyle(el, 'height'), 10) || 0;
+ if(!isBorderBox){
+ h += parseInt(SL.getStyle(el, 'padding-top'), 10)
+ + parseInt(SL.getStyle(el, 'padding-bottom'), 10)
+ + parseInt(SL.getStyle(el, 'border-top-width'), 10)
+ + parseInt(SL.getStyle(el, 'border-bottom-width'), 10);
+ }
+ }
+ return h;
+ };
+
+ /**
+ * Gets either the offsetWidth or the width of the given element plus
+ * padding and borders (when offsetWidth is not available). Based on
+ * Ext.Element's getComputedWidth.
+ *
+ * @return {Number} The computed width of the element
+ * @private
+ */
+ var getComputedWidth = function(el){
+ var w = Math.max(el.offsetWidth, el.clientWidth);
+ if(!w){
+ w = parseInt(SL.getStyle(el, 'width'), 10) || 0;
+ if(!isBorderBox){
+ w += parseInt(SL.getStyle(el, 'padding-left'), 10)
+ + parseInt(SL.getStyle(el, 'padding-right'), 10)
+ + parseInt(SL.getStyle(el, 'border-left-width'), 10)
+ + parseInt(SL.getStyle(el, 'border-right-width'), 10);
+ }
+ }
+ return w;
+ };
+
+ /**
+ * Determines the player needed to display the file at the given URL. If
+ * the file type is not supported, the return value will be 'unsupported-*'
+ * where * will be the player abbreviation.
+ *
+ * @param {String} url The url of the file
+ * @return {String} The name of the player to use
+ * @private
+ */
+ var getPlayerType = function(url){
+ if(RE.img.test(url)){
+ return 'img';
+ }
+ var this_domain = (domain_match = url.match(RE.domain))
+ ? (document.domain == domain_match[1])
+ : false;
+ if(url.indexOf('#') > -1 && this_domain) return 'html';
+ var q_index = url.indexOf('?');
+ if(q_index > -1){
+ url = url.substring(0, q_index);
+ }
+ if(RE.swf.test(url)) return (plugins.fla) ? 'swf' : 'unsupported-swf';
+ if(RE.flv.test(url)) return (plugins.fla) ? 'flv' : 'unsupported-flv';
+ if(RE.qt.test(url)) return (plugins.qt) ? 'qt' : 'unsupported-qt';
+ if(RE.wmp.test(url)){
+ if(plugins.wmp){
+ return 'wmp';
+ }else if(plugins.f4m){
+ return 'qt';
+ }else{
+ if(isMac) return (plugins.qt ? 'unsupported-f4m' : 'unsupported-qtf4m');
+ return 'unsupported-wmp';
+ }
+ }else if(RE.qtwmp.test(url)){
+ if(plugins.qt){
+ return 'qt';
+ }else if(plugins.wmp){
+ return 'wmp';
+ }else{
+ return (isMac ? 'unsupported-qt' : 'unsupported-qtwmp');
+ }
+ }else if(!this_domain || RE.iframe.test(url)){
+ return 'iframe';
+ }
+ return 'unsupported';
+ };
+
+ /**
+ * Gets an array of information regarding the gallery for the given link
+ * element. The first element of this array will itself be an array of link
+ * objects that share the same gallery as the given link. The second element
+ * of the returned array will be the index in the first array of the given
+ * link element. This represents the starting point of the gallery. Note: We
+ * create copies of objects in the cache (using apply()) so that we don't
+ * permanently modify them later in setupGallery().
+ *
+ * @param {HTMLElement} link The link that was clicked
+ * @return {Array} The gallery information as detailed above
+ * @private
+ */
+ var getGallery = function(link){
+ var key = link.shadowboxCacheKey;
+ var name = cache[key].gallery;
+ if(!name){
+ return [[apply({}, cache[key])], 0]; // single item, no gallery
+ }else{
+ var gallery = [], index;
+ for(var i = 0, len = cache.length; i < len; ++i){
+ if(key == i){
+ index = gallery.length; // key element found
+ gallery[gallery.length] = apply({}, cache[i]);
+ }else if(cache[i].gallery && cache[i].gallery == name){
+ gallery[gallery.length] = apply({}, cache[i]);
+ }
+ }
+ if(index == null) throw 'No Shadowbox cache item with index ' + key;
+ return [gallery, index];
+ }
+ };
+
+ /**
+ * Sets up the current gallery and checks to see if any of the gallery
+ * pieces are not supported by the user's browser. If there are, they will
+ * be handled according to the handleUnsupported option.
+ *
+ * @param {HTMLElement} link The link to set up the gallery for
+ * @return void
+ * @private
+ */
+ var setupGallery = function(link){
+ // update current & current_gallery
+ var gallery_info = getGallery(link);
+ current_gallery = gallery_info[0];
+ current = gallery_info[1];
+
+ // are any media in the current gallery supported?
+ var match;
+ for(var i = 0; i < current_gallery.length; ++i){
+ if(match = RE.unsupported.exec(current_gallery[i].type)){ // handle unsupported elements
+ if(options.handleUnsupported == 'link'){
+ // generate a link to the appropriate plugin download page(s)
+ current_gallery[i].type = 'html';
+ var m;
+ switch(match[1]){
+ case 'qtwmp':
+ m = String.format(
+ options.text.errors.either,
+ options.errors.qt.url,
+ options.errors.qt.name,
+ options.errors.wmp.url,
+ options.errors.wmp.name);
+ break;
+ case 'qtf4m':
+ m = String.format(
+ options.text.errors.shared,
+ options.errors.qt.url,
+ options.errors.qt.name,
+ options.errors.f4m.url,
+ options.errors.f4m.name);
+ break;
+ default:
+ if(match[1] == 'swf' || match[1] == 'flv'){
+ match[1] = 'fla';
+ }
+ m = String.format(
+ options.text.errors.single,
+ options.errors[match[1]].url,
+ options.errors[match[1]].name);
+ }
+ current_gallery[i] = apply(current_gallery[i], {
+ height: options.initialHeight,
+ width: options.initialWidth,
+ html: Shadowbox.createHTML({
+ tag: 'div',
+ cls: 'shadowbox_message',
+ html: m
+ })
+ });
+ }else{
+ // remove the element from the gallery
+ current_gallery.splice(i, 1);
+ if(i < current) --current;
+ --i;
+ }
+ }else if(current_gallery[i].type == 'html'){ // handle inline elements
+ var match = RE.inline.exec(current_gallery[i].href);
+ if(match){
+ var el;
+ if(el = SL.get(match[1])){
+ current_gallery[i].html = el.innerHTML;
+ }else{
+ throw 'No element found with id ' + match[1];
+ }
+ }else{
+ throw 'No element id found for inline content';
+ }
+ }
+ }
+ };
+
+ /**
+ * Handles all clicks on links that have been set up to work with Shadowbox.
+ * Determines if the type of medium is supported. If so, stops the browser
+ * from navigating away and opens Shadowbox.
+ *
+ * @param {Event} ev The click event object
+ * @return void
+ * @private
+ */
+ var handleClick = function(ev){
+ if(activated) return; // already open
+ activated = true;
+
+ // get link (anchor) element
+ var link;
+ if(typeof this.tagName == 'string' && this.tagName.toUpperCase() == 'A'){
+ link = this; // jQuery, Prototype, YUI
+ }else{
+ link = SL.getTarget(ev); // Ext
+ while(link.tagName.toUpperCase() != 'A' && link.parentNode){
+ link = link.parentNode;
+ }
+ }
+
+ // setup current gallery
+ setupGallery(link);
+
+ // if so, don't follow the link and open Shadowbox
+ if(current_gallery.length){
+ SL.preventDefault(ev);
+ openContent(link);
+ }
+ };
+
+ /**
+ * Hides the title bar and toolbar and populates them with the proper
+ * content.
+ *
+ * @return void
+ * @private
+ */
+ var buildBars = function(){
+ var link = current_gallery[current];
+ if(!link) return; // nothing to build
+
+ // build the title
+ var title_i = SL.get('shadowbox_title_inner');
+ title_i.innerHTML = (link.title) ? link.title : '';
+ // empty the toolbar
+ var tool_i = SL.get('shadowbox_toolbar_inner');
+ tool_i.innerHTML = '';
+
+ // build the nav
+ if(options.displayNav){
+ tool_i.innerHTML = String.format(options.skin.close, options.text.close);
+ if(current_gallery.length > 1){
+ if(options.continuous){
+ // show both
+ appendHTML(tool_i, String.format(options.skin.next, options.text.next));
+ appendHTML(tool_i, String.format(options.skin.prev, options.text.prev));
+ }else{
+ // not last in the gallery, show the next link
+ if((current_gallery.length - 1) > current){
+ appendHTML(tool_i, String.format(options.skin.next, options.text.next));
+ }
+ // not first in the gallery, show the previous link
+ if(current > 0){
+ appendHTML(tool_i, String.format(options.skin.prev, options.text.prev));
+ }
+ }
+ }
+ }
+
+ // build the counter
+ if(current_gallery.length > 1 && options.displayCounter){
+ // append the counter div
+ var counter = '';
+ if(options.counterType == 'skip'){
+ for(var i = 0, len = current_gallery.length; i < len; ++i){
+ counter += '<a href="javascript:Shadowbox.change(' + i + ');"';
+ if(i == current){
+ counter += ' class="shadowbox_counter_current"';
+ }
+ counter += '>' + (i + 1) + '</a>';
+ }
+ }else{
+ counter = (current + 1) + ' of ' + current_gallery.length;
+ }
+ appendHTML(tool_i, String.format(options.skin.counter, counter));
+ }
+ };
+
+ /**
+ * Hides the title and tool bars.
+ *
+ * @param {Function} callback A function to call on finish
+ * @return void
+ * @private
+ */
+ var hideBars = function(callback){
+ var title_m = getComputedHeight(SL.get('shadowbox_title'));
+ var tool_m = 0 - getComputedHeight(SL.get('shadowbox_toolbar'));
+ var title_i = SL.get('shadowbox_title_inner');
+ var tool_i = SL.get('shadowbox_toolbar_inner');
+
+ if(options.animate && callback){
+ // animate the transition
+ SL.animate(title_i, {
+ marginTop: { to: title_m }
+ }, 0.2);
+ SL.animate(tool_i, {
+ marginTop: { to: tool_m }
+ }, 0.2, callback);
+ }else{
+ SL.setStyle(title_i, 'marginTop', title_m + 'px');
+ SL.setStyle(tool_i, 'marginTop', tool_m + 'px');
+ }
+ };
+
+ /**
+ * Shows the title and tool bars.
+ *
+ * @param {Function} callback A callback function to execute after
+ * the animation completes
+ * @return void
+ * @private
+ */
+ var showBars = function(callback){
+ var title_i = SL.get('shadowbox_title_inner');
+ if(options.animate){
+ if(title_i.innerHTML != ''){
+ SL.animate(title_i, { marginTop: { to: 0 } }, 0.35);
+ }
+ SL.animate(SL.get('shadowbox_toolbar_inner'), {
+ marginTop: { to: 0 }
+ }, 0.35, callback);
+ }else{
+ if(title_i.innerHTML != ''){
+ SL.setStyle(title_i, 'margin-top', '0px');
+ }
+ SL.setStyle(SL.get('shadowbox_toolbar_inner'), 'margin-top', '0px');
+ callback();
+ }
+ };
+
+ /**
+ * Resets the class drag variable.
+ *
+ * @return void
+ * @private
+ */
+ var resetDrag = function(){
+ drag = {
+ x: 0,
+ y: 0,
+ start_x: null,
+ start_y: null
+ };
+ };
+
+ /**
+ * Toggles the drag function on and off.
+ *
+ * @param {Boolean} on True to toggle on, false to toggle off
+ * @return void
+ * @private
+ */
+ var toggleDrag = function(on){
+ if(on){
+ resetDrag();
+ // add drag layer to prevent browser dragging of actual image
+ var styles = [
+ 'position:absolute',
+ 'cursor:' + (isGecko ? '-moz-grab' : 'move')
+ ];
+ // make drag layer transparent
+ styles.push(isIE ? 'background-color:#fff;filter:alpha(opacity=0)' : 'background-color:transparent');
+ appendHTML('shadowbox_body_inner', '<div id="shadowbox_drag_layer" style="' + styles.join(';') + '"></div>');
+ SL.addEvent(SL.get('shadowbox_drag_layer'), 'mousedown', listenDrag);
+ }else{
+ var d = SL.get('shadowbox_drag_layer');
+ if(d){
+ SL.removeEvent(d, 'mousedown', listenDrag);
+ SL.remove(d);
+ }
+ }
+ };
+
+ /**
+ * Sets up a drag listener on the document. Called when the mouse button is
+ * pressed (mousedown).
+ *
+ * @param {mixed} ev The mousedown event
+ * @return void
+ * @private
+ */
+ var listenDrag = function(ev){
+ drag.start_x = ev.clientX;
+ drag.start_y = ev.clientY;
+ draggable = SL.get('shadowbox_content');
+ SL.addEvent(document, 'mousemove', positionDrag);
+ SL.addEvent(document, 'mouseup', unlistenDrag);
+ if(isGecko) SL.setStyle(SL.get('shadowbox_drag_layer'), 'cursor', '-moz-grabbing');
+ };
+
+ /**
+ * Removes the drag listener. Called when the mouse button is released
+ * (mouseup).
+ *
+ * @return void
+ * @private
+ */
+ var unlistenDrag = function(){
+ SL.removeEvent(document, 'mousemove', positionDrag);
+ SL.removeEvent(document, 'mouseup', unlistenDrag); // clean up
+ if(isGecko) SL.setStyle(SL.get('shadowbox_drag_layer'), 'cursor', '-moz-grab');
+ };
+
+ /**
+ * Positions an oversized image on drag.
+ *
+ * @param {mixed} ev The drag event
+ * @return void
+ * @private
+ */
+ var positionDrag = function(ev){
+ var move_y = ev.clientY - drag.start_y;
+ drag.start_y = drag.start_y + move_y;
+ drag.y = Math.max(Math.min(0, drag.y + move_y), current_height - optimal_height); // y boundaries
+ SL.setStyle(draggable, 'top', drag.y + 'px');
+ var move_x = ev.clientX - drag.start_x;
+ drag.start_x = drag.start_x + move_x;
+ drag.x = Math.max(Math.min(0, drag.x + move_x), current_width - optimal_width); // x boundaries
+ SL.setStyle(draggable, 'left', drag.x + 'px');
+ };
+
+ /**
+ * Opens the content linked to by the given link element. By this point,
+ * setupGallery() should have been called either by the click handler or
+ * by Shadowbox.open() directly.
+ *
+ * @param {HTMLElement} link The link to open
+ * @return void
+ * @private
+ */
+ var openContent = function(link){
+ // revert to default options
+ if(default_options){
+ options = default_options;
+ default_options = null; // erase for next time
+ }
+ // apply custom options
+ if(current_gallery[current].options){
+ default_options = apply({}, options); // store default options
+ options = apply(options, current_gallery[current].options);
+ }
+ // fire onOpen hook
+ if(options.onOpen && typeof options.onOpen == 'function'){
+ options.onOpen(link);
+ }
+
+ // display:block here helps with correct dimension calculations
+ SL.setStyle(SL.get('shadowbox'), 'display', 'block');
+
+ toggleTroubleElements(false);
+ var dims = getDimensions(options.initialHeight, options.initialWidth);
+ adjustHeight(dims.height, dims.top);
+ adjustWidth(dims.width);
+ hideBars(false);
+
+ // show the overlay and load the content
+ toggleOverlay(function(){
+ SL.setStyle(SL.get('shadowbox'), 'visibility', 'visible');
+ showLoading();
+ loadContent();
+ });
+ };
+
+ /**
+ * Removes old content and sets the new content of the Shadowbox.
+ *
+ * @param {Object} obj The content to set (appropriate to pass
+ * directly to Shadowbox.createHTML())
+ * @return {HTMLElement} The newly appended element (or null if
+ * none is provided)
+ * @private
+ */
+ var setContent = function(obj){
+ var id = 'shadowbox_content';
+ var content = SL.get(id);
+ if(content){
+ // remove old content first
+ switch(content.tagName.toUpperCase()){
+ case 'OBJECT':
+ // if we're in a gallery (i.e. changing and there's a new
+ // object) we want the LAST link object
+ var link = current_gallery[(obj ? current - 1 : current)];
+ if(link.type == 'wmp' && isIE){
+ try{
+ shadowbox_content.controls.stop(); // stop the movie
+ shadowbox_content.URL = 'non-existent.wmv'; // force player refresh
+ window.shadowbox_content = function(){}; // remove from window
+ }catch(e){}
+ }else if(link.type == 'qt' && isSafari){
+ try{
+ document.shadowbox_content.Stop(); // stop QT movie
+ }catch(e){}
+ // stop QT audio stream for movies that have not yet loaded
+ content.innerHTML = '';
+ // console.log(document.shadowbox_content);
+ }
+ setTimeout(function(){ // using setTimeout prevents browser crashes with WMP
+ SL.remove(content);
+ }, 10);
+ break;
+ case 'IFRAME':
+ SL.remove(content);
+ if(isGecko) delete window.frames[id]; // needed for Firefox
+ break;
+ default:
+ SL.remove(content);
+ }
+ }
+ if(obj){
+ if(!obj.id) obj.id = id;
+ return appendHTML('shadowbox_body_inner', Shadowbox.createHTML(obj));
+ }
+ return null;
+ };
+
+ /**
+ * Loads the Shadowbox with the current piece.
+ *
+ * @return void
+ * @private
+ */
+ var loadContent = function(){
+ var link = current_gallery[current];
+ if(!link) return; // invalid
+
+ buildBars();
+
+ switch(link.type){
+ case 'img':
+ // preload the image
+ preloader = new Image();
+ preloader.onload = function(){
+ // images default to image height and width
+ var h = link.height ? parseInt(link.height, 10) : preloader.height;
+ var w = link.width ? parseInt(link.width, 10) : preloader.width;
+ resizeContent(preloader.height, preloader.width, function(dims){
+ showBars(function(){
+ setContent({
+ tag: 'img',
+ height: dims.i_height,
+ width: dims.i_width,
+ src: link.href,
+ style: 'position:absolute'
+ });
+ if(dims.enableDrag && options.handleLgImages == 'drag'){
+ // listen for drag
+ toggleDrag(true);
+ SL.setStyle(SL.get('shadowbox_drag_layer'), {
+ height: dims.i_height + 'px',
+ width: dims.i_width + 'px'
+ });
+ }
+ finishContent();
+ });
+ });
+
+ preloader.onload = function(){}; // clear onload for IE
+ };
+ preloader.src = link.href;
+ break;
+
+ case 'swf':
+ case 'flv':
+ case 'qt':
+ case 'wmp':
+ var markup = Shadowbox.movieMarkup(link);
+ resizeContent(markup.height, markup.width, function(){
+ showBars(function(){
+ setContent(markup);
+ finishContent();
+ });
+ });
+ break;
+
+ case 'iframe':
+ // iframes default to full viewport height and width
+ var h = link.height ? parseInt(link.height, 10) : SL.getViewportHeight();
+ var w = link.width ? parseInt(link.width, 10) : SL.getViewportWidth();
+ var content = {
+ tag: 'iframe',
+ name: 'shadowbox_content',
+ height: '100%',
+ width: '100%',
+ frameborder: '0',
+ marginwidth: '0',
+ marginheight: '0',
+ scrolling: 'auto'
+ };
+
+ resizeContent(h, w, function(dims){
+ showBars(function(){
+ setContent(content);
+ var win = (isIE)
+ ? SL.get('shadowbox_content').contentWindow
+ : window.frames['shadowbox_content'];
+ win.location = link.href;
+ finishContent();
+ });
+ });
+ break;
+
+ case 'html':
+ // HTML content defaults to full viewport height and width
+ var h = link.height ? parseInt(link.height, 10) : SL.getViewportHeight();
+ var w = link.width ? parseInt(link.width, 10) : SL.getViewportWidth();
+ var content = {
+ tag: 'div',
+ cls: 'html', /* give special class to make scrollable */
+ html: link.html
+ };
+ resizeContent(h, w, function(){
+ showBars(function(){
+ setContent(content);
+ finishContent();
+ });
+ });
+ break;
+
+ case 'unsupported':
+ // should never happen because links to unsupported media are
+ // removed or taken care of with an error message in setupGallery()
+ throw 'Content type cannot be determined for ' + link.href;
+ break;
+ }
+
+ // preload neighboring images
+ if(current_gallery.length > 0){
+ var next = current_gallery[current + 1];
+ if(!next){
+ next = current_gallery[0];
+ }
+ if(next.type == 'img'){
+ var preload_next = new Image();
+ preload_next.src = next.href;
+ }
+
+ var prev = current_gallery[current - 1];
+ if(!prev){
+ prev = current_gallery[current_gallery.length - 1];
+ }
+ if(prev.type == 'img'){
+ var preload_prev = new Image();
+ preload_prev.src = prev.href;
+ }
+ }
+ };
+
+ /**
+ * This function is used as the callback after the Shadowbox has been
+ * positioned, resized, and loaded with content.
+ *
+ * @return void
+ * @private
+ */
+ var finishContent = function(){
+ var link = current_gallery[current];
+ if(!link) return; // invalid
+ hideLoading(function(){
+ listenKeyboard(true);
+ // fire onFinish hook
+ if(options.onFinish && typeof options.onFinish == 'function'){
+ options.onFinish(link.el);
+ }
+ });
+ };
+
+ /**
+ * Resizes and positions the content box using the given height and width.
+ * If the callback parameter is missing, the transition will not be
+ * animated. If the callback parameter is present, it will be passed the
+ * new calculated dimensions object as its first parameter. Note: the height
+ * and width here should represent the optimal height and width of the box.
+ *
+ * @param {Function} callback A callback function to use when the
+ * resize completes
+ * @return void
+ * @private
+ */
+ var resizeContent = function(height, width, callback){
+ // update optimal height and width
+ optimal_height = height;
+ optimal_width = width;
+ var resizable = RE.resize.test(current_gallery[current].type);
+ var dims = getDimensions(optimal_height, optimal_width, resizable);
+ if(callback){
+ var cb = function(){ callback(dims); };
+ switch(options.animSequence){
+ case 'hw':
+ adjustHeight(dims.height, dims.top, true, function(){
+ adjustWidth(dims.width, true, cb);
+ });
+ break;
+ case 'wh':
+ adjustWidth(dims.width, true, function(){
+ adjustHeight(dims.height, dims.top, true, cb);
+ });
+ break;
+ default: // sync
+ adjustWidth(dims.width, true);
+ adjustHeight(dims.height, dims.top, true, cb);
+ }
+ }else{ // window resize
+ adjustWidth(dims.width, false);
+ adjustHeight(dims.height, dims.top, false);
+ // resize content images & flash in 'resize' mode
+ if(options.handleLgImages == 'resize' && resizable){
+ var content = SL.get('shadowbox_content');
+ if(content){ // may be animating, not present
+ content.height = dims.i_height;
+ content.width = dims.i_width;
+ }
+ }
+ }
+ };
+
+ /**
+ * Calculates the dimensions for Shadowbox, taking into account the borders,
+ * margins, and surrounding elements of the shadowbox_body. If the image
+ * is still to large for Shadowbox, and options.handleLgImages is 'resize',
+ * the resized dimensions will be returned (preserving the original aspect
+ * ratio). Otherwise, the originally calculated dimensions will be returned.
+ * The returned object will have the following properties:
+ *
+ * - height: The height to use for shadowbox_body_inner
+ * - width: The width to use for shadowbox
+ * - i_height: The height to use for resizable content
+ * - i_width: The width to use for resizable content
+ * - top: The top to use for shadowbox
+ * - enableDrag: True if dragging should be enabled (image is oversized)
+ *
+ * @param {Number} o_height The optimal height
+ * @param {Number} o_width The optimal width
+ * @param {Boolean} resizable True if the content is able to be
+ * resized. Defaults to false.
+ * @return {Object} The resize dimensions (see above)
+ * @private
+ */
+ var getDimensions = function(o_height, o_width, resizable){
+ if(typeof resizable == 'undefined') resizable = false;
+
+ var height = o_height = parseInt(o_height);
+ var width = o_width = parseInt(o_width);
+ var shadowbox_b = SL.get('shadowbox_body');
+
+ // calculate the max height
+ var view_height = SL.getViewportHeight();
+ var extra_height = parseInt(SL.getStyle(shadowbox_b, 'border-top-width'), 10)
+ + parseInt(SL.getStyle(shadowbox_b, 'border-bottom-width'), 10)
+ + parseInt(SL.getStyle(shadowbox_b, 'margin-top'), 10)
+ + parseInt(SL.getStyle(shadowbox_b, 'margin-bottom'), 10)
+ + getComputedHeight(SL.get('shadowbox_title'))
+ + getComputedHeight(SL.get('shadowbox_toolbar'))
+ + (2 * options.viewportPadding);
+ if((height + extra_height) >= view_height){
+ height = view_height - extra_height;
+ }
+
+ // calculate the max width
+ var view_width = SL.getViewportWidth();
+ var extra_body_width = parseInt(SL.getStyle(shadowbox_b, 'border-left-width'), 10)
+ + parseInt(SL.getStyle(shadowbox_b, 'border-right-width'), 10)
+ + parseInt(SL.getStyle(shadowbox_b, 'margin-left'), 10)
+ + parseInt(SL.getStyle(shadowbox_b, 'margin-right'), 10);
+ var extra_width = extra_body_width + (2 * options.viewportPadding);
+ if((width + extra_width) >= view_width){
+ width = view_width - extra_width;
+ }
+
+ // handle oversized images & flash
+ var enableDrag = false;
+ var i_height = o_height;
+ var i_width = o_width;
+ var handle = options.handleLgImages;
+ if(resizable && (handle == 'resize' || handle == 'drag')){
+ var change_h = (o_height - height) / o_height;
+ var change_w = (o_width - width) / o_width;
+ if(handle == 'resize'){
+ if(change_h > change_w){
+ width = Math.round((o_width / o_height) * height);
+ }else if(change_w > change_h){
+ height = Math.round((o_height / o_width) * width);
+ }
+ // adjust image height or width accordingly
+ i_width = width;
+ i_height = height;
+ }else{
+ // drag on oversized images only
+ var link = current_gallery[current];
+ if(link) enableDrag = link.type == 'img' && (change_h > 0 || change_w > 0);
+ }
+ }
+
+ return {
+ height: height,
+ width: width + extra_body_width,
+ i_height: i_height,
+ i_width: i_width,
+ top: ((view_height - (height + extra_height)) / 2) + options.viewportPadding,
+ enableDrag: enableDrag
+ };
+ };
+
+ /**
+ * Centers Shadowbox vertically in the viewport. Needs to be called on
+ * scroll in IE6 because it does not support fixed positioning.
+ *
+ * @return void
+ * @private
+ */
+ var centerVertically = function(){
+ var shadowbox = SL.get('shadowbox');
+ var scroll = document.documentElement.scrollTop;
+ var s_top = scroll + Math.round((SL.getViewportHeight() - (shadowbox.offsetHeight || 0)) / 2);
+ SL.setStyle(shadowbox, 'top', s_top + 'px');
+ };
+
+ /**
+ * Adjusts the height of shadowbox_body_inner and centers Shadowbox
+ * vertically in the viewport.
+ *
+ * @param {Number} height The height of shadowbox_body_inner
+ * @param {Number} top The top of the Shadowbox
+ * @param {Boolean} animate True to animate the transition
+ * @param {Function} callback A callback to use when the animation completes
+ * @return void
+ * @private
+ */
+ var adjustHeight = function(height, top, animate, callback){
+ height = parseInt(height);
+
+ // update current_height
+ current_height = height;
+
+ // adjust the height
+ var sbi = SL.get('shadowbox_body_inner');
+ if(animate && options.animate){
+ SL.animate(sbi, {
+ height: { to: height }
+ }, options.resizeDuration, callback);
+ }else{
+ SL.setStyle(sbi, 'height', height + 'px');
+ if(typeof callback == 'function') callback();
+ }
+
+ // manually adjust the top because we're using fixed positioning in IE6
+ if(absolute_pos){
+ // listen for scroll so we can adjust
+ centerVertically();
+ SL.addEvent(window, 'scroll', centerVertically);
+
+ // add scroll to top
+ top += document.documentElement.scrollTop;
+ }
+
+ // adjust the top
+ var shadowbox = SL.get('shadowbox');
+ if(animate && options.animate){
+ SL.animate(shadowbox, {
+ top: { to: top }
+ }, options.resizeDuration);
+ }else{
+ SL.setStyle(shadowbox, 'top', top + 'px');
+ }
+ };
+
+ /**
+ * Adjusts the width of shadowbox.
+ *
+ * @param {Number} width The width to use
+ * @param {Boolean} animate True to animate the transition
+ * @param {Function} callback A callback to use when the animation completes
+ * @return void
+ * @private
+ */
+ var adjustWidth = function(width, animate, callback){
+ width = parseInt(width);
+
+ // update current_width
+ current_width = width;
+
+ var shadowbox = SL.get('shadowbox');
+ if(animate && options.animate){
+ SL.animate(shadowbox, {
+ width: { to: width }
+ }, options.resizeDuration, callback);
+ }else{
+ SL.setStyle(shadowbox, 'width', width + 'px');
+ if(typeof callback == 'function') callback();
+ }
+ };
+
+ /**
+ * Sets up a listener on the document for keystrokes.
+ *
+ * @param {Boolean} on True to enable the listner, false to turn
+ * it off
+ * @return void
+ * @private
+ */
+ var listenKeyboard = function(on){
+ if(!options.enableKeys) return;
+ if(on){
+ document.onkeydown = handleKey;
+ }else{
+ document.onkeydown = '';
+ }
+ };
+
+ /**
+ * Asserts the given key or code is present in the array of valid keys.
+ *
+ * @param {Array} valid An array of valid keys and codes
+ * @param {String} key The character that was pressed
+ * @param {Number} code The key code that was pressed
+ * @return {Boolean} True if the key is valid
+ * @private
+ */
+ var assertKey = function(valid, key, code){
+ return (valid.indexOf(key) != -1 || valid.indexOf(code) != -1);
+ };
+
+ /**
+ * A listener function that will act on a key pressed.
+ *
+ * @param {Event} e The event object
+ * @return void
+ * @private
+ */
+ var handleKey = function(e){
+ var code = e ? e.which : event.keyCode;
+ var key = String.fromCharCode(code).toLowerCase();
+ if(assertKey(options.keysClose, key, code)){
+ Shadowbox.close();
+ }else if(assertKey(options.keysPrev, key, code)){
+ Shadowbox.previous();
+ }else if(assertKey(options.keysNext, key, code)){
+ Shadowbox.next();
+ }
+ };
+
+ /**
+ * Shows and hides elements that are troublesome for modal overlays.
+ *
+ * @param {Boolean} on True to show the elements, false otherwise
+ * @return void
+ * @private
+ */
+ var toggleTroubleElements = function(on){
+ var vis = (on ? 'visible' : 'hidden');
+ var selects = document.getElementsByTagName('select');
+ for(i = 0, len = selects.length; i < len; ++i){
+ selects[i].style.visibility = vis;
+ }
+ var objects = document.getElementsByTagName('object');
+ for(i = 0, len = objects.length; i < len; ++i){
+ objects[i].style.visibility = vis;
+ }
+ var embeds = document.getElementsByTagName('embed');
+ for(i = 0, len = embeds.length; i < len; ++i){
+ embeds[i].style.visibility = vis;
+ }
+ };
+
+ /**
+ * Fills the Shadowbox with the loading skin.
+ *
+ * @return void
+ * @private
+ */
+ var showLoading = function(){
+ var loading = SL.get('shadowbox_loading');
+ overwriteHTML(loading, String.format(options.skin.loading,
+ options.loadingImage,
+ options.text.loading,
+ options.text.cancel));
+ loading.style.visibility = 'visible';
+ };
+
+ /**
+ * Hides the Shadowbox loading skin.
+ *
+ * @param {Function} callback The callback function to call after
+ * hiding the loading skin
+ * @return void
+ * @private
+ */
+ var hideLoading = function(callback){
+ var t = current_gallery[current].type;
+ var anim = (t == 'img' || t == 'html'); // fade on images & html
+ var loading = SL.get('shadowbox_loading');
+ if(anim){
+ fadeOut(loading, 0.35, callback);
+ }else{
+ loading.style.visibility = 'hidden';
+ callback();
+ }
+ };
+
+ /**
+ * Sets the size of the overlay to the size of the document.
+ *
+ * @return void
+ * @private
+ */
+ var resizeOverlay = function(){
+ var overlay = SL.get('shadowbox_overlay');
+ SL.setStyle(overlay, {
+ height: '100%',
+ width: '100%'
+ });
+ SL.setStyle(overlay, 'height', SL.getDocumentHeight() + 'px');
+ if(!isSafari3){
+ // Safari3 includes vertical scrollbar in SL.getDocumentWidth()!
+ // Leave overlay width at 100% for now...
+ SL.setStyle(overlay, 'width', SL.getDocumentWidth() + 'px');
+ }
+ };
+
+ /**
+ * Used to determine if the pre-made overlay background image is needed
+ * instead of using the trasparent background overlay. A pre-made background
+ * image is used for all but image pieces in FF Mac because it has problems
+ * displaying correctly if the background layer is not 100% opaque. When
+ * displaying a gallery, if any piece in the gallery meets these criteria,
+ * the pre-made background image will be used.
+ *
+ * @return {Boolean} Whether or not an overlay image is needed
+ * @private
+ */
+ var checkOverlayImgNeeded = function(){
+ if(!(isGecko && isMac)) return false;
+ var t;
+ for(var i = 0, len = current_gallery.length; i < len; ++i){
+ t = current_gallery[i].type;
+ if(t != 'img' && t != 'html') return true;
+ }
+ return false;
+ };
+
+ /**
+ * Activates (or deactivates) the Shadowbox overlay. If a callback function
+ * is provided, we know we're activating. Otherwise, deactivate the overlay.
+ *
+ * @param {Function} callback A callback to call after activation
+ * @return void
+ * @private
+ */
+ var toggleOverlay = function(callback){
+ var overlay = SL.get('shadowbox_overlay');
+ if(overlay_img_needed == null){
+ overlay_img_needed = checkOverlayImgNeeded();
+ }
+
+ if(callback){
+ resizeOverlay(); // size the overlay before showing
+ if(overlay_img_needed){
+ SL.setStyle(overlay, {
+ visibility: 'visible',
+ backgroundColor: 'transparent',
+ backgroundImage: 'url(' + options.overlayBgImage + ')',
+ backgroundRepeat: 'repeat',
+ opacity: 1
+ });
+ callback();
+ }else{
+ SL.setStyle(overlay, {
+ visibility: 'visible',
+ backgroundColor: options.overlayColor,
+ backgroundImage: 'none'
+ });
+ fadeIn(overlay, options.overlayOpacity, options.fadeDuration,
+ callback);
+ }
+ }else{
+ if(overlay_img_needed){
+ SL.setStyle(overlay, 'visibility', 'hidden');
+ }else{
+ fadeOut(overlay, options.fadeDuration);
+ }
+
+ // reset for next time
+ overlay_img_needed = null;
+ }
+ };
+
+ /**
+ * Initializes the Shadowbox environment.
+ *
+ * @param {Object} opts The default options to use
+ * @return void
+ * @public
+ */
+ Shadowbox.init = function(opts){
+ if(initialized) return; // don't initialize twice
+ options = apply(options, opts || {});
+
+ // add markup
+ appendHTML(document.body, options.skin.main);
+
+ // compile file type regular expressions here for speed
+ RE.img = new RegExp('\.(' + options.ext.img.join('|') + ')\s*$', 'i');
+ RE.qt = new RegExp('\.(' + options.ext.qt.join('|') + ')\s*$', 'i');
+ RE.wmp = new RegExp('\.(' + options.ext.wmp.join('|') + ')\s*$', 'i');
+ RE.qtwmp = new RegExp('\.(' + options.ext.qtwmp.join('|') + ')\s*$', 'i');
+ RE.iframe = new RegExp('\.(' + options.ext.iframe.join('|') + ')\s*$', 'i');
+
+ // handle window resize events
+ var id = null;
+ var resize = function(){
+ clearInterval(id);
+ id = null;
+ resizeOverlay();
+ resizeContent(optimal_height, optimal_width);
+ };
+ SL.addEvent(window, 'resize', function(){
+ if(activated){
+ // use event buffering to prevent jerky window resizing
+ if(id){
+ clearInterval(id);
+ id = null;
+ }
+ if(!id) id = setInterval(resize, 50);
+ }
+ });
+
+ // add a listener to the overlay
+ SL.addEvent(SL.get('shadowbox_overlay'), 'click', function(){
+ Shadowbox.close();
+ });
+
+ // adjust some positioning if needed
+ if(absolute_pos){
+ // give the container absolute positioning
+ SL.setStyle(SL.get('shadowbox_container'), 'position', 'absolute');
+ // give shadowbox_body "layout"...whatever that is
+ SL.setStyle('shadowbox_body', 'zoom', 1);
+ // need to listen to the container element because it covers the top
+ // half of the page
+ SL.addEvent(SL.get('shadowbox_container'), 'click', function(e){
+ var target = SL.getTarget(e);
+ if(target.id && target.id == 'shadowbox_container') Shadowbox.close();
+ });
+ }
+
+ // skip setup, will need to be done manually later
+ if(!options.skipSetup) Shadowbox.setup();
+ initialized = true;
+ };
+
+ /**
+ * Grabs all relevant anchor elements on the page and sets them up for use
+ * with Shadowbox. Note: This method may be used to reset Shadowbox if links
+ * on a page change after initialization.
+ *
+ * @param {Array} links An array (or array-like) list of link
+ * elements to set up
+ * @param {Object} opts Some options to use for the given links
+ * @return void
+ * @public
+ */
+ Shadowbox.setup = function(links, opts){
+ // get links if none specified
+ if(!links){
+ var links = [];
+ var a = document.getElementsByTagName('a'), rel;
+ for(var i = 0, len = a.length; i < len; ++i){
+ rel = a[i].getAttribute('rel');
+ if(rel && RE.rel.test(rel)) links[links.length] = a[i];
+ }
+ }else if(!links.length){
+ links = [links]; // one link
+ }
+
+ var link, key;
+ for(var i = 0, len = links.length; i < len; ++i){
+ link = links[i];
+ if(typeof link.shadowboxCacheKey == 'undefined'){
+ // assign cache key expando
+ // use integer primitive so no memory leak in IE
+ link.shadowboxCacheKey = cache.length;
+ SL.addEvent(link, 'click', handleClick); // add listener
+ }
+ cache[link.shadowboxCacheKey] = this.buildCacheObj(link, opts);
+ }
+ };
+
+ /**
+ * Builds an object from the original element data. That will be stored in
+ * the cache. These objects contain (most of) the following keys:
+ *
+ * - id: the link's id
+ * - title: the linked file title
+ * - href: the linked file location
+ * - type: the linked file type
+ * - gallery: the gallery the file belongs to
+ * - height: the height of the linked file (only necessary for movies)
+ * - width: the width of the linked file (only necessary for movies)
+ * - options: custom options to use (optional)
+ *
+ * @param {HTMLElement} link The link to process
+ * @return {Object} An object representing the link
+ * @public
+ */
+ Shadowbox.buildCacheObj = function(link, opts){
+ var href = link.href; // don't use getAttribute() here
+ var o = {
+ el: link,
+ title: link.getAttribute('title'),
+ href: href,
+ type: getPlayerType(href),
+ options: apply({}, opts || {})
+ };
+
+ // remove link-level options from top-level options
+ var opt, l_opts = ['height', 'width', 'gallery'];
+ for(var i = 0, len = l_opts.length; i < len; ++i){
+ opt = l_opts[i];
+ if(typeof o.options[opt] != 'undefined'){
+ o[opt] = o.options[opt];
+ delete o.options[opt];
+ }
+ }
+
+ // HTML options always trump JavaScript options, so do these last
+ var rel = link.getAttribute('rel');
+ if(rel){
+ // extract gallery name from shadowbox[name] format
+ var match = rel.match(RE.gallery);
+ if(match) o.gallery = escape(match[1]);
+
+ // other parameters
+ var params = rel.split(';');
+ for(var i = 0, len = params.length; i < len; ++i){
+ match = params[i].match(RE.param);
+ if(match){
+ if(match[1] == 'options'){
+ eval('o.options = apply(o.options, ' + match[2] + ')');
+ }else{
+ o[match[1]] = match[2];
+ }
+ }
+ }
+ }
+
+ return o;
+ };
+
+ /**
+ * Activates the Shadowbox with the given link element's content.
+ *
+ * @param {HTMLElement} link The link that was clicked
+ * @return void
+ * @public
+ */
+ Shadowbox.open = function(link){
+ if(activated) return; // already open
+ activated = true;
+
+ // setup the current gallery
+ setupGallery(link);
+
+ // anything to display?
+ if(current_gallery.length){
+ openContent(link);
+ }else{
+ throw 'Shadowbox unable to open link, run setup() first';
+ }
+ };
+
+ /**
+ * Changes the view to the picture in the current gallery specified by
+ * num.
+ *
+ * @param {Number} num The gallery index to view
+ * @return void
+ * @public
+ */
+ Shadowbox.change = function(num){
+ if(!current_gallery) return; // no current gallery
+ if(!current_gallery[num]){ // index does not exist
+ if(!options.continuous){
+ return;
+ }else{
+ num = (num < 0) ? (current_gallery.length - 1) : 0; // loop
+ }
+ }
+
+ // update current
+ current = num;
+
+ // stop listening for drag
+ toggleDrag(false);
+ // empty the content
+ setContent(null);
+ // turn this back on when done
+ listenKeyboard(false);
+
+ showLoading();
+ hideBars(loadContent);
+ };
+
+ /**
+ * Attempts to forward the gallery to the next image.
+ *
+ * @return {Boolean} True if the gallery changed to next item, false
+ * otherwise
+ * @public
+ */
+ Shadowbox.next = function(){
+ return this.change(current + 1);
+ };
+
+ /**
+ * Attempts to rewind the gallery to the previous image.
+ *
+ * @return {Boolean} True if the gallery changed to previous item,
+ * false otherwise
+ * @public
+ */
+ Shadowbox.previous = function(){
+ return this.change(current - 1);
+ };
+
+ /**
+ * Deactivates the Shadowbox.
+ *
+ * @return void
+ * @public
+ */
+ Shadowbox.close = function(){
+ if(!activated) return; // already closed
+
+ // stop listening for keys
+ listenKeyboard(false);
+ // hide
+ SL.setStyle(SL.get('shadowbox'), {
+ display: 'none',
+ visibility: 'hidden'
+ });
+ // stop listening for scroll on IE
+ if(absolute_pos) SL.removeEvent(window, 'scroll', centerVertically);
+ // stop listening for drag
+ toggleDrag(false);
+ // empty the content
+ setContent(null);
+ // prevent old image requests from loading
+ if(preloader){
+ preloader.onload = function(){};
+ preloader = null;
+ }
+ // hide the overlay
+ toggleOverlay(false);
+ // turn on trouble elements
+ toggleTroubleElements(true);
+ // fire onClose hook
+ if(options.onClose && typeof options.onClose == 'function'){
+ var link = current_gallery[current];
+ if(link) options.onClose(link.el);
+ }
+
+ activated = false;
+ };
+
+ /**
+ * Generates the markup necessary to embed the movie file with the given
+ * link element. This markup will be browser-specific. Useful for generating
+ * the media test suite.
+ *
+ * @param {HTMLElement} link The link to the media file
+ * @return {Object} The proper markup to use (see above)
+ * @public
+ */
+ Shadowbox.movieMarkup = function(link){
+ // movies default to 300x300 pixels
+ var h = link.height ? parseInt(link.height, 10) : 300;
+ var w = link.width ? parseInt(link.width, 10) : 300;
+
+ var autoplay = options.autoplayMovies;
+ var controls = options.showMovieControls;
+ if(link.options){
+ if(link.options.autoplayMovies != null){
+ autoplay = link.options.autoplayMovies;
+ }
+ if(link.options.showMovieControls != null){
+ controls = link.options.showMovieControls;
+ }
+ }
+
+ var markup = {
+ tag: 'object',
+ name: 'shadowbox_content'
+ };
+
+ switch(link.type){
+ case 'swf':
+ var dims = getDimensions(h, w, true);
+ h = dims.height;
+ w = dims.width;
+ markup.type = 'application/x-shockwave-flash';
+ markup.data = link.href;
+ markup.children = [
+ { tag: 'param', name: 'movie', value: link.href }
+ ];
+ break;
+ case 'flv':
+ autoplay = autoplay ? 'true' : 'false';
+ var showicons = 'false';
+ var a = h/w; // aspect ratio
+ if(controls){
+ showicons = 'true';
+ h += 20; // height of JW FLV player controller
+ }
+ var dims = getDimensions(h, h/a, true); // resize
+ h = dims.height;
+ w = (h-(controls?20:0))/a; // maintain aspect ratio
+ var flashvars = [
+ 'file=' + link.href,
+ 'height=' + h,
+ 'width=' + w,
+ 'autostart=' + autoplay,
+ 'displayheight=' + (h - (controls?20:0)),
+ 'showicons=' + showicons,
+ 'backcolor=0x000000&frontcolor=0xCCCCCC&lightcolor=0x557722'
+ ];
+ markup.type = 'application/x-shockwave-flash';
+ markup.data = options.flvPlayer;
+ markup.children = [
+ { tag: 'param', name: 'movie', value: options.flvPlayer },
+ { tag: 'param', name: 'flashvars', value: flashvars.join('&') },
+ { tag: 'param', name: 'allowfullscreen', value: 'true' }
+ ];
+ break;
+ case 'qt':
+ autoplay = autoplay ? 'true' : 'false';
+ if(controls){
+ controls = 'true';
+ h += 16; // height of QuickTime controller
+ }else{
+ controls = 'false';
+ }
+ markup.children = [
+ { tag: 'param', name: 'src', value: link.href },
+ { tag: 'param', name: 'scale', value: 'aspect' },
+ { tag: 'param', name: 'controller', value: controls },
+ { tag: 'param', name: 'autoplay', value: autoplay }
+ ];
+ if(isIE){
+ markup.classid = 'clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B';
+ markup.codebase = 'http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0';
+ }else{
+ markup.type = 'video/quicktime';
+ markup.data = link.href;
+ }
+ break;
+ case 'wmp':
+ autoplay = autoplay ? 1 : 0;
+ markup.children = [
+ { tag: 'param', name: 'autostart', value: autoplay }
+ ];
+ if(isIE){
+ if(controls){
+ controls = 'full';
+ h += 70; // height of WMP controller in IE
+ }else{
+ controls = 'none';
+ }
+ // markup.type = 'application/x-oleobject';
+ markup.classid = 'clsid:6BF52A52-394A-11d3-B153-00C04F79FAA6';
+ markup.children[markup.children.length] = { tag: 'param', name: 'url', value: link.href };
+ markup.children[markup.children.length] = { tag: 'param', name: 'uimode', value: controls };
+ }else{
+ if(controls){
+ controls = 1;
+ h += 45; // height of WMP controller in non-IE
+ }else{
+ controls = 0;
+ }
+ markup.type = 'video/x-ms-wmv';
+ markup.data = link.href;
+ markup.children[markup.children.length] = { tag: 'param', name: 'showcontrols', value: controls };
+ }
+ break;
+ }
+
+ markup.height = h; // new height includes controller
+ markup.width = w;
+
+ return markup;
+ };
+
+ /**
+ * Creates an HTML string from an object representing HTML elements. Based
+ * on Ext.DomHelper's createHtml.
+ *
+ * @param {Object} obj The HTML definition object
+ * @return {String} An HTML string
+ * @public
+ */
+ Shadowbox.createHTML = function(obj){
+ var html = '<' + obj.tag;
+ for(var attr in obj){
+ if(attr == 'tag' || attr == 'html' || attr == 'children') continue;
+ if(attr == 'cls'){
+ html += ' class="' + obj['cls'] + '"';
+ }else{
+ html += ' ' + attr + '="' + obj[attr] + '"';
+ }
+ }
+ if(RE.empty.test(obj.tag)){
+ html += '/>\n';
+ }else{
+ html += '>\n';
+ var cn = obj.children;
+ if(cn){
+ for(var i = 0, len = cn.length; i < len; ++i){
+ html += this.createHTML(cn[i]);
+ }
+ }
+ if(obj.html) html += obj.html;
+ html += '</' + obj.tag + '>\n';
+ }
+ return html;
+ };
+
+ /**
+ * Gets an object that lists which plugins are supported on this platform.
+ * The keys of this object will be:
+ *
+ * - fla: Adobe Flash Player
+ * - qt: QuickTime Player
+ * - wmp: Windows Media Player
+ * - f4m: Flip4Mac QuickTime Player
+ *
+ * @return {Object} The plugins object
+ * @public
+ */
+ Shadowbox.getPlugins = function(){
+ return plugins;
+ };
+
+ /**
+ * Gets the current options object in use.
+ *
+ * @return {Object} The options object
+ * @public
+ */
+ Shadowbox.getOptions = function(){
+ return options;
+ };
+
+ /**
+ * Gets the current gallery item.
+ *
+ * @return {Object} The current gallery item
+ * @public
+ */
+ Shadowbox.getCurrent = function(){
+ return current_gallery[current];
+ };
+
+ /**
+ * Gets the current version number of Shadowbox.
+ *
+ * @return {String} The current version
+ * @public
+ */
+ Shadowbox.version = function(){
+ return version;
+ };
+
+})();
+
+/**
+ * Finds the index of the given object in this array.
+ *
+ * @param {mixed} o The object to search for
+ * @return {Number} The index of the given object
+ * @public
+ */
+Array.prototype.indexOf = Array.prototype.indexOf || function(o){
+ for (var i = 0, len = this.length; i < len; ++i){
+ if(this[i] == o) return i;
+ }
+ return -1;
+};
+
+/**
+ * Formats a string with the given parameters. The string for format must have
+ * placeholders that correspond to the numerical index of the arguments passed
+ * in surrounded by curly braces (e.g. 'Some {0} string {1}').
+ *
+ * @param {String} format The string to format
+ * @param ... The parameters to put inside the string
+ * @return {String} The string with the specified parameters
+ * replaced
+ * @public
+ * @static
+ */
+String.format = String.format || function(format){
+ var args = Array.prototype.slice.call(arguments, 1);
+ return format.replace(/\{(\d+)\}/g, function(m, i){
+ return args[i];
+ });
+};
Modified: modules/portlet/trunk/test/src/test/resources/simple-portal-war/layouts/header.jsp
===================================================================
--- modules/portlet/trunk/test/src/test/resources/simple-portal-war/layouts/header.jsp 2008-02-10 23:55:18 UTC (rev 9929)
+++ modules/portlet/trunk/test/src/test/resources/simple-portal-war/layouts/header.jsp 2008-02-11 02:47:55 UTC (rev 9930)
@@ -7,6 +7,16 @@
<head>
<title>Portlet Container 2.0</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/master.css" type="text/css"/>
+<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/css/shadowbox.css" />
+<script type="text/javascript" src="${pageContext.request.contextPath}/js/prototype.js"></script>
+<script type="text/javascript" src="${pageContext.request.contextPath}/js/scriptaculous/scriptaculous.js?load=effects"></script>
+<script type="text/javascript" src="${pageContext.request.contextPath}/js/shadowbox-prototype.js"></script>
+<script type="text/javascript" src="${pageContext.request.contextPath}/js/shadowbox.js"></script>
+ <script type="text/javascript">
+ window.onload = function(){
+ Shadowbox.init();
+ };
+ </script>
<portal:headers/>
</head>
<body>
16 years, 8 months