JBoss Rich Faces SVN: r22976 - modules/docs/trunk/Component_Reference/src/main/docbook/en-US.
by richfaces-svn-commits@lists.jboss.org
Author: bleathem
Date: 2011-11-22 18:37:09 -0500 (Tue, 22 Nov 2011)
New Revision: 22976
Modified:
modules/docs/trunk/Component_Reference/src/main/docbook/en-US/chap-Component_Reference-Output_and_messages.xml
Log:
Grammar cleanup of the notify component documentation
Modified: modules/docs/trunk/Component_Reference/src/main/docbook/en-US/chap-Component_Reference-Output_and_messages.xml
===================================================================
--- modules/docs/trunk/Component_Reference/src/main/docbook/en-US/chap-Component_Reference-Output_and_messages.xml 2011-11-22 23:04:07 UTC (rev 22975)
+++ modules/docs/trunk/Component_Reference/src/main/docbook/en-US/chap-Component_Reference-Output_and_messages.xml 2011-11-22 23:37:09 UTC (rev 22976)
@@ -242,37 +242,29 @@
<section id="sect-Component_Reference-Output_and_messages-richnotify">
<title><sgmltag><rich:notify></sgmltag></title>
<para>
- The <sgmltag><rich:notify></sgmltag> component serves for advanced user interaction, giving user instant feedback on what's happening in application in form of notifications.
+ The <sgmltag><rich:notify></sgmltag> component serves for advanced user interaction, using notification boxes to give the user instant feedback on what's happening within the application.
+ Each time this component is rendered, a floating notification box is displayed in the selected corner of the browser screen.
</para>
- <para>
- Each time this component is rendered, floating notification box is displayed in the selected corner of the browser screen.
- </para>
<section id="sect-Component_Reference-richnotify-Basic_usage">
<title>Basic usage</title>
<para>
- The <sgmltag><rich:notify></sgmltag> has two message customization attributes: <parameter>summary</parameter> is short text summarizing the message, while <parameter>detail</parameter> configures detailed body of the message.
+ The <sgmltag><rich:notify></sgmltag> has two message customization attributes: <parameter>summary</parameter> is short text summarizing the message, while <parameter>detail</parameter> configures the detailed body of the message.
+ Both attributes have their counterparts in form of facets with the same names as the corresponding attributes.
</para>
- <para>
- Both attributes have their counterparts in form of facets with same names as attributes.
- </para>
</section>
<section id="sect-Component_Reference-richnotify-Customizing_notifications">
<title>Customizing notifications</title>
<para>
- Notification appears on the page each them they are rendered, on full-page or ajax requests.
+ A notification appears on the page each time it is rendered, either on full-page or ajax requests.
+ The notification remains on the screen for 8 seconds and then disappears.
+ Users can close the notification with the close button in the top-right corner of the notification.
</para>
<para>
- Notifications are waiting on the screen for 8 seconds and then they disappear.
+ Notification stacks can be used to create sequences. For customization of stacking see the <sgmltag><rich:notifyStack></sgmltag> component.
</para>
<para>
- Notification stacks and creates sequences. For customizition of stacking see <sgmltag><rich:notifyStack></sgmltag> component.
- </para>
- <para>
- User can close notification with close button in the top-right corner of each notification.
- </para>
- <para>
There are several attributes that can change default behavior:
</para>
<itemizedlist>
@@ -308,7 +300,7 @@
</listitem>
</itemizedlist>
<note>
- Nonblocking notifications can be clicked through, but because they are using jQuery mechanism to bypass events, only jQuery event handlers are triggered. It specifically means that standard links won't be triggered.
+ Nonblocking notifications can be clicked through, but because they are using jQuery mechanism to bypass events, only jQuery event handlers are triggered. IThis means that standard links won't be triggered.
</note>
</section>
@@ -354,16 +346,12 @@
<title>Basic usage</title>
<para>
The <sgmltag><rich:notifyMessage></sgmltag> component is built on top of <sgmltag><rich:notify></sgmltag>, the difference is in usage.
+ The <sgmltag><rich:notifyMessage></sgmltag> component displays <code>FacesMessage</code>s associated with a given component, similar to <sgmltag><rich:message></sgmltag>: one notification is displayed for first <code>FacesMessage</code> in the stack that is risen either programatically or during conversion/validation of the component.
+ The severity of the message determines the color and icon of the resulting notification.
</para>
<para>
- Component displays <code>FacesMessage</code>s associated with given component, similarry to <sgmltag><rich:message></sgmltag>: one notification is displayed for first <code>FacesMessage</code> in stack, raisen either programatically or during component conversion or validation.
+ For customization of notification behavior, please refer to <link linkend="sect-Component_Reference-richnotify-Customizing_notifications">Customizing notifications</link> of <sgmltag><rich:notify></sgmltag>.
</para>
- <para>
- Severify of message which triggers notification determines color and icon of notification.
- </para>
- <para>
- For customization of notifications behavior refer to <link linkend="sect-Component_Reference-richnotify-Customizing_notifications">Customizing notifications</link> of <sgmltag><rich:notify></sgmltag>.
- </para>
</section>
<section id="sect-Component_Reference-richnotifyMessage-Reference_data">
@@ -395,10 +383,10 @@
<section id="sect-Component_Reference-richnotifyMessage-Style_classes_and_skin_parameters">
<title>Style classes and skin parameters</title>
<para>
- Note that <sgmltag><rich:notifyMessage></sgmltag> shares common classes with <sgmltag><rich:notify></sgmltag>, since for each JSF message, exactly one notification is rendered.
+ Note that <sgmltag><rich:notifyMessage></sgmltag> shares common classes with <sgmltag><rich:notify></sgmltag>, since there is exactly one notification rendered for each JSF message.
</para>
<para>
- The <sgmltag><rich:notifyMessage></sgmltag> specific classes are redefining look for various message severity levels.
+ The <sgmltag><rich:notifyMessage></sgmltag> specific classes are redefining the look for various message severity levels.
</para>
<xi:include href="skinning/tabl-richnotifyMessage.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
</section>
@@ -407,7 +395,7 @@
<section id="sect-Component_Reference-Output_and_messages-richnotifyMessages">
<title><sgmltag><rich:notifyMessages></sgmltag></title>
<para>
- The <sgmltag><rich:notifyMessages></sgmltag> component is same as <sgmltag><rich:notifyMessage></sgmltag>, but each of available messages generates one notification.
+ The <sgmltag><rich:notifyMessages></sgmltag> component is the same as the <sgmltag><rich:notifyMessage></sgmltag> component, but each of the available messages generates one notification.
</para>
<para>
<sgmltag><rich:notifyMessages></sgmltag> shares the same set of attributes with <sgmltag><rich:notifyMessage></sgmltag>
@@ -474,31 +462,31 @@
<section id="sect-Component_Reference-richnotifyStack-Basic_usage">
<title>Basic usage</title>
<para>
- They are three alternative ways how to bind notification with stack:
+ They are three alternative ways to bind notifications with a stack:
</para>
<itemizedlist>
<listitem>
<para>
- <emphasis>wrapping</emphasis>: nesting <sgmltag><rich:notify></sgmltag>, <sgmltag><rich:notifyMessage></sgmltag> or <sgmltag><rich:notifyMessages></sgmltag> bounds notifications with stack which is wrapping them
+ <emphasis>wrapping</emphasis>: nesting <sgmltag><rich:notify></sgmltag>, <sgmltag><rich:notifyMessage></sgmltag> or <sgmltag><rich:notifyMessages></sgmltag> binds notifications with the stack in which they are wrapped
</para>
</listitem>
<listitem>
<para>
- <emphasis>binding by id</emphasis>: notification can be bound directly to stack using it's <code>componentId</code> in attribute <parameter>stack</parameter>
+ <emphasis>binding by id</emphasis>: notification can be bound directly to a stack using it's <code>componentId</code> in the <parameter>stack</parameter> attribute
</para>
</listitem>
<listitem>
<para>
- <emphasis>using default stack</emphasis>: default stack is used when no other binding is defined for given notification
+ <emphasis>using default stack</emphasis>: a default stack is used when no other binding is defined for a given notification
</para>
</listitem>
</itemizedlist>
<programlisting language="XML" role="XML"><xi:include parse="text" href="extras/exam-Component_Reference-richnotifyStack-Basic_usage.xml_sample" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<para>
- The sample above defines stack explicitly, where notifications uses stack in that they are wrapped.
+ The sample above defines the stack explicitly, where notifications use the stack in which they are wrapped.
</para>
<para>
- The sample bellow uses notification, which is rendered into top-left screen corner. Notification is bound to stack using its id.
+ The sample bellow uses a notification rendered into the top-left corner of the screen. The notification is bound to a stack using it's id.
</para>
<programlisting language="XML" role="XML"><xi:include parse="text" href="extras/exam-Component_Reference-richnotifyStack-Binding_by_stack_id.xml_sample" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
</section>
@@ -506,17 +494,17 @@
<section id="sect-Component_Reference-richnotifyStack-Positioning_notifications">
<title>Positioning notifications</title>
<para>
- To redefine positioning of notification, one needs to define stack and bind it with given notification.
+ To redefine the position of a notification, one needs to define a stack and bind it with the given notification.
</para>
<para>
- <sgmltag><rich:notifyStack></sgmltag> uses <parameter>position</parameter> attribute which places stack and its notifications into one of four corners: <code>topRight</code> (default), <code>bottomRight</code>, <code>bottomLeft</code>, <code>topLeft</code>.
+ <sgmltag><rich:notifyStack></sgmltag> uses the <parameter>position</parameter> attribute to place the stack and it's notifications into one of four corners: <code>topRight</code> (default), <code>bottomRight</code>, <code>bottomLeft</code>, <code>topLeft</code>.
</para>
</section>
<section id="sect-Component_Reference-richnotifyStack-Stacking_notifications">
<title>Stacking notifications</title>
<para>
- There are two attributes which influences how notifications are placed into stack:
+ There are two attributes which influences how notifications are placed into a stack:
</para>
<itemizedlist>
<listitem>
@@ -529,11 +517,11 @@
</listitem>
</itemizedlist>
<para>
- Following sample shows stack which will place new notification as first - incoming message will appear first, causing all notifications currently in stack to shift. Adequately, messages on the end of stack will be then removed from stack as first.
+ The following sample shows a stack which will place new notifications up front - the incoming message will appear first, causing all notifications currently in stack to shift. Subsequently, messages at the end of stack will be then removed.
</para>
<programlisting language="XML" role="XML"><xi:include parse="text" href="extras/exam-Component_Reference-richnotifyStack-Stacking_method_first.xml_sample" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<para>
- On the other hand, stacking method <code>last</code> does provide method to place messages on the end of stack, and when removing notification, it is removed from start, causing all other notifications to shift.
+ On the other hand, stacking method <code>last</code> provides a method to place messages on the end of the stack, and when removing a notification, it is removed from the start, causing all other notifications to shift.
</para>
</section>
13 years, 1 month
JBoss Rich Faces SVN: r22975 - modules/docs/trunk/Component_Reference/src/main/docbook/en-US.
by richfaces-svn-commits@lists.jboss.org
Author: bleathem
Date: 2011-11-22 18:04:07 -0500 (Tue, 22 Nov 2011)
New Revision: 22975
Modified:
modules/docs/trunk/Component_Reference/src/main/docbook/en-US/chap-Component_Reference-Rich_inputs.xml
Log:
Grammar cleanup of the editor component documentation
Modified: modules/docs/trunk/Component_Reference/src/main/docbook/en-US/chap-Component_Reference-Rich_inputs.xml
===================================================================
--- modules/docs/trunk/Component_Reference/src/main/docbook/en-US/chap-Component_Reference-Rich_inputs.xml 2011-11-22 19:28:59 UTC (rev 22974)
+++ modules/docs/trunk/Component_Reference/src/main/docbook/en-US/chap-Component_Reference-Rich_inputs.xml 2011-11-22 23:04:07 UTC (rev 22975)
@@ -814,10 +814,10 @@
</mediaobject>
</figure>
<para>
- <sgmltag><rich:editor></sgmltag> component is based on CKEditor implementation.
+ <sgmltag><rich:editor></sgmltag> component is based on the CKEditor implementation.
</para>
<para>
- When rendering <sgmltag><rich:editor></sgmltag>, textarea is rendered to the page and once the page is completely loaded (ready state), textarea is enhanced using CKEditor script and replaced with full-featured WYSIWYG editor.
+ When rendering a <sgmltag><rich:editor></sgmltag>, a textarea is rendered to the page and once the page is completely loaded (ready state), the textarea is enhanced using a CKEditor script and replaced with a full-featured WYSIWYG editor.
</para>
<section id="sect-Component_Reference-richeditor-Basic_usage">
@@ -829,45 +829,45 @@
<title>Basic usage of <sgmltag><rich:editor></sgmltag></title>
<programlisting language="XML" role="XML"><xi:include parse="text" href="extras/exam-Component_Reference-richeditor-Basic_usage.xml_sample" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<para>
- Note that editor does produce HTML markup and to be able render it's output, markup needs to be output as unescaped (as with <sgmltag><h:outputText></sgmltag> component in example above).
+ Note that the editor produces HTML markup and to be able to render it's output, the markup needs to be unescaped (as with <sgmltag><h:outputText></sgmltag> component in example above).
</para>
</example>
<para>
- Dimensions of editor can be managed using <parameter>width</parameter> and <parameter>height</parameter> attributes.
+ The dimensions of the editor can be managed using <parameter>width</parameter> and <parameter>height</parameter> attributes.
</para>
<para>
- The <parameter>readonly</parameter> attribute can be used to switch editor into read-only mode.
+ The <parameter>readonly</parameter> attribute can be used to switch the editor into a read-only mode.
</para>
<para>
The <parameter>tabindex</parameter> attribute specifies the position of the current element in the tabbing order for the current document.
</para>
<note>
- <code>ResourceServlet</code> has to be registered for <code>url-pattern</code> <code>/org.richfaces.resources/*</code> in order to serve editor resources (JavaScript, CSS, images) correctly. Check <emphasis>RichFaces Developer's Guide</emphasis> out for details.
+ The <code>ResourceServlet</code> has to be registered for the <code>url-pattern</code> <code>/org.richfaces.resources/*</code> in order to serve the editor resources (JavaScript, CSS, images) correctly. Check the <emphasis>RichFaces Developer's Guide</emphasis> for further details.
</note>
<note>
- The <sgmltag><rich:editor></sgmltag> requires having <sgmltag><h:body></sgmltag> component present in the view and being its ancestor for rendering resource dependencies correctly.
+ The <sgmltag><rich:editor></sgmltag> requires the <sgmltag><h:body></sgmltag> component to be present in the view and must be an ancestor of the editor in order for the resource dependencies to render correctly.
</note>
</section>
<section id="sect-Component_Reference-richeditor-Styling">
<title>Styling</title>
<para>
- There are several options to customize style of the editor:
+ There are several options to customize the style of the editor:
</para>
<itemizedlist>
<listitem>
<para>
- <parameter>style, styleClass</parameter>: customizes style of the editor and underlying textarea
+ <parameter>style, styleClass</parameter>: customizes the style of the editor and underlying textarea
</para>
</listitem>
<listitem>
<para>
- <parameter>editorStyle, editorClass</parameter>: customizes style of the CKEditor instance
+ <parameter>editorStyle, editorClass</parameter>: customizes the style of the CKEditor instance
</para>
</listitem>
<listitem>
<para>
- <parameter>textareaStyle, textareaClass</parameter>: customizes style of the underlying textarea
+ <parameter>textareaStyle, textareaClass</parameter>: customizes the style of the underlying textarea
</para>
</listitem>
</itemizedlist>
@@ -876,10 +876,10 @@
<section id="sect-Component_Reference-richeditor-Editor_skins">
<title>Editor skins</title>
<para>
- The <sgmltag><rich:editor></sgmltag> comes with skinnability using attribute <code>skin</code> and two approaches:
+ The <sgmltag><rich:editor></sgmltag> is skinnable using the <code>skin</code> attribute and either of the two approaches:
</para>
<para>
- At first, <sgmltag><rich:editor></sgmltag> has default skin called <code>richfaces</code> that is optimized to suit rest of the component suite look & feel and behaves regarding to which RichFaces skin is currently chosen (refer to <emphasis>RichFaces Developer's Guide</emphasis> for details about <emphasis>Skinning and theming</emphasis>).
+ By default, <sgmltag><rich:editor></sgmltag> has a skin called <code>richfaces</code> that is optimized to match rest of the component suite look & feel and changes to match the active RichFaces skin (refer to <emphasis>RichFaces Developer's Guide</emphasis> for details about <emphasis>Skinning and theming</emphasis>).
</para>
<example>
<title>The skin <code>richfaces</code> of <sgmltag><rich:editor></sgmltag></title>
@@ -890,17 +890,17 @@
</imageobject>
<textobject>
<para>
- A <sgmltag><rich:editor></sgmltag> with default <code>richfaces</code> editor skin rendered against several RichFaces Skin options.
+ A <sgmltag><rich:editor></sgmltag> with the default <code>richfaces</code> editor skin rendered against several RichFaces Skin options.
</para>
</textobject>
</mediaobject>
</figure>
<para>
- A <sgmltag><rich:editor></sgmltag> with default <code>richfaces</code> editor skin rendered against several RichFaces Skin options.
+ A <sgmltag><rich:editor></sgmltag> with the default <code>richfaces</code> editor skin rendered against several RichFaces Skin options.
</para>
</example>
<para>
- Second option gives you possibility to use any other skin for CKEditor, either from standard distribution or downloaded one or custom one. In distribution, there are three skins bundled: <code>kama, v2, office2003</code>.
+ Alternatively, you can use any other CKeditor skin, either from the standard distribution, a downloaded skin, or a custom skin. In the distribution, there are three skins bundled: <code>kama, v2, office2003</code>.
</para>
<example>
<title>Examples of <sgmltag><rich:editor></sgmltag> skins in CKEditor distribution</title>
@@ -925,23 +925,19 @@
<section id="sect-Component_Reference-richeditor-Advanced_configuration">
<title>Advanced configuration</title>
<para>
- Basic set of <sgmltag><rich:editor></sgmltag> attributes allows to support common use-cases for WYSIWYG editor.
+ The basic set of <sgmltag><rich:editor></sgmltag> attributes allows you to support common use-cases for a WYSIWYG editor.
+ However the underlying CKEditor implementation supports many more configuration options.
</para>
<para>
- However CKEditor imlementation supports much more configuration options.
+ Use the <parameter>config</parameter> attribute to define any of these advanced configuration options supported by the CKEditor.
+ This configuration is written in JavaScript object format and its value is interpolated for EL expressions (making configuration dynamic).
</para>
<para>
- Attribute <parameter>config</parameter> gives possibility to define any advanced configuration option which CKEditor supports.
+ There are two ways to define the configuration: the <parameter>config</parameter> attribute or a facet named <parameter>config</parameter>. The facet takes precedence over attribute when both are defined.
</para>
- <para>
- Configuration is written in JavaScript object format and its value is interpolated for EL expressions (making configuration dynamic).
- </para>
- <para>
- There are two options to define configuration: <parameter>config</parameter> attribute and facet with name <parameter>config</parameter>. Facet takes precedence over attribute when both defined.
- </para>
<programlisting language="XML" role="XML"><xi:include parse="text" href="extras/exam-Component_Reference-richeditor-Advanced_configuration.xml_sample" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<para>
- In samples above, <sgmltag><rich:editor></sgmltag> is configured to take focus after load of the page regarding to user preferences. Definitions using either attribute or facet are adequate.
+ In the above samples, the <sgmltag><rich:editor></sgmltag> is configured to take focus after loading the page as defined by the userPreference bean. Definitions using either attribute or facet are equivalent.
</para>
<note>
For further configuration options, refer to <ulink url="http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Setting_Configurat...">CKEditor 3 Developer Guide</ulink> and <ulink url="http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html">CKEditor 3 configuration reference</ulink>.
@@ -951,14 +947,12 @@
<section id="sect-Component_Reference-richeditor-Toolbar_customization">
<title>Toolbar customization</title>
<para>
- <sgmltag><rich:editor></sgmltag> supports <parameter>toolbar</parameter> attribute able to switch between configurations of toolbar's button set.
+ The <sgmltag><rich:editor></sgmltag> supports a <parameter>toolbar</parameter> attribute, which is able to change the configuration of the toolbar's button set.
+ There are two configurations available: <code>basic</code> (default), <code>full</code> (enables all of the features).
</para>
<para>
- There are two configurations available: <code>basic</code> (default), <code>full</code> (enables all of the features).
+ It is also possible to define a custom toolbar using the CKEditor toolbar configuration in a <code>config</code> facet:
</para>
- <para>
- It is possible to define custom toolbar using CKEditor toolbar configuration in <code>config</code> facet:
- </para>
<programlisting language="XML" role="XML"><xi:include parse="text" href="extras/exam-Component_Reference-richeditor-Toolbar_customization.xml_sample" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<para>
Note that toolbar name (<code>CustomToolbar</code>) needs to match the <code>toolbar_<name></code> configuration option.
@@ -968,9 +962,7 @@
<section id="sect-Component_Reference-richeditor-Internationalization_and_localization">
<title>Internationalization and localization</title>
<para>
- <sgmltag><rich:editor></sgmltag> comes with attribute <parameter>lang</parameter> which allows to change localization of editor.
- </para>
- <para>
+ The <sgmltag><rich:editor></sgmltag> comes with a <parameter>lang</parameter> attribute which allows you to change the localization of the editor.
For language configuration options, refer to <ulink url="http://www.w3.org/TR/html4/struct/dirlang.html">http://www.w3.org/TR/html4/struct/dirlang.html</ulink>.
</para>
<para>
@@ -994,14 +986,12 @@
</listitem>
</itemizedlist>
<para>
- However localization of interface is in first place localized using the browser configuration (usually determined by client system settings).
+ However the interface first localized using the browser configuration (usually determined by client system settings).
+ To force the editor to use a specific localization for the interface, you use the advanced CKEditor configuration option <code>language</code>, as in following sample:
</para>
- <para>
- To force editor to use specific localization of interface, you can use advanced CKEditor configuration option <code>language</code> as in following sample:
- </para>
<programlisting language="XML" role="XML"><xi:include parse="text" href="extras/exam-Component_Reference-richeditor-Internationalization_and_localization.xml_sample" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<para>
- The sample above force editor to use french interface, supressing the browser preferred settings.
+ The above sample forces the editor to use a french interface, suppressing the browser preferred settings.
</para>
</section>
@@ -1038,18 +1028,18 @@
</listitem>
</itemizedlist>
<para>
- Events can be handled either by registering JavaScript event handler or by attaching JSF behavior:
+ Events can be handled either by registering a JavaScript event handler or by attaching JSF behavior:
</para>
<programlisting language="XML" role="XML"><xi:include parse="text" href="extras/exam-Component_Reference-richeditor-Client_side_event_handlers.xml_sample" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<para>
- Example above shows editor and its output, which is updated each 1 sec after each instant change or immediately after user focus leaves editor area.
+ The example above shows the editor and its output, which is updated every second after each instant change or immediately after user focus leaves the editor area.
</para>
</section>
<section id="sect-Component_Reference-richeditor-JavaScript_API">
<title>JavaScript API</title>
<para>
- The <sgmltag><rich:inplaceInput></sgmltag> component can be controlled through the JavaScript API. The JavaScript API provides the following functions:
+ The <sgmltag><rich:editor></sgmltag> component can be controlled through the JavaScript API. The JavaScript API provides the following functions:
</para>
<variablelist>
<varlistentry>
13 years, 1 month
JBoss Rich Faces SVN: r22974 - modules/docs/trunk/Component_Reference/src/main/docbook/en-US.
by richfaces-svn-commits@lists.jboss.org
Author: bleathem
Date: 2011-11-22 14:28:59 -0500 (Tue, 22 Nov 2011)
New Revision: 22974
Modified:
modules/docs/trunk/Component_Reference/src/main/docbook/en-US/chap-Component_Reference-Actions.xml
Log:
Grammar cleanup of the push component documentation
Modified: modules/docs/trunk/Component_Reference/src/main/docbook/en-US/chap-Component_Reference-Actions.xml
===================================================================
--- modules/docs/trunk/Component_Reference/src/main/docbook/en-US/chap-Component_Reference-Actions.xml 2011-11-22 15:00:50 UTC (rev 22973)
+++ modules/docs/trunk/Component_Reference/src/main/docbook/en-US/chap-Component_Reference-Actions.xml 2011-11-22 19:28:59 UTC (rev 22974)
@@ -558,7 +558,7 @@
<itemizedlist>
<listitem>
<para>
- <emphasis>JMS</emphasis> - RichFaces Push consumes messages from enterprise messaging system and exposes them to client (tightly couples with JMS runtime)
+ <emphasis>JMS</emphasis> - RichFaces Push consumes messages from enterprise messaging system and exposes them to client (tightly coupled with JMS runtime)
</para>
</listitem>
<listitem>
@@ -568,12 +568,12 @@
</listitem>
<listitem>
<para>
- <emphasis>Push CDI</emphasis> - uses CDI Events mechanism to access TopicsContext
+ <emphasis>Push CDI</emphasis> - uses the CDI Event mechanism to access TopicsContext
</para>
</listitem>
</itemizedlist>
<para>
- On the client side, push notification may be processed by following ways:
+ On the client side, push notifications may be processed in the following ways:
</para>
<itemizedlist>
<listitem>
@@ -583,7 +583,7 @@
</listitem>
<listitem>
<para>
- Attaching behavior to <code>dataavailable</code> event - any behavior may be attached (<sgmltag><a4j:ajax></sgmltag>, <sgmltag><rich:componentControl></sgmltag>, etc.)
+ Attaching a behavior to the <code>dataavailable</code> event - any behavior may be attached (<sgmltag><a4j:ajax></sgmltag>, <sgmltag><rich:componentControl></sgmltag>, etc.)
</para>
</listitem>
</itemizedlist>
@@ -592,42 +592,42 @@
<section id="sect-Component_Reference-a4jpush-Push_Topics">
<title>Push Topics</title>
<para>
- No matter which type way of producing messages is chosen, message is delivered to the client based on the topic name (with optional subtopic).
+ No matter which method of producing messages is chosen, the message is delivered to the client based on the topic name (with optional subtopic).
</para>
<para>
Examples of topic names might be <code>someTopic</code> or <code>subtopic@anotherTopic</code>.
</para>
<note>
- Push topic name format is very close to JMS topic names and thus enables seamless transport of JMS messages to RichFaces message queue.
+ The format for the name of the push topic is very close to that of the JMS topic name and thus enables seamless transport of JMS messages to the RichFaces message queue.
</note>
</section>
<section id="sect-Component_Reference-a4jpush-Handling_a_push_notification">
<title>Handling a push notification</title>
<para>
- A push notification from server sent to the <sgmltag><a4j:push></sgmltag> component on client will cause it to trigger any event handlers defined using the <varname>dataavailable</varname> event handler.
+ A push notification sent from the server to the <sgmltag><a4j:push></sgmltag> component on the client will cause it to trigger any event handlers defined using the <varname>dataavailable</varname> event handler.
</para>
<para>
- The <sgmltag><a4j:push></sgmltag> component should also include the <varname>error</varname> event handler to inform the user when an error has occurred with the push notifications.
+ The <sgmltag><a4j:push></sgmltag> component should also include the <varname>onerror</varname> event handler to inform the user when an error has occurred with the push notifications.
</para>
<para>
- Topic name may be determined dynamically in runtime, using Expression Language expressions.
+ The topic name may be determined dynamically at runtime, using Expression Language expressions.
</para>
<example id="exam-Component_Reference-a4jpush-Handling_a_push_notification">
<title>Handling a push notification</title>
<programlisting language="XML" role="XML"><xi:include parse="text" href="extras/exam-Component_Reference-a4jpush-Handling_a_push_notification.xml_sample" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<para>
- The example uses the <varname>dataavailable</varname> event attribute with JavaScript to update messages in a chat room. The <parameter>event.rf.data</parameter> parameter contains JMS message data serialized to JavaScript.
+ This example uses the <varname>dataavailable</varname> event attribute with some JavaScript to update messages in a chat room. The <parameter>event.rf.data</parameter> parameter contains JMS message data serialized to JavaScript.
</para>
</example>
<para>
- <sgmltag><a4j:push></sgmltag> can be used for both, immediate processing of message (like in example above) or it can trigger partial page update.
+ <sgmltag><a4j:push></sgmltag> can be used for either immediate processing of messages (like in the previous example) or it can trigger partial page update (see the following example).
</para>
<example id="exam-Component_Reference-a4jpush-Updating_DOM_for_each_push_notification">
<title>Updating DOM for each push notification</title>
<programlisting language="XML" role="XML"><xi:include parse="text" href="extras/exam-Component_Reference-a4jpush-Updating_DOM_for_each_push_notification.xml_sample" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<para>
- The example uses the <varname>dataavailable</varname> event handler to trigger AJAX request and partial page update.
+ This example uses the <varname>dataavailable</varname> event handler to trigger an AJAX request and partial page update.
</para>
</example>
</section>
@@ -714,7 +714,7 @@
<programlisting language="XML" role="XML"><xi:include parse="text" href="extras/exam-Component_Reference-a4jpush-Basic_usage.xml_sample" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<programlisting language="Java" role="JAVA"><xi:include parse="text" href="extras/exam-Component_Reference-a4jpush-Basic_usage.java" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<para>
- The example demonstrates a simple use of the <sgmltag><a4j:push></sgmltag> causing immediate update of page content.
+ This example demonstrates a simple use of the <sgmltag><a4j:push></sgmltag> causing immediate update of page content.
</para>
</example>
</section>
@@ -722,30 +722,30 @@
<section id="sect-Component_Reference-a4jpush-Using_Push_without_JMS">
<title>Using Push without JMS</title>
<para>
- Since JMS coupling may be unwanted in certain cases, RichFaces provides switch which turns off JMS integration:
+ Since JMS coupling may be unwanted in certain cases, RichFaces provides a switch to turn off the JMS integration:
</para>
<programlisting language="XML" role="XML"><xi:include parse="text" href="extras/exam-Component_Reference-a4jpush-Using_Push_without_JMS.xml_sample" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<para>
- This switch is important in situations when you want to use alternative situations of sending push notifications: <emphasis>Push CDI</emphasis> or <code>TopicsContext</code> programmatic interface.
+ This switch is necessary when you want to make use of the alternative methods for sending push notifications: <emphasis>Push CDI</emphasis> or the <code>TopicsContext</code> programmatic interface.
</para>
</section>
<section id="sect-Component_Reference-a4jpush-Using_TopicsContext_to_publish_message">
<title>Using TopicsContext to publish message</title>
<para>
- Alternative way of producing messages is using <code>TopicsContext</code> interface directly as in following sample:
+ Alternative way of producing messages is using the <code>TopicsContext</code> interface directly as in the following sample:
</para>
<programlisting language="Java" role="JAVA"><xi:include parse="text" href="extras/exam-Component_Reference-a4jpush-Using_TopicsContext_to_publish_message.xml_sample" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<para>
- Sending message using <code>TopicsContext</code> involves creating <code>TopicKey</code> instance (denominator of given message topic) and looking up for <code>TopicsContext</code> used for topic registration and message publishing.
+ Sending a message using the <code>TopicsContext</code> involves creating a <code>TopicKey</code> instance (denominator of given message topic) and looking up the <code>TopicsContext</code> used for topic registration and message publishing.
</para>
<important id="important-Component_Reference-a4jpush-Limitations_for_using_TopicsContext_interface">
- <title>Limitations for using <code>TopicsContext</code> interface</title>
+ <title>Limitations for using the <code>TopicsContext</code> interface</title>
<para>
- For using <code>TopicsContext</code> is it necessary having <code>FacesContext</code> instance available in current thread.
+ Using the <code>TopicsContext</code> requires the <code>FacesContext</code> instance to be available in the current thread.
</para>
<para>
- The method <code>TopicsContext.lookup()</code> can be called only in context of web application class loader (<code>ServiceTracker</code> needs to be accessible for given application).
+ The method <code>TopicsContext.lookup()</code> can be called only in the context of a web application class loader (<code>ServiceTracker</code> needs to be accessible for given application).
</para>
</important>
</section>
@@ -753,20 +753,20 @@
<section id="sect-Component_Reference-a4jpush-Push_CDI_Integration">
<title>Integrating Push with CDI events</title>
<para>
- Second alternative way of producing messages is using CDI Events mechanism.
+ A second alternative way of producing messages is to use the CDI Event mechanism.
</para>
<para>
- Push notifications can be produced by annotating CDI event with <code>@Push</code> annotation, which specifies end-point (topic name).
+ Push notifications can be produced by annotating a CDI event injection point with the <code>@Push</code> annotation, which specifies an end-point (topic name).
</para>
<para>
- Payload of the message is the serialized object sent using CDI event interface (<code>Event.fire(T object)</code>).
+ The payload of the message is the serialized object sent using the CDI event interface (<code>Event.fire(T object)</code>).
</para>
<programlisting language="Java" role="JAVA"><xi:include parse="text" href="extras/exam-Component_Reference-a4jpush-Integrating_Push_with_CDI_events.xml_sample" xmlns:xi="http://www.w3.org/2001/XInclude" /></programlisting>
<important>
- For using CDI events for producing push notifications, same <link linkend="important-Component_Reference-a4jpush-Limitations_for_using_TopicsContext_interface">limitations</link> applies as for using <code>TopicsContext</code> interface.
+ When using CDI events for producing push notifications, the same <link linkend="important-Component_Reference-a4jpush-Limitations_for_using_TopicsContext_interface">limitations</link> apply as for using the <code>TopicsContext</code> interface.
</important>
<note>
- Associations for CDI events and their handlers are constructed in application deployment. Therefore topics can't be dynamically associated using <code>Event.select(Annotation...)</code> and <code>@Push</code> CDI qualifier.
+ Associations for CDI events and their handlers are constructed during application deployment. Therefore topics can't be dynamically associated using <code>Event.select(Annotation...)</code> and the <code>@Push</code> CDI qualifier.
</note>
</section>
13 years, 1 month
JBoss Rich Faces SVN: r22973 - in modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest: richAutocomplete and 1 other directory.
by richfaces-svn-commits@lists.jboss.org
Author: ppitonak(a)redhat.com
Date: 2011-11-22 10:00:50 -0500 (Tue, 22 Nov 2011)
New Revision: 22973
Modified:
modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/model/Autocomplete.java
modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richAutocomplete/TestAutocompleteAttributes.java
Log:
test for rich:autocomplete fixed
Modified: modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/model/Autocomplete.java
===================================================================
--- modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/model/Autocomplete.java 2011-11-22 12:18:09 UTC (rev 22972)
+++ modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/model/Autocomplete.java 2011-11-22 15:00:50 UTC (rev 22973)
@@ -42,141 +42,138 @@
* @version $Revision$
*/
public class Autocomplete implements Model {
-
+
private static final String KEY_UP = "38";
private static final String KEY_DOWN = "40";
AjaxSelenium selenium = AjaxSeleniumProxy.getInstance();
JQueryLocator input = pjq("input.rf-au-inp[id$=autocompleteInput]");
-
+
JQueryLocator items = jq("div.rf-au-lst-cord[id$=autocompleteList] div[id$=autocompleteItems]");
JQueryLocator selection = items.getDescendant(jq("div.rf-au-itm-sel"));
JQueryLocator option = jq("div.rf-au-itm");
JQueryLocator labeledOption = jq("div.rf-au-opt:contains('{0}')");
-
+
public void typeKeys(String keys) {
- for (int i = 0; i < keys.length(); i++) {
- final String key = String.valueOf(keys.charAt(i));
+ for (int i = 0; i < keys.length(); i++) {
+ final String key = String.valueOf(keys.charAt(i));
selenium.focus(input);
selenium.keyPress(input, key);
- selenium.fireEvent(input, Event.KEYDOWN);
- }
+ selenium.fireEvent(input, Event.KEYDOWN);
+ }
}
-
+
public void type(String value) {
- selenium.type(input, value);
+ selenium.type(input, value);
selenium.fireEvent(input, Event.BLUR);
}
-
+
public void confirmByKeys() {
- pressEnter();
+ selenium.fireEvent(input, Event.FOCUS);
+ pressEnter();
}
-
+
public void selectByKeys(String label) {
- int labeledIndex = getLabeledOptionIndex(label);
- while (getSelectedOptionIndex() < labeledIndex) {
+ int labeledIndex = getLabeledOptionIndex(label);
+ while (getSelectedOptionIndex() < labeledIndex) {
pressDown();
- }
- while (getSelectedOptionIndex() > labeledIndex) {
+ }
+ while (getSelectedOptionIndex() > labeledIndex) {
pressUp();
- }
+ }
}
-
+
public boolean isLabeledOptionAvailable(String label) {
- return selenium.isElementPresent(getLabeledOption(label));
+ return selenium.isElementPresent(getLabeledOption(label));
}
-
+
public int getLabeledOptionIndex(String label) {
- String index = selenium.getEval(jqScript(getLabeledOption(label),
- "index()"));
- return Integer.valueOf(index);
+ String index = selenium.getEval(jqScript(getLabeledOption(label), "index()"));
+ return Integer.valueOf(index);
}
-
+
public int getSelectedOptionIndex() {
- JavaScript script = jqScript(option, "index({0})").parametrize(
- jqObject(selection));
- String index = selenium.getEval(script);
- return Integer.valueOf(index);
+ JavaScript script = jqScript(option, "index({0})").parametrize(jqObject(selection));
+ String index = selenium.getEval(script);
+ return Integer.valueOf(index);
}
-
+
public String getSelectedOptionText() {
- return selenium.getText(selection);
+ return selenium.getText(selection);
}
-
+
public String getInputText() {
- return selenium.getValue(input);
+ return selenium.getValue(input);
}
-
+
public void clearInputValue() {
selenium.type(input, "");
- // selenium.fireEvent(input, Event.KEYUP);
- // selenium.fireEvent(input, Event.BLUR);
selenium.focus(input);
}
-
+
private JQueryLocator getLabeledOption(String label) {
- return labeledOption.format(label);
+ return labeledOption.format(label);
}
-
+
public void pressBackspace() {
- selenium.keyPressNative(String.valueOf(KeyEvent.VK_BACK_SPACE));
+ selenium.keyPressNative(String.valueOf(KeyEvent.VK_BACK_SPACE));
}
-
+
public void pressUp() {
- selenium.keyDown(input, KEY_UP);
+ selenium.keyDown(input, KEY_UP);
}
-
+
public void pressDown() {
- selenium.keyDown(input, KEY_DOWN);
+ selenium.keyDown(input, KEY_DOWN);
}
-
+
public void pressEnter() {
- selenium.keyDownNative(Integer.toString(KeyEvent.VK_ENTER));
+ selenium.keyPressNative(Integer.toString(KeyEvent.VK_ENTER));
}
-
+
public void pressLeft() {
- selenium.keyPressNative(String.valueOf(KeyEvent.VK_LEFT));
+ selenium.keyPressNative(String.valueOf(KeyEvent.VK_LEFT));
}
-
+
public void pressRight() {
- selenium.keyPressNative(String.valueOf(KeyEvent.VK_RIGHT));
+ selenium.keyPressNative(String.valueOf(KeyEvent.VK_RIGHT));
}
-
+
public void pressDelete() {
- selenium.keyPressNative(String.valueOf(KeyEvent.VK_DELETE));
+ selenium.keyPressNative(String.valueOf(KeyEvent.VK_DELETE));
}
-
+
public void textSelectionLeft(int size) {
- selenium.keyDownNative(String.valueOf(KeyEvent.VK_SHIFT));
- for (int i = 0; i < size; i++) {
- selenium.keyPressNative(String.valueOf(KeyEvent.VK_LEFT));
- }
- selenium.keyUpNative(String.valueOf(KeyEvent.VK_SHIFT));
+ selenium.keyDownNative(String.valueOf(KeyEvent.VK_SHIFT));
+ for (int i = 0; i < size; i++) {
+ selenium.keyPressNative(String.valueOf(KeyEvent.VK_LEFT));
+ }
+ selenium.keyUpNative(String.valueOf(KeyEvent.VK_SHIFT));
}
-
+
public void textSelectionRight(int size) {
- selenium.keyDownNative(String.valueOf(KeyEvent.VK_SHIFT));
- for (int i = 0; i < size; i++) {
- selenium.keyPressNative(String.valueOf(KeyEvent.VK_RIGHT));
- }
- selenium.keyUpNative(String.valueOf(KeyEvent.VK_SHIFT));
+ selenium.keyDownNative(String.valueOf(KeyEvent.VK_SHIFT));
+ for (int i = 0; i < size; i++) {
+ selenium.keyPressNative(String.valueOf(KeyEvent.VK_RIGHT));
+ }
+ selenium.keyUpNative(String.valueOf(KeyEvent.VK_SHIFT));
}
-
+
public void textSelectAll() {
- selenium.keyDownNative(String.valueOf(KeyEvent.VK_CONTROL));
- selenium.keyPressNative(String.valueOf(KeyEvent.VK_A));
- selenium.keyDownNative(String.valueOf(KeyEvent.VK_CONTROL));
+ selenium.keyDownNative(String.valueOf(KeyEvent.VK_CONTROL));
+ selenium.keyPressNative(String.valueOf(KeyEvent.VK_A));
+ selenium.keyDownNative(String.valueOf(KeyEvent.VK_CONTROL));
}
-
- public boolean isCompletionVisible() {
- if (!selenium.isElementPresent(items)) {
- return false;
- }
- return selenium.isVisible(items);
+
+ public boolean isCompletionVisible() {
+ if (!selenium.isElementPresent(items)) {
+ return false;
+ }
+ return selenium.isVisible(items);
}
-
+
public void waitForCompletionVisible() {
- WAIT_GUI.until(ELEMENT_PRESENT.locator(option));
+ WAIT_GUI.until(ELEMENT_PRESENT.locator(option));
}
}
Modified: modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richAutocomplete/TestAutocompleteAttributes.java
===================================================================
--- modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richAutocomplete/TestAutocompleteAttributes.java 2011-11-22 12:18:09 UTC (rev 22972)
+++ modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richAutocomplete/TestAutocompleteAttributes.java 2011-11-22 15:00:50 UTC (rev 22973)
@@ -37,6 +37,7 @@
private static final JQueryLocator PHASE_FORMAT = jq("div#phasesPanel li:eq({0})");
private static final String PHASE_LISTENER_LOG_FORMAT = "*1 value changed: {0} -> {1}";
+
@Override
public URL getTestUrl() {
return buildUrl(contextPath, "faces/components/richAutocomplete/autocomplete.xhtml");
@@ -44,23 +45,25 @@
@Test
public void testValueChangeListener() {
- getAutocomplete().type("something");
+ getAutocomplete().typeKeys("something");
getAutocomplete().confirmByKeys();
// valueChangeListener output as 4th record
- waitGui
- .failWith("Expected <" + format(PHASE_LISTENER_LOG_FORMAT, "null", "something") + ">, found <" + selenium.getText(PHASE_FORMAT.format(3)) + ">")
- .until(textEquals.locator(PHASE_FORMAT.format(3)).text(
- format(PHASE_LISTENER_LOG_FORMAT, "null", "something"))
- );
-
- getAutocomplete().type("something else");
+ waitFor(2000);
+ waitGui.failWith(
+ "Expected <" + format(PHASE_LISTENER_LOG_FORMAT, "null", "something") + ">, found <"
+ + selenium.getText(PHASE_FORMAT.format(3)) + ">").until(
+ textEquals.locator(PHASE_FORMAT.format(3)).text(format(PHASE_LISTENER_LOG_FORMAT, "null", "something")));
+
+ getAutocomplete().clearInputValue();
+ getAutocomplete().typeKeys("something else");
getAutocomplete().confirmByKeys();
// valueChangeListener output as 4th record
- waitGui
- .failWith("Expected <" + format(PHASE_LISTENER_LOG_FORMAT, "something", "something else") + ">, found <" + selenium.getText(PHASE_FORMAT.format(3)) + ">")
- .until(textEquals.locator(PHASE_FORMAT.format(3)).text(
- format(PHASE_LISTENER_LOG_FORMAT, "something", "something else"))
- );
+ waitFor(2000);
+ waitGui.failWith(
+ "Expected <" + format(PHASE_LISTENER_LOG_FORMAT, "something", "something else") + ">, found <"
+ + selenium.getText(PHASE_FORMAT.format(3)) + ">").until(
+ textEquals.locator(PHASE_FORMAT.format(3)).text(
+ format(PHASE_LISTENER_LOG_FORMAT, "something", "something else")));
}
-
+
}
13 years, 1 month
JBoss Rich Faces SVN: r22972 - modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/a4jStatus.
by richfaces-svn-commits@lists.jboss.org
Author: ppitonak(a)redhat.com
Date: 2011-11-22 07:18:09 -0500 (Tue, 22 Nov 2011)
New Revision: 22972
Modified:
modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/a4jStatus/TestInstantAttributes.java
Log:
tests for a4j:status fixed
Modified: modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/a4jStatus/TestInstantAttributes.java
===================================================================
--- modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/a4jStatus/TestInstantAttributes.java 2011-11-22 11:58:48 UTC (rev 22971)
+++ modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/a4jStatus/TestInstantAttributes.java 2011-11-22 12:18:09 UTC (rev 22972)
@@ -33,7 +33,8 @@
import org.jboss.test.selenium.encapsulated.JavaScript;
import org.jboss.test.selenium.request.RequestType;
import org.richfaces.tests.metamer.ftest.annotations.IssueTracking;
-import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
@@ -49,11 +50,16 @@
return buildUrl(contextPath, "faces/components/a4jStatus/instantAttributes.xhtml");
}
- @BeforeMethod
+ @BeforeClass
public void setupAttributes() {
statusAttributes.setRequestType(RequestType.XHR);
}
+ @AfterClass
+ public void teardownAttributes() {
+ statusAttributes.setRequestType(RequestType.HTTP);
+ }
+
@Test
@IssueTracking("https://issues.jboss.org/browse/RFPL-1516")
public void testOnStart() {
13 years, 1 month
JBoss Rich Faces SVN: r22971 - modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn.
by richfaces-svn-commits@lists.jboss.org
Author: ppitonak(a)redhat.com
Date: 2011-11-22 06:58:48 -0500 (Tue, 22 Nov 2011)
New Revision: 22971
Modified:
modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/AbstractColumnSortingTest.java
modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnSimple.java
Log:
test for rich:columnGroup fixed
Modified: modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/AbstractColumnSortingTest.java
===================================================================
--- modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/AbstractColumnSortingTest.java 2011-11-22 11:48:37 UTC (rev 22970)
+++ modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/AbstractColumnSortingTest.java 2011-11-22 11:58:48 UTC (rev 22971)
@@ -67,5 +67,7 @@
assertEquals(actualCapital.getName(), expectedCapital.getName());
assertEquals(actualCapital.getState(), expectedCapital.getState());
}
+
+ columnAttributes.setRequestType(RequestType.HTTP);
}
}
Modified: modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnSimple.java
===================================================================
--- modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnSimple.java 2011-11-22 11:48:37 UTC (rev 22970)
+++ modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnSimple.java 2011-11-22 11:58:48 UTC (rev 22971)
@@ -56,8 +56,6 @@
assertEquals(selenium.getAttribute(headerCell(1, 1).getAttribute(COLSPAN)), "2");
assertEquals(selenium.getAttribute(headerCell(2, 2).getAttribute(ROWSPAN)), "2");
-
- columnAttributes.setRequestType(RequestType.HTTP);
}
@Test
13 years, 1 month
JBoss Rich Faces SVN: r22970 - modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn.
by richfaces-svn-commits@lists.jboss.org
Author: ppitonak(a)redhat.com
Date: 2011-11-22 06:48:37 -0500 (Tue, 22 Nov 2011)
New Revision: 22970
Modified:
modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnComparator.java
modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnFiltering.java
modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnSimple.java
modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnSorting.java
Log:
tests for rich:column fixed
Modified: modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnComparator.java
===================================================================
--- modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnComparator.java 2011-11-22 10:58:06 UTC (rev 22969)
+++ modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnComparator.java 2011-11-22 11:48:37 UTC (rev 22970)
@@ -31,7 +31,6 @@
* @author <a href="mailto:lfryc@redhat.com">Lukas Fryc</a>
* @version $Revision$
*/
-@Test
public class TestColumnComparator extends AbstractColumnSortingTest {
@Override
Modified: modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnFiltering.java
===================================================================
--- modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnFiltering.java 2011-11-22 10:58:06 UTC (rev 22969)
+++ modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnFiltering.java 2011-11-22 11:48:37 UTC (rev 22970)
@@ -49,12 +49,12 @@
JQueryLocator tableWithFilter = pjq("table.rf-dt[id$=richDataTable1]");
JQueryLocator tableWithFilterExpression = pjq("table.rf-dt[id$=richDataTable2]");
RichColumnBean richColumnBean = new RichColumnBean();
-
+
@Override
public URL getTestUrl() {
return buildUrl(contextPath, "faces/components/richColumn/filtering.xhtml");
}
-
+
@Test
public void testFilterAttribute() {
model.setRoot(tableWithFilter);
@@ -88,7 +88,7 @@
assertEqualsCapitals(actualCapitals, expectedCapitals);
}
}
-
+
private void assertEqualsCapitals(Collection<Capital> actualCapitals, Collection<Capital> expectedCapitals) {
assertEquals(actualCapitals.size(), expectedCapitals.size());
Modified: modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnSimple.java
===================================================================
--- modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnSimple.java 2011-11-22 10:58:06 UTC (rev 22969)
+++ modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnSimple.java 2011-11-22 11:48:37 UTC (rev 22970)
@@ -31,6 +31,7 @@
import java.net.URL;
import org.jboss.test.selenium.locator.JQueryLocator;
+import org.jboss.test.selenium.request.RequestType;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -55,6 +56,8 @@
assertEquals(selenium.getAttribute(headerCell(1, 1).getAttribute(COLSPAN)), "2");
assertEquals(selenium.getAttribute(headerCell(2, 2).getAttribute(ROWSPAN)), "2");
+
+ columnAttributes.setRequestType(RequestType.HTTP);
}
@Test
Modified: modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnSorting.java
===================================================================
--- modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnSorting.java 2011-11-22 10:58:06 UTC (rev 22969)
+++ modules/tests/metamer/trunk/ftest-source/src/main/java/org/richfaces/tests/metamer/ftest/richColumn/TestColumnSorting.java 2011-11-22 11:48:37 UTC (rev 22970)
@@ -31,7 +31,6 @@
* @author <a href="mailto:lfryc@redhat.com">Lukas Fryc</a>
* @version $Revision$
*/
-@Test
public class TestColumnSorting extends AbstractColumnSortingTest {
@Override
13 years, 1 month
JBoss Rich Faces SVN: r22969 - in modules/tests/metamer/trunk/application/src/main: webapp/components/a4jCommandButton and 1 other directory.
by richfaces-svn-commits@lists.jboss.org
Author: ppitonak(a)redhat.com
Date: 2011-11-22 05:58:06 -0500 (Tue, 22 Nov 2011)
New Revision: 22969
Added:
modules/tests/metamer/trunk/application/src/main/webapp/components/a4jCommandButton/image.xhtml
Modified:
modules/tests/metamer/trunk/application/src/main/java/org/richfaces/tests/metamer/bean/a4j/A4JCommandButtonBean.java
modules/tests/metamer/trunk/application/src/main/webapp/components/a4jCommandButton/list.xhtml
Log:
added new sample for a4j:commandButton
Modified: modules/tests/metamer/trunk/application/src/main/java/org/richfaces/tests/metamer/bean/a4j/A4JCommandButtonBean.java
===================================================================
--- modules/tests/metamer/trunk/application/src/main/java/org/richfaces/tests/metamer/bean/a4j/A4JCommandButtonBean.java 2011-11-22 10:01:57 UTC (rev 22968)
+++ modules/tests/metamer/trunk/application/src/main/java/org/richfaces/tests/metamer/bean/a4j/A4JCommandButtonBean.java 2011-11-22 10:58:06 UTC (rev 22969)
@@ -62,8 +62,6 @@
attributes.setAttribute("action", "first6CharsAction");
attributes.setAttribute("actionListener", "toUpperCaseActionListener");
- attributes.setAttribute("disabled", false);
- attributes.get("disabled").setType(Boolean.class);
attributes.setAttribute("render", "output1 output2 output3");
attributes.setAttribute("rendered", true);
attributes.setAttribute("value", "command button");
Added: modules/tests/metamer/trunk/application/src/main/webapp/components/a4jCommandButton/image.xhtml
===================================================================
--- modules/tests/metamer/trunk/application/src/main/webapp/components/a4jCommandButton/image.xhtml (rev 0)
+++ modules/tests/metamer/trunk/application/src/main/webapp/components/a4jCommandButton/image.xhtml 2011-11-22 10:58:06 UTC (rev 22969)
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:a4j="http://richfaces.org/a4j"
+ xmlns:metamer="http://java.sun.com/jsf/composite/metamer">
+
+ <!--
+JBoss, Home of Professional Open Source
+Copyright 2010-2011, Red Hat, Inc. and individual contributors
+by the @authors tag. See the copyright.txt in the distribution for a
+full listing of individual contributors.
+
+This 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 2.1 of
+the License, or (at your option) any later version.
+
+This software 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 this software; if not, write to the Free
+Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ -->
+
+ <ui:composition template="/templates/template.xhtml">
+
+ <ui:define name="view">
+ <f:metadata>
+ <f:viewParam name="templates" value="#{templateBean.templates}">
+ <f:converter converterId="templatesListConverter" />
+ </f:viewParam>
+ </f:metadata>
+ </ui:define>
+
+ <ui:define name="component">
+
+ <h:panelGrid id="panel" columns="2">
+ <h:inputText id="input" value="#{a4jButtonBean.input}" />
+
+ <a4j:commandButton id="a4jCommandButton"
+ action="#{a4jButtonBean.attributes.action}"
+ actionListener="#{a4jButtonBean.attributes.actionListener}"
+ bypassUpdates="#{a4jButtonBean.attributes['bypassUpdates'].value}"
+ data="#{a4jButtonBean.attributes['data'].value}"
+ disabled="#{a4jButtonBean.attributes['disabled'].value}"
+ execute="#{a4jButtonBean.attributes['execute'].value}"
+ image="/resources/images/star.png"
+ immediate="#{a4jButtonBean.attributes['immediate'].value}"
+ limitRender="#{a4jButtonBean.attributes['limitRender'].value}"
+ onbeforedomupdate="#{a4jButtonBean.attributes['onbeforedomupdate'].value}"
+ onbegin="#{a4jButtonBean.attributes['onbegin'].value}"
+ onclick="#{a4jButtonBean.attributes['onclick'].value}"
+ oncomplete="#{a4jButtonBean.attributes['oncomplete'].value}"
+ ondblclick="#{a4jButtonBean.attributes['ondblclick'].value}"
+ onkeydown="#{a4jButtonBean.attributes['onkeydown'].value}"
+ onkeypress="#{a4jButtonBean.attributes['onkeypress'].value}"
+ onkeyup="#{a4jButtonBean.attributes['onkeyup'].value}"
+ onmousedown="#{a4jButtonBean.attributes['onmousedown'].value}"
+ onmousemove="#{a4jButtonBean.attributes['onmousemove'].value}"
+ onmouseout="#{a4jButtonBean.attributes['onmouseout'].value}"
+ onmouseover="#{a4jButtonBean.attributes['onmouseover'].value}"
+ onmouseup="#{a4jButtonBean.attributes['onmouseup'].value}"
+ render="#{a4jButtonBean.attributes['render'].value}"
+ rendered="#{a4jButtonBean.attributes['rendered'].value}"
+ status="#{a4jButtonBean.attributes['status'].value}"
+ style="#{a4jButtonBean.attributes['style'].value}"
+ styleClass="#{a4jButtonBean.attributes['styleClass'].value}"
+ title="#{a4jButtonBean.attributes['title'].value}"
+ type="#{a4jButtonBean.attributes['type'].value}"
+ value="#{a4jButtonBean.attributes['value'].value}"
+ />
+ </h:panelGrid>
+
+ <h:panelGrid id="outputs" columns="2">
+ output: <h:outputText value="#{a4jButtonBean.input}" id="output1" />
+ action: <h:outputText value="#{a4jButtonBean.input2}" id="output2" />
+ action listener:<h:outputText value="#{a4jButtonBean.input3}" id="output3" />
+ </h:panelGrid>
+ </ui:define>
+
+ <ui:define name="outOfTemplateAfter">
+ <metamer:attributes value="#{a4jButtonBean.attributes}" id="attributes" render="log"/>
+ </ui:define>
+
+ </ui:composition>
+</html>
\ No newline at end of file
Modified: modules/tests/metamer/trunk/application/src/main/webapp/components/a4jCommandButton/list.xhtml
===================================================================
--- modules/tests/metamer/trunk/application/src/main/webapp/components/a4jCommandButton/list.xhtml 2011-11-22 10:01:57 UTC (rev 22968)
+++ modules/tests/metamer/trunk/application/src/main/webapp/components/a4jCommandButton/list.xhtml 2011-11-22 10:58:06 UTC (rev 22969)
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
- xmlns:metamer="http://java.sun.com/jsf/composite/metamer">
+ xmlns:metamer="http://java.sun.com/jsf/composite/metamer">
- <!--
+<!--
JBoss, Home of Professional Open Source
Copyright 2010-2011, Red Hat, Inc. and individual contributors
by the @authors tag. See the copyright.txt in the distribution for a
@@ -25,18 +25,22 @@
02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
- <ui:composition template="/templates/list.xhtml">
+<ui:composition template="/templates/list.xhtml">
- <ui:define name="pageTitle">A4J Command Button</ui:define>
+ <ui:define name="pageTitle">A4J Command Button</ui:define>
- <ui:define name="links">
+ <ui:define name="links">
- <metamer:testPageLink id="simple" outcome="simple" value="Simple">
+ <metamer:testPageLink id="simple" outcome="simple" value="Simple">
Simple page that contains <b>a4j:commandButton</b> and input boxes for all its attributes.
- </metamer:testPageLink>
+ </metamer:testPageLink>
- </ui:define>
+ <metamer:testPageLink id="image" outcome="image" value="Image">
+ Simple page that contains <b>a4j:commandButton</b> with image and input boxes for all its attributes.
+ </metamer:testPageLink>
- </ui:composition>
+ </ui:define>
+</ui:composition>
+
</html>
13 years, 1 month
JBoss Rich Faces SVN: r22968 - modules/docs/trunk/Migration_Guide/src/main/docbook/en-US.
by richfaces-svn-commits@lists.jboss.org
Author: lfryc(a)redhat.com
Date: 2011-11-22 05:01:57 -0500 (Tue, 22 Nov 2011)
New Revision: 22968
Modified:
modules/docs/trunk/Migration_Guide/src/main/docbook/en-US/Changes_and_new_features.xml
Log:
RFPL-1845: Migration-Guide: remove section 3.6.1. rich:busyBehavior
Modified: modules/docs/trunk/Migration_Guide/src/main/docbook/en-US/Changes_and_new_features.xml
===================================================================
--- modules/docs/trunk/Migration_Guide/src/main/docbook/en-US/Changes_and_new_features.xml 2011-11-22 06:22:20 UTC (rev 22967)
+++ modules/docs/trunk/Migration_Guide/src/main/docbook/en-US/Changes_and_new_features.xml 2011-11-22 10:01:57 UTC (rev 22968)
@@ -1020,16 +1020,9 @@
<section id="sect-Migration_Guide-Changes_and_new_features-New_components_and_behaviors">
<title>New components and behaviors</title>
<para>
- Several new components and behaviors have been added to <productname>RichFaces</productname> 4, some of which replace the functionality of deprecated elements. For full details on how to use these new components and behaviors, refer to the <citetitle>Developer Guide</citetitle>.
+ Several new components and behaviors have been added to <productname>RichFaces</productname> 4, some of which replace the functionality of deprecated elements. For full details on how to use these new components and behaviors, refer to the <citetitle>Component Reference</citetitle>.
</para>
- <section id="sect-Migration_Guide-New_components_and_behaviors-richbusyBehavior">
- <title><sgmltag><rich:busyBehavior></sgmltag></title>
- <para>
- The <sgmltag><rich:busyBehavior></sgmltag> component is used when heavy processing takes place to provide feedback to the user. It can be attached to a component's events to trigger appropriate messages.
- </para>
- </section>
-
<section id="sect-Migration_Guide-New_components_and_behaviors-richtogglePanelItem">
<title><sgmltag><rich:togglePanelItem></sgmltag></title>
<para>
13 years, 1 month
JBoss Rich Faces SVN: r22967 - in sandbox/trunk/ui/schedule: demo/src/main/webapp/pages and 4 other directories.
by richfaces-svn-commits@lists.jboss.org
Author: blabno
Date: 2011-11-22 01:22:20 -0500 (Tue, 22 Nov 2011)
New Revision: 22967
Modified:
sandbox/trunk/ui/schedule/demo/src/main/java/org/richfaces/schedule/Bean.java
sandbox/trunk/ui/schedule/demo/src/main/java/org/richfaces/schedule/ScheduleTask.java
sandbox/trunk/ui/schedule/demo/src/main/webapp/pages/schedule.xhtml
sandbox/trunk/ui/schedule/ui/pom.xml
sandbox/trunk/ui/schedule/ui/src/main/java/org/richfaces/component/AbstractSchedule.java
sandbox/trunk/ui/schedule/ui/src/main/java/org/richfaces/component/AbstractScheduleItem.java
sandbox/trunk/ui/schedule/ui/src/main/java/org/richfaces/renderkit/ScheduleRendererBase.java
sandbox/trunk/ui/schedule/ui/src/main/resources/META-INF/resources/fullcalendar.css
sandbox/trunk/ui/schedule/ui/src/main/resources/META-INF/resources/fullcalendar.js
sandbox/trunk/ui/schedule/ui/src/main/resources/META-INF/resources/gcal.js
Log:
Upgraded fullcalendar plugin to 1.5.2 and introduced new attributes to scheduleItem: color,backgroundColor,borderColor,textColor.
Modified: sandbox/trunk/ui/schedule/demo/src/main/java/org/richfaces/schedule/Bean.java
===================================================================
--- sandbox/trunk/ui/schedule/demo/src/main/java/org/richfaces/schedule/Bean.java 2011-11-22 06:11:32 UTC (rev 22966)
+++ sandbox/trunk/ui/schedule/demo/src/main/java/org/richfaces/schedule/Bean.java 2011-11-22 06:22:20 UTC (rev 22967)
@@ -123,6 +123,7 @@
public Bean() {
Calendar instance = Calendar.getInstance();
instance.setTime(getInitialDate());
+ final String[] colors = new String[]{"#00ff00", "#ff0000", "#0000ff"};
Random random = new Random();
for (int i = -30; i < 60; i++) {
instance.set(Calendar.HOUR, minTime + random.nextInt(maxTime - minTime));
@@ -131,7 +132,8 @@
Map<String, Object> data = new HashMap<String, Object>();
data.put("category", "category-" + (i % 3));
int taskId = taskIdSequence++;
- allTasks.add(new ScheduleTask("" + taskId, "Title " + taskId, instance.getTime(), instance.getTime(), data));
+
+ allTasks.add(new ScheduleTask("" + taskId, "Title " + taskId, instance.getTime(), instance.getTime(), data, colors[random.nextInt(3)]));
}
}
Modified: sandbox/trunk/ui/schedule/demo/src/main/java/org/richfaces/schedule/ScheduleTask.java
===================================================================
--- sandbox/trunk/ui/schedule/demo/src/main/java/org/richfaces/schedule/ScheduleTask.java 2011-11-22 06:11:32 UTC (rev 22966)
+++ sandbox/trunk/ui/schedule/demo/src/main/java/org/richfaces/schedule/ScheduleTask.java 2011-11-22 06:22:20 UTC (rev 22967)
@@ -17,6 +17,7 @@
private Boolean editable;
private String url;
private String details;
+ private String color;
public ScheduleTask() {
}
@@ -36,12 +37,13 @@
this.allDay = allDay;
}
- public ScheduleTask(String id, String title, Date start, Date end, Map<String, Object> data) {
+ public ScheduleTask(String id, String title, Date start, Date end, Map<String, Object> data, String color) {
this.id = id;
this.title = title;
this.startDate = start;
this.endDate = end;
this.data = data;
+ this.color = color;
}
public String getId() {
@@ -138,4 +140,12 @@
public void setDetails(String details) {
this.details = details;
}
+
+ public String getColor() {
+ return color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
}
Modified: sandbox/trunk/ui/schedule/demo/src/main/webapp/pages/schedule.xhtml
===================================================================
--- sandbox/trunk/ui/schedule/demo/src/main/webapp/pages/schedule.xhtml 2011-11-22 06:11:32 UTC (rev 22966)
+++ sandbox/trunk/ui/schedule/demo/src/main/webapp/pages/schedule.xhtml 2011-11-22 06:22:20 UTC (rev 22967)
@@ -1,7 +1,9 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core"
- xmlns:h="http://java.sun.com/jsf/html" xmlns:rich="http://richfaces.org/rich" xmlns:schedule="http://richfaces.org/sandbox/schedule">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:h="http://java.sun.com/jsf/html" xmlns:rich="http://richfaces.org/rich"
+ xmlns:schedule="http://richfaces.org/sandbox/schedule">
<head>
<title></title>
</head>
@@ -9,32 +11,52 @@
<!-- mode should passed to this template as param -->
<ui:composition>
<h:form id="f">
- <schedule:schedule id="schedule" widgetVar="schedule" switchType="#{mode}" value="#{myBean.lazyDataModel}" var="event" weekMode="#{myBean.weekMode}"
- height="#{myBean.height}" date="#{myBean.initialDate}" firstDay="#{myBean.firstDay}" showWeekends="#{myBean.showWeekends}"
-
- allDayText="#{myBean.allDayText}" allDayByDefault="#{myBean.allDayByDefault}" allDaySlot="#{myBean.allDaySlot}"
- aspectRatio="#{myBean.aspectRatio}" axisFormat="#{myBean.axisFormat}" contentHeight="#{myBean.contentHeight}"
+ <schedule:schedule id="schedule" widgetVar="schedule" switchType="#{mode}" value="#{myBean.lazyDataModel}"
+ var="event" weekMode="#{myBean.weekMode}"
+ height="#{myBean.height}" date="#{myBean.initialDate}" firstDay="#{myBean.firstDay}"
+ showWeekends="#{myBean.showWeekends}"
+ eventColor="#00ff00" eventBorderColor="#ffff00" eventBackgroundColor="#ff00ff"
+ eventTextColor="#ff55ff"
+ allDayText="#{myBean.allDayText}" allDayByDefault="#{myBean.allDayByDefault}"
+ allDaySlot="#{myBean.allDaySlot}"
+ aspectRatio="#{myBean.aspectRatio}" axisFormat="#{myBean.axisFormat}"
+ contentHeight="#{myBean.contentHeight}"
defaultEventMinutes="#{myBean.defaultEventMinutes}" dragOpacity="#{myBean.dragOpacity}"
- dragRevertDuration="#{myBean.dragRevertDuration}" editable="#{myBean.editable}" selectable="#{myBean.selectable}"
- selectHelper="#{myBean.selectHelper}" unselectAuto="#{myBean.unselectAuto}" unselectCancel="#{myBean.unselectCancel}"
- firstHour="#{myBean.firstHour}" headerCenter="#{myBean.headerCenter}" headerLeft="#{myBean.headerLeft}"
- headerRight="#{myBean.headerRight}" isRTL="#{myBean.isRTL}" maxTime="#{myBean.maxTime}" minTime="#{myBean.minTime}"
+ dragRevertDuration="#{myBean.dragRevertDuration}" editable="#{myBean.editable}"
+ selectable="#{myBean.selectable}"
+ selectHelper="#{myBean.selectHelper}" unselectAuto="#{myBean.unselectAuto}"
+ unselectCancel="#{myBean.unselectCancel}"
+ firstHour="#{myBean.firstHour}" headerCenter="#{myBean.headerCenter}"
+ headerLeft="#{myBean.headerLeft}"
+ headerRight="#{myBean.headerRight}" isRTL="#{myBean.isRTL}" maxTime="#{myBean.maxTime}"
+ minTime="#{myBean.minTime}"
slotMinutes="#{myBean.slotMinutes}" view="#{myBean.view}"
- columnFormat="#{myBean.columnFormat}" titleFormat="#{myBean.titleFormat}" timeFormat="#{myBean.timeFormat}"
+ columnFormat="#{myBean.columnFormat}" titleFormat="#{myBean.titleFormat}"
+ timeFormat="#{myBean.timeFormat}"
- itemResizeListener="#{myBean.taskResized}" itemMoveListener="#{myBean.taskMoved}" itemSelectListener="#{myBean.taskSelected}"
- viewChangeListener="#{myBean.viewChanged}" dateRangeChangeListener="#{myBean.dateRangeChanged}"
- dateRangeSelectListener="#{myBean.dateRangeSelected}" dateSelectListener="#{myBean.dateSelected}"
+ itemResizeListener="#{myBean.taskResized}" itemMoveListener="#{myBean.taskMoved}"
+ itemSelectListener="#{myBean.taskSelected}"
+ viewChangeListener="#{myBean.viewChanged}"
+ dateRangeChangeListener="#{myBean.dateRangeChanged}"
+ dateRangeSelectListener="#{myBean.dateRangeSelected}"
+ dateSelectListener="#{myBean.dateSelected}"
ondaterangeselect="#{rich:component('schedule')}.unselect()">
- <schedule:scheduleItem eventId="#{event.id}" startDate="#{event.startDate}" title="#{event.title}" endDate="#{event.endDate}"
- allDay="#{event.allDay}" styleClass="#{event.id == 1 ? 'first' : null}" data="#{event.data}"/>
- <schedule:scheduleMonthView weekMode="#{myBean.weekMode}" titleFormat="MM yy" timeFormat="h:m" columnFormat="dddd" dragOpacity=".1"/>
- <schedule:scheduleAgendaDayView titleFormat="d MMM yy" timeFormat="hh:m" columnFormat="ddd" dragOpacity=".3"/>
- <schedule:scheduleBasicDayView titleFormat="dd MMM yy" timeFormat="hh:mm" columnFormat="aaa ddd" dragOpacity=".5"/>
- <schedule:scheduleAgendaWeekView titleFormat="dd MMM yy" timeFormat="hh:mm" columnFormat="aaa ddd" dragOpacity=".7"/>
- <schedule:scheduleBasicWeekView titleFormat="bw dd MMM yy" timeFormat="bw hh:mm" columnFormat="bw ddd" dragOpacity=".9"/>
+ <schedule:scheduleItem eventId="#{event.id}" startDate="#{event.startDate}" title="#{event.title}"
+ endDate="#{event.endDate}"
+ allDay="#{event.allDay}" styleClass="#{event.id == 1 ? 'first' : null}"
+ data="#{event.data}" color="#{event.color}"/>
+ <schedule:scheduleMonthView weekMode="#{myBean.weekMode}" titleFormat="MM yy" timeFormat="h:m"
+ columnFormat="dddd" dragOpacity=".1"/>
+ <schedule:scheduleAgendaDayView titleFormat="d MMM yy" timeFormat="hh:m" columnFormat="ddd"
+ dragOpacity=".3"/>
+ <schedule:scheduleBasicDayView titleFormat="dd MMM yy" timeFormat="hh:mm" columnFormat="aaa ddd"
+ dragOpacity=".5"/>
+ <schedule:scheduleAgendaWeekView titleFormat="dd MMM yy" timeFormat="hh:mm" columnFormat="aaa ddd"
+ dragOpacity=".7"/>
+ <schedule:scheduleBasicWeekView titleFormat="bw dd MMM yy" timeFormat="bw hh:mm" columnFormat="bw ddd"
+ dragOpacity=".9"/>
<!--
TODO uncomment this
<schedule:itemSelectedListener binding="#{myBean.additionalListener}"/>
Modified: sandbox/trunk/ui/schedule/ui/pom.xml
===================================================================
--- sandbox/trunk/ui/schedule/ui/pom.xml 2011-11-22 06:11:32 UTC (rev 22966)
+++ sandbox/trunk/ui/schedule/ui/pom.xml 2011-11-22 06:22:20 UTC (rev 22967)
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
-<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
@@ -33,6 +34,7 @@
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<scope>provided</scope>
+ <version>1.2</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
Modified: sandbox/trunk/ui/schedule/ui/src/main/java/org/richfaces/component/AbstractSchedule.java
===================================================================
--- sandbox/trunk/ui/schedule/ui/src/main/java/org/richfaces/component/AbstractSchedule.java 2011-11-22 06:11:32 UTC (rev 22966)
+++ sandbox/trunk/ui/schedule/ui/src/main/java/org/richfaces/component/AbstractSchedule.java 2011-11-22 06:22:20 UTC (rev 22967)
@@ -597,9 +597,9 @@
};
((ExtendedDataModel) dataModel).walk(getFacesContext(), visitor, new DateRange(startDate, endDate), null);
}
- ELContext elContext = getFacesContext().getELContext();
+ ELContext elContext = (ELContext) getFacesContext().getELContext();
ValueExpression valueExpression = getFacesContext().getApplication().getExpressionFactory()
- .createValueExpression(getFacesContext().getELContext(), "#{" + getVar() + "}", Object.class);
+ .createValueExpression(elContext, "#{" + getVar() + "}", Object.class);
List<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
for (int i = 0; i < dataModel.getRowCount(); i++) {
dataModel.setRowIndex(i);
@@ -633,6 +633,18 @@
if (item.getData() != null) {
firstDataElement.put("data", item.getData());
}
+ if (item.getColor() != null) {
+ firstDataElement.put("color", item.getColor());
+ }
+ if (item.getBackgroundColor() != null) {
+ firstDataElement.put("backgroundColor", item.getBackgroundColor());
+ }
+ if (item.getBorderColor() != null) {
+ firstDataElement.put("borderColor", item.getBorderColor());
+ }
+ if (item.getTextColor() != null) {
+ firstDataElement.put("textColor", item.getTextColor());
+ }
data.add(firstDataElement);
}
}
@@ -715,6 +727,18 @@
@Attribute(defaultValue = "" + DEFAULT_UNSELECT_AUTO)
public abstract Boolean isUnselectAuto();
+ @Attribute
+ public abstract String getEventColor();
+
+ @Attribute
+ public abstract String getEventBackgroundColor();
+
+ @Attribute
+ public abstract String getEventBorderColor();
+
+ @Attribute
+ public abstract String getEventTextColor();
+
public void setDataModel(DataModel model) {
this.model = model;
}
Modified: sandbox/trunk/ui/schedule/ui/src/main/java/org/richfaces/component/AbstractScheduleItem.java
===================================================================
--- sandbox/trunk/ui/schedule/ui/src/main/java/org/richfaces/component/AbstractScheduleItem.java 2011-11-22 06:11:32 UTC (rev 22966)
+++ sandbox/trunk/ui/schedule/ui/src/main/java/org/richfaces/component/AbstractScheduleItem.java 2011-11-22 06:22:20 UTC (rev 22967)
@@ -66,4 +66,17 @@
@Attribute
public abstract Object getData();
+
+ @Attribute
+ public abstract String getColor();
+
+ @Attribute
+ public abstract String getBackgroundColor();
+
+ @Attribute
+ public abstract String getBorderColor();
+
+ @Attribute
+ public abstract String getTextColor();
+
}
Modified: sandbox/trunk/ui/schedule/ui/src/main/java/org/richfaces/renderkit/ScheduleRendererBase.java
===================================================================
--- sandbox/trunk/ui/schedule/ui/src/main/java/org/richfaces/renderkit/ScheduleRendererBase.java 2011-11-22 06:11:32 UTC (rev 22966)
+++ sandbox/trunk/ui/schedule/ui/src/main/java/org/richfaces/renderkit/ScheduleRendererBase.java 2011-11-22 06:22:20 UTC (rev 22967)
@@ -102,6 +102,10 @@
*/
static {
Map<String, Object> defaults = new HashMap<String, Object>();
+ defaults.put("eventColor", "");
+ defaults.put("eventBackgroundColor", "");
+ defaults.put("eventBorderColor", "");
+ defaults.put("eventTextColor", "");
defaults.put("styleClass", "");
defaults.put("defaultView", AbstractSchedule.DEFAULT_VIEW);
defaults.put("firstDay", AbstractSchedule.DEFAULT_FIRST_DAY - 1);
@@ -332,6 +336,10 @@
addOptionIfSetAndNotDefault("disableDragging", schedule.isDisableDragging(), options);
addOptionIfSetAndNotDefault("disableResizing", schedule.isDisableResizing(), options);
addOptionIfSetAndNotDefault("dragRevertDuration", schedule.getDragRevertDuration(), options);
+ addOptionIfSetAndNotDefault("eventColor", schedule.getEventColor(), options);
+ addOptionIfSetAndNotDefault("eventBackgroundColor", schedule.getEventBackgroundColor(), options);
+ addOptionIfSetAndNotDefault("eventBorderColor", schedule.getEventBorderColor(), options);
+ addOptionIfSetAndNotDefault("eventTextColor", schedule.getEventTextColor(), options);
addOptionHash("dragOpacity", schedule, options);
addOptionHash("titleFormat", schedule, options);
addOptionHash("timeFormat", schedule, options);
Modified: sandbox/trunk/ui/schedule/ui/src/main/resources/META-INF/resources/fullcalendar.css
===================================================================
--- sandbox/trunk/ui/schedule/ui/src/main/resources/META-INF/resources/fullcalendar.css 2011-11-22 06:11:32 UTC (rev 22966)
+++ sandbox/trunk/ui/schedule/ui/src/main/resources/META-INF/resources/fullcalendar.css 2011-11-22 06:22:20 UTC (rev 22967)
@@ -1,21 +1,14 @@
/*
- * FullCalendar v1.4.5 Stylesheet
+ * FullCalendar v1.5.2 Stylesheet
*
- * Feel free to edit this file to customize the look of FullCalendar.
- * When upgrading to newer versions, please upgrade this file as well,
- * porting over any customizations afterwards.
+ * Copyright (c) 2011 Adam Shaw
+ * Dual licensed under the MIT and GPL licenses, located in
+ * MIT-LICENSE.txt and GPL-LICENSE.txt respectively.
*
- * Date: Sun Feb 21 20:30:11 2010 -0800
+ * Date: Sun Aug 21 22:06:09 2011 -0700
*
- * TODO adjust class names to richfaces naming conventions
*/
-.fc,
-.fc .fc-header,
-.fc .fc-content {
- font-size: 1em;
-}
-
.fc {
direction: ltr;
text-align: left;
@@ -26,7 +19,13 @@
border-spacing: 0;
}
-.fc td, .fc th {
+html .fc,
+.fc table {
+ font-size: 1em;
+}
+
+.fc td,
+.fc th {
padding: 0;
vertical-align: top;
}
@@ -34,266 +33,274 @@
/* Header
------------------------------------------------------------------------*/
-table.fc-header {
- width: 100%;
+.fc-header td {
+ white-space: nowrap;
}
.fc-header-left {
width: 25%;
+ text-align: left;
}
-.fc-header-left table {
- float: left;
-}
-
.fc-header-center {
- width: 50%;
text-align: center;
}
-.fc-header-center table {
- margin: 0 auto;
-}
-
.fc-header-right {
width: 25%;
+ text-align: right;
}
-.fc-header-right table {
- float: right;
+.fc-header-title {
+ display: inline-block;
+ vertical-align: top;
}
-.fc-header-title {
+.fc-header-title h2 {
margin-top: 0;
white-space: nowrap;
}
-.fc-header-space {
+.fc .fc-header-space {
padding-left: 10px;
}
-/* right-to-left */
-
-.fc-rtl .fc-header-title {
- direction: rtl;
+.fc-header .fc-button {
+ margin-bottom: 1em;
+ vertical-align: top;
}
-/* Buttons
-------------------------------------------------------------------------*/
+/* buttons edges butting together */
-.fc-header .fc-state-default,
-.fc-header .ui-state-default {
- margin-bottom: 1em;
- cursor: pointer;
+.fc-header .fc-button {
+ margin-right: -1px;
}
-.fc-header .fc-state-default {
- border-width: 1px 0;
- padding: 0 1px;
+.fc-header .fc-corner-right {
+ margin-right: 1px; /* back to normal */
}
-.fc-header .fc-state-default,
-.fc-header .fc-state-default a {
- border-style: solid;
+.fc-header .ui-corner-right {
+ margin-right: 0; /* back to normal */
}
-.fc-header .fc-state-default a {
- display: block;
- border-width: 0 1px;
- margin: 0 -1px;
- width: 100%;
- text-decoration: none;
+/* button layering (for border precedence) */
+
+.fc-header .fc-state-hover,
+.fc-header .ui-state-hover {
+ z-index: 2;
}
-.fc-header .fc-state-default span {
- display: block;
- border-style: solid;
- border-width: 1px 0 1px 1px;
- padding: 3px 5px;
+.fc-header .fc-state-down {
+ z-index: 3;
}
-.fc-header .ui-state-default {
- padding: 4px 6px;
+.fc-header .fc-state-active,
+.fc-header .ui-state-active {
+ z-index: 4;
}
-.fc-header .fc-state-default span,
-.fc-header .ui-state-default span {
- white-space: nowrap;
+/* Content
+------------------------------------------------------------------------*/
+
+.fc-content {
+ clear: both;
}
-/* for adjacent buttons */
+.fc-view {
+ width: 100%; /* needed for view switching (when view is absolute) */
+ overflow: hidden;
+}
-.fc-header .fc-no-right {
- padding-right: 0;
+/* Cell Styles
+------------------------------------------------------------------------*/
+
+.fc-widget-header, /* <th>, usually */
+.fc-widget-content {
+ /* <td>, usually */
+ border: 1px solid #ccc;
}
-.fc-header .fc-no-right a {
- margin-right: 0;
- border-right: 0;
+.fc-state-highlight {
+ /* <td> today cell */
+ /* TODO: add .fc-today to <th> */
+ background: #ffc;
}
-.fc-header .ui-no-right {
- border-right: 0;
+.fc-cell-overlay {
+ /* semi-transparent rectangle while dragging */
+ background: #9cf;
+ opacity: .2;
+ filter: alpha(opacity = 20); /* for IE */
}
-/* for fake rounded corners */
+/* Buttons
+------------------------------------------------------------------------*/
-.fc-header .fc-corner-left {
- margin-left: 1px;
- padding-left: 0;
+.fc-button {
+ position: relative;
+ display: inline-block;
+ cursor: pointer;
}
-.fc-header .fc-corner-right {
- margin-right: 1px;
- padding-right: 0;
+.fc-state-default {
+ /* non-theme */
+ border-style: solid;
+ border-width: 1px 0;
}
-/* DEFAULT button COLORS */
+.fc-button-inner {
+ position: relative;
+ float: left;
+ overflow: hidden;
+}
-.fc-header .fc-state-default,
-.fc-header .fc-state-default a {
- border-color: #777; /* outer border */
- color: #333;
+.fc-state-default .fc-button-inner {
+ /* non-theme */
+ border-style: solid;
+ border-width: 0 1px;
}
-.fc-header .fc-state-default span {
- border-color: #fff #fff #d1d1d1; /* inner border */
- background: #e8e8e8;
+.fc-button-content {
+ position: relative;
+ float: left;
+ height: 1.9em;
+ line-height: 1.9em;
+ padding: 0 .6em;
+ white-space: nowrap;
}
-/* PRESSED button COLORS (down and active) */
+/* icon (for jquery ui) */
-.fc-header .fc-state-active a {
- color: #fff;
+.fc-button-content .fc-icon-wrap {
+ position: relative;
+ float: left;
+ top: 50%;
}
-.fc-header .fc-state-down span,
-.fc-header .fc-state-active span {
- background: #888;
- border-color: #808080 #808080 #909090; /* inner border */
+.fc-button-content .ui-icon {
+ position: relative;
+ float: left;
+ margin-top: -50%;
+ *margin-top: 0;
+ *top: -50%;
}
-/* DISABLED button COLORS */
+/* gloss effect */
-.fc-header .fc-state-disabled a {
- color: #999;
+.fc-state-default .fc-button-effect {
+ position: absolute;
+ top: 50%;
+ left: 0;
}
-.fc-header .fc-state-disabled,
-.fc-header .fc-state-disabled a {
- border-color: #ccc; /* outer border */
+.fc-state-default .fc-button-effect span {
+ position: absolute;
+ top: -100px;
+ left: 0;
+ width: 500px;
+ height: 100px;
+ border-width: 100px 0 0 1px;
+ border-style: solid;
+ border-color: #fff;
+ background: #444;
+ opacity: .09;
+ filter: alpha(opacity = 9);
}
-.fc-header .fc-state-disabled span {
- border-color: #fff #fff #f0f0f0; /* inner border */
- background: #f0f0f0;
-}
+/* button states (determines colors) */
-/* Content Area & Global Cell Styles
-------------------------------------------------------------------------*/
-
-.fc-widget-content {
- border: 1px solid #ccc; /* outer border color */
+.fc-state-default,
+.fc-state-default .fc-button-inner {
+ border-style: solid;
+ border-color: #ccc #bbb #aaa;
+ background: #F3F3F3;
+ color: #000;
}
-.fc-content {
- clear: both;
+.fc-state-hover,
+.fc-state-hover .fc-button-inner {
+ border-color: #999;
}
-.fc-content .fc-state-default {
- border-style: solid;
- border-color: #ccc; /* inner border color */
+.fc-state-down,
+.fc-state-down .fc-button-inner {
+ border-color: #555;
+ background: #777;
}
-.fc-content .fc-state-highlight {
-/* today */
- background: #ffc;
+.fc-state-active,
+.fc-state-active .fc-button-inner {
+ border-color: #555;
+ background: #777;
+ color: #fff;
}
-.fc-content .fc-not-today {
- background: none;
+.fc-state-disabled,
+.fc-state-disabled .fc-button-inner {
+ color: #999;
+ border-color: #ddd;
}
-.fc-cell-overlay {
-/* semi-transparent rectangle while dragging */
- background: #9cf;
- opacity: .2;
- filter: alpha(opacity = 20); /* for IE */
+.fc-state-disabled {
+ cursor: default;
}
-.fc-view {
-/* prevents dragging outside of widget */
- width: 100%;
- overflow: hidden;
+.fc-state-disabled .fc-button-effect {
+ display: none;
}
/* Global Event Styles
------------------------------------------------------------------------*/
-.fc-event,
-.fc-agenda .fc-event-time,
-.fc-event a {
+.fc-event {
border-style: solid;
- border-color: #36c; /* default BORDER color (probably the same as background-color) */
- background-color: #36c; /* default BACKGROUND color */
- color: #fff; /* default TEXT color */
+ border-width: 0;
+ font-size: .85em;
+ cursor: default;
}
-/* Use the 'className' CalEvent property and the following
- * example CSS to change event color on a per-event basis:
- *
- * .myclass,
- * .fc-agenda .myclass .fc-event-time,
- * .myclass a {
- * background-color: black;
- * border-color: black;
- * color: red;
- * }
- */
-
-.fc-event {
- text-align: left;
+a.fc-event,
+.fc-event-draggable {
+ cursor: pointer;
}
-.fc-event a {
- overflow: hidden;
- font-size: .85em;
+a.fc-event {
text-decoration: none;
- cursor: pointer;
}
-.fc-event-editable {
- cursor: pointer;
+.fc-rtl .fc-event {
+ text-align: right;
}
-.fc-event-time,
-.fc-event-title {
- padding: 0 1px;
+.fc-event-skin {
+ border-color: #36c; /* default BORDER color */
+ background-color: #36c; /* default BACKGROUND color */
+ color: #fff; /* default TEXT color */
}
-/* for fake rounded corners */
-
-.fc-event a {
- display: block;
+.fc-event-inner {
position: relative;
width: 100%;
height: 100%;
+ border-style: solid;
+ border-width: 0;
+ overflow: hidden;
}
-/* right-to-left */
-
-.fc-rtl .fc-event a {
- text-align: right;
+.fc-event-time,
+.fc-event-title {
+ padding: 0 1px;
}
-/* resizable */
-
.fc .ui-resizable-handle {
+ /*** TODO: don't use ui-resizable anymore, change class ***/
display: block;
position: absolute;
z-index: 99999;
- border: 0 !important; /* important overrides pre jquery ui 1.7 styles */
- background: url(data:image/gif;base64,AAAA) !important; /* hover fix for IE */
+ overflow: hidden; /* hacky spaces (IE6/7) */
+ font-size: 300%; /* */
+ line-height: 50%; /* */
}
/* Horizontal Events
@@ -304,30 +311,6 @@
margin-bottom: 1px;
}
-.fc-event-hori a {
- border-width: 0;
-}
-
-/* for fake rounded corners */
-
-.fc-content .fc-corner-left {
- margin-left: 1px;
-}
-
-.fc-content .fc-corner-left a {
- margin-left: -1px;
- border-left-width: 1px;
-}
-
-.fc-content .fc-corner-right {
- margin-right: 1px;
-}
-
-.fc-content .fc-corner-right a {
- margin-right: -1px;
- border-right-width: 1px;
-}
-
/* resizable */
.fc-event-hori .ui-resizable-e {
@@ -350,27 +333,96 @@
_padding-bottom: 14px; /* IE6 had 0 height */
}
-/* Month View, Basic Week View, Basic Day View
-------------------------------------------------------------------------*/
+/* Fake Rounded Corners (for buttons and events)
+------------------------------------------------------------*/
-.fc-grid table {
- width: 100%;
+.fc-corner-left {
+ margin-left: 1px;
}
-.fc .fc-grid th {
- border-width: 0 0 0 1px;
- text-align: center;
+.fc-corner-left .fc-button-inner,
+.fc-corner-left .fc-event-inner {
+ margin-left: -1px;
}
-.fc .fc-grid td {
+.fc-corner-right {
+ margin-right: 1px;
+}
+
+.fc-corner-right .fc-button-inner,
+.fc-corner-right .fc-event-inner {
+ margin-right: -1px;
+}
+
+.fc-corner-top {
+ margin-top: 1px;
+}
+
+.fc-corner-top .fc-event-inner {
+ margin-top: -1px;
+}
+
+.fc-corner-bottom {
+ margin-bottom: 1px;
+}
+
+.fc-corner-bottom .fc-event-inner {
+ margin-bottom: -1px;
+}
+
+/* Fake Rounded Corners SPECIFICALLY FOR EVENTS
+-----------------------------------------------------------------*/
+
+.fc-corner-left .fc-event-inner {
+ border-left-width: 1px;
+}
+
+.fc-corner-right .fc-event-inner {
+ border-right-width: 1px;
+}
+
+.fc-corner-top .fc-event-inner {
+ border-top-width: 1px;
+}
+
+.fc-corner-bottom .fc-event-inner {
+ border-bottom-width: 1px;
+}
+
+/* Reusable Separate-border Table
+------------------------------------------------------------*/
+
+table.fc-border-separate {
+ border-collapse: separate;
+}
+
+.fc-border-separate th,
+.fc-border-separate td {
border-width: 1px 0 0 1px;
}
-.fc-grid th.fc-leftmost,
-.fc-grid td.fc-leftmost {
- border-left: 0;
+.fc-border-separate th.fc-last,
+.fc-border-separate td.fc-last {
+ border-right-width: 1px;
}
+.fc-border-separate tr.fc-last th,
+.fc-border-separate tr.fc-last td {
+ border-bottom-width: 1px;
+}
+
+.fc-border-separate tbody tr.fc-first td,
+.fc-border-separate tbody tr.fc-first th {
+ border-top-width: 0;
+}
+
+/* Month View, Basic Week View, Basic Day View
+------------------------------------------------------------------------*/
+
+.fc-grid th {
+ text-align: center;
+}
+
.fc-grid .fc-day-number {
float: right;
padding: 0 2px;
@@ -386,7 +438,7 @@
.fc-grid .fc-day-content {
clear: both;
- padding: 2px 2px 0; /* distance between events and day edges */
+ padding: 2px 2px 1px; /* distance between events and day edges */
}
/* event styles */
@@ -397,10 +449,6 @@
/* right-to-left */
-.fc-rtl .fc-grid {
- direction: rtl;
-}
-
.fc-rtl .fc-grid .fc-day-number {
float: left;
}
@@ -412,131 +460,110 @@
/* Agenda Week View, Agenda Day View
------------------------------------------------------------------------*/
-.fc .fc-agenda th,
-.fc .fc-agenda td {
- border-width: 1px 0 0 1px;
+.fc-agenda table {
+ border-collapse: separate;
}
-.fc .fc-agenda .fc-leftmost {
- border-left: 0;
-}
-
-.fc-agenda tr.fc-first th,
-.fc-agenda tr.fc-first td {
- border-top: 0;
-}
-
-.fc-agenda-head tr.fc-last th {
- border-bottom-width: 1px;
-}
-
-.fc .fc-agenda-head td,
-.fc .fc-agenda-body td {
- background: none;
-}
-
-.fc-agenda-head th {
+.fc-agenda-days th {
text-align: center;
}
-/* the time axis running down the left side */
-
-.fc-agenda .fc-axis {
+.fc-agenda .fc-agenda-axis {
width: 50px;
padding: 0 4px;
vertical-align: middle;
+ text-align: right;
white-space: nowrap;
- text-align: right;
font-weight: normal;
}
-/* all-day event cells at top */
+.fc-agenda .fc-day-content {
+ padding: 2px 2px 1px;
+}
-.fc-agenda-head tr.fc-all-day th {
- height: 35px;
+/* make axis border take precedence */
+
+.fc-agenda-days .fc-agenda-axis {
+ border-right-width: 1px;
}
-.fc-agenda-head td {
- padding-bottom: 10px;
+.fc-agenda-days .fc-col0 {
+ border-left-width: 0;
}
-.fc .fc-divider div {
- font-size: 1px; /* for IE6/7 */
- height: 2px;
+/* all-day area */
+
+.fc-agenda-allday th {
+ border-width: 0 1px;
}
-.fc .fc-divider .fc-state-default {
- background: #eee; /* color for divider between all-day and time-slot events */
+.fc-agenda-allday .fc-day-content {
+ min-height: 34px; /* TODO: doesnt work well in quirksmode */
+ _height: 34px;
}
-/* body styles */
+/* divider (between all-day and slots) */
-.fc .fc-agenda-body td div {
- height: 20px; /* slot height */
+.fc-agenda-divider-inner {
+ height: 2px;
+ overflow: hidden;
}
-.fc .fc-agenda-body tr.fc-minor th,
-.fc .fc-agenda-body tr.fc-minor td {
- border-top-style: dotted;
+.fc-widget-header .fc-agenda-divider-inner {
+ background: #eee;
}
-.fc-agenda .fc-day-content {
- padding: 2px 2px 0; /* distance between events and day edges */
+/* slot rows */
+
+.fc-agenda-slots th {
+ border-width: 1px 1px 0;
}
-/* Vertical Events
-------------------------------------------------------------------------*/
-
-.fc-event-vert {
- border-width: 0 1px;
+.fc-agenda-slots td {
+ border-width: 1px 0 0;
+ background: none;
}
-.fc-event-vert a {
- border-width: 0;
+.fc-agenda-slots td div {
+ height: 20px;
}
-/* for fake rounded corners */
-
-.fc-content .fc-corner-top {
- margin-top: 1px;
+.fc-agenda-slots tr.fc-slot0 th,
+.fc-agenda-slots tr.fc-slot0 td {
+ border-top-width: 0;
}
-.fc-content .fc-corner-top a {
- margin-top: -1px;
- border-top-width: 1px;
+.fc-agenda-slots tr.fc-minor th,
+.fc-agenda-slots tr.fc-minor td {
+ border-top-style: dotted;
}
-.fc-content .fc-corner-bottom {
- margin-bottom: 1px;
+.fc-agenda-slots tr.fc-minor th.ui-widget-header {
+ *border-top-style: solid; /* doesn't work with background in IE6/7 */
}
-.fc-content .fc-corner-bottom a {
- margin-bottom: -1px;
- border-bottom-width: 1px;
+/* Vertical Events
+------------------------------------------------------------------------*/
+
+.fc-event-vert {
+ border-width: 0 1px;
}
-/* event content */
-
-.fc-event-vert span {
- display: block;
+.fc-event-vert .fc-event-head,
+.fc-event-vert .fc-event-content {
position: relative;
z-index: 2;
+ width: 100%;
+ overflow: hidden;
}
-.fc-event-vert span.fc-event-time {
+.fc-event-vert .fc-event-time {
white-space: nowrap;
- _white-space: normal;
- overflow: hidden;
- border: 0;
font-size: 10px;
}
-.fc-event-vert span.fc-event-title {
- line-height: 13px;
-}
-
-.fc-event-vert span.fc-event-bg {
-/* makes the event lighter w/ a semi-transparent overlay */
+.fc-event-vert .fc-event-bg {
+ /* makes the event lighter w/ a semi-transparent overlay */
position: absolute;
z-index: 1;
top: 0;
@@ -545,20 +572,31 @@
height: 100%;
background: #fff;
opacity: .3;
- filter: alpha(opacity = 30); /* for IE */
+ filter: alpha(opacity = 30);
}
+.fc .ui-draggable-dragging .fc-event-bg, /* TODO: something nicer like .fc-opacity */
+.fc-select-helper .fc-event-bg {
+ display: none\9; /* for IE6/7/8. nested opacity filters while dragging don't work */
+}
+
/* resizable */
.fc-event-vert .ui-resizable-s {
bottom: 0 !important; /* importants override pre jquery ui 1.7 styles */
width: 100% !important;
height: 8px !important;
+ overflow: hidden !important;
line-height: 8px !important;
font-size: 11px !important;
font-family: monospace;
text-align: center;
cursor: s-resize;
}
+
+.fc-agenda .ui-resizable-resizing {
+ /* TODO: better selector */
+ _overflow: hidden;
+}
Modified: sandbox/trunk/ui/schedule/ui/src/main/resources/META-INF/resources/fullcalendar.js
===================================================================
--- sandbox/trunk/ui/schedule/ui/src/main/resources/META-INF/resources/fullcalendar.js 2011-11-22 06:11:32 UTC (rev 22966)
+++ sandbox/trunk/ui/schedule/ui/src/main/resources/META-INF/resources/fullcalendar.js 2011-11-22 06:22:20 UTC (rev 22967)
@@ -1,31 +1,23 @@
/**
* @preserve
- * FullCalendar v1.4.6
+ * FullCalendar v1.5.2
* http://arshaw.com/fullcalendar/
*
* Use fullcalendar.css for basic styling.
- * For event drag & drop, required jQuery UI draggable.
+ * For event drag & drop, requires jQuery UI draggable.
* For event resizing, requires jQuery UI resizable.
*
- * Copyright (c) 2009 Adam Shaw
- * Dual licensed under the MIT and GPL licenses:
- * http://www.opensource.org/licenses/mit-license.php
- * http://www.gnu.org/licenses/gpl.html
+ * Copyright (c) 2011 Adam Shaw
+ * Dual licensed under the MIT and GPL licenses, located in
+ * MIT-LICENSE.txt and GPL-LICENSE.txt respectively.
*
- * Date: Mon May 31 10:18:29 2010 -0700
+ * Date: Sun Aug 21 22:06:09 2011 -0700
*
*/
-(function($) {
+(function($, undefined) {
- var fc = $.fullCalendar = {};
- var views = fc.views = {};
-
-
- /* Defaults
- -----------------------------------------------------------------------------*/
-
var defaults = {
// display
@@ -44,6 +36,7 @@
//disableResizing: false,
allDayDefault: true,
+ ignoreTimezone: true,
// event ajax
lazyFetching: true,
@@ -73,10 +66,10 @@
dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
buttonText: {
- prev: ' ◄ ',
- next: ' ► ',
- prevYear: ' << ',
- nextYear: ' >> ',
+ prev: ' ◄ ',
+ next: ' ► ',
+ prevYear: ' << ',
+ nextYear: ' >> ',
today: 'today',
month: 'month',
week: 'week',
@@ -91,11 +84,13 @@
},
//selectable: false,
- unselectAuto: true
+ unselectAuto: true,
+ dropAccept: '*'
+
};
- // right-to-left defaults
+// right-to-left defaults
var rtlDefaults = {
header: {
left: 'next,prev today',
@@ -103,10 +98,10 @@
right: 'title'
},
buttonText: {
- prev: ' ► ',
- next: ' ◄ ',
- prevYear: ' >> ',
- nextYear: ' << '
+ prev: ' ► ',
+ next: ' ◄ ',
+ prevYear: ' >> ',
+ nextYear: ' << '
},
buttonIcons: {
prev: 'circle-triangle-e',
@@ -114,31 +109,28 @@
}
};
- // function for adding/overriding defaults
- var setDefaults = fc.setDefaults = function(d) {
- $.extend(true, defaults, d);
- };
+ var fc = $.fullCalendar = { version: "1.5.2" };
+ var fcViews = fc.views = {};
- /* .fullCalendar jQuery function
- -----------------------------------------------------------------------------*/
$.fn.fullCalendar = function(options) {
+
// method calling
if (typeof options == 'string') {
- var args = Array.prototype.slice.call(arguments, 1),
- res;
+ var args = Array.prototype.slice.call(arguments, 1);
+ var res;
this.each(function() {
- var data = $.data(this, 'fullCalendar');
- if (data) {
- var meth = data[options];
- if (meth) {
- var r = meth.apply(this, args);
- if (res === undefined) {
- res = r;
- }
+ var calendar = $.data(this, 'fullCalendar');
+ if (calendar && $.isFunction(calendar[options])) {
+ var r = calendar[options].apply(calendar, args);
+ if (res === undefined) {
+ res = r;
}
+ if (options == 'destroy') {
+ $.removeData(this, 'fullCalendar');
+ }
}
});
if (res !== undefined) {
@@ -147,7 +139,8 @@
return this;
}
- // pluck the 'events' and 'eventSources' options
+
+ // would like to have this logic in EventManager, but needs to happen before options are recursively extended
var eventSources = options.eventSources || [];
delete options.eventSources;
if (options.events) {
@@ -155,1319 +148,2279 @@
delete options.events;
}
- // first event source reserved for 'sticky' events
- eventSources.unshift([]);
- // initialize options
options = $.extend(true, {},
defaults,
(options.isRTL || options.isRTL === undefined && defaults.isRTL) ? rtlDefaults : {},
options
- );
- var tm = options.theme ? 'ui' : 'fc'; // for making theme classes
+ );
- this.each(function() {
+ this.each(function(i, _element) {
+ var element = $(_element);
+ var calendar = new Calendar(element, options, eventSources);
+ element.data('fullCalendar', calendar); // TODO: look into memory leak implications
+ calendar.render();
+ });
- /* Instance Initialization
- -----------------------------------------------------------------------------*/
+ return this;
- // element
- var _element = this,
- element = $(_element).addClass('fc'),
- elementOuterWidth,
- content = $("<div class='fc-content " + tm + "-widget-content' style='position:relative'/>").prependTo(_element),
- suggestedViewHeight,
- resizeUID = 0,
- ignoreWindowResize = 0,
- date = new Date(),
- viewName, // the current view name (TODO: look into getting rid of)
- view, // the current view
- viewInstances = {},
- absoluteViewElement;
+ };
+// function for adding/overriding defaults
+ function setDefaults(d) {
+ $.extend(true, defaults, d);
+ }
+
+
+ function Calendar(element, options, eventSources) {
+ var t = this;
+
+
+ // exports
+ t.options = options;
+ t.render = render;
+ t.destroy = destroy;
+ t.refetchEvents = refetchEvents;
+ t.reportEvents = reportEvents;
+ t.reportEventChange = reportEventChange;
+ t.rerenderEvents = rerenderEvents;
+ t.changeView = changeView;
+ t.select = select;
+ t.unselect = unselect;
+ t.prev = prev;
+ t.next = next;
+ t.prevYear = prevYear;
+ t.nextYear = nextYear;
+ t.today = today;
+ t.gotoDate = gotoDate;
+ t.incrementDate = incrementDate;
+ t.formatDate = function(format, date) {
+ return formatDate(format, date, options)
+ };
+ t.formatDates = function(format, date1, date2) {
+ return formatDates(format, date1, date2, options)
+ };
+ t.getDate = getDate;
+ t.getView = getView;
+ t.option = option;
+ t.trigger = trigger;
+
+
+ // imports
+ EventManager.call(t, options, eventSources);
+ var isFetchNeeded = t.isFetchNeeded;
+ var fetchEvents = t.fetchEvents;
+
+
+ // locals
+ var _element = element[0];
+ var header;
+ var headerElement;
+ var content;
+ var tm; // for making theme classes
+ var currentView;
+ var viewInstances = {};
+ var elementOuterWidth;
+ var suggestedViewHeight;
+ var absoluteViewElement;
+ var resizeUID = 0;
+ var ignoreWindowResize = 0;
+ var date = new Date();
+ var events = [];
+ var _dragElement;
+
+
+ /* Main Rendering
+ -----------------------------------------------------------------------------*/
+
+
+ setYMD(date, options.year, options.month, options.date);
+
+
+ function render(inc) {
+ if (!content) {
+ initialRender();
+ } else {
+ calcSize();
+ markSizesDirty();
+ markEventsDirty();
+ renderView(inc);
+ }
+ }
+
+
+ function initialRender() {
+ tm = options.theme ? 'ui' : 'fc';
+ element.addClass('fc');
if (options.isRTL) {
element.addClass('fc-rtl');
}
if (options.theme) {
element.addClass('ui-widget');
}
-
- if (options.year !== undefined && options.year != date.getFullYear()) {
- date.setDate(1);
- date.setMonth(0);
- date.setFullYear(options.year);
+ content = $("<div class='fc-content' style='position:relative'/>")
+ .prependTo(element);
+ header = new Header(t, options);
+ headerElement = header.render();
+ if (headerElement) {
+ element.prepend(headerElement);
}
- if (options.month !== undefined && options.month != date.getMonth()) {
- date.setDate(1);
- date.setMonth(options.month);
+ changeView(options.defaultView);
+ $(window).resize(windowResize);
+ // needed for IE in a 0x0 iframe, b/c when it is resized, never triggers a windowResize
+ if (!bodyVisible()) {
+ lateRender();
}
- if (options.date !== undefined) {
- date.setDate(options.date);
- }
+ }
- /* View Rendering
- -----------------------------------------------------------------------------*/
+ // called when we know the calendar couldn't be rendered when it was initialized,
+ // but we think it's ready now
+ function lateRender() {
+ setTimeout(function() { // IE7 needs this so dimensions are calculated correctly
+ if (!currentView.start && bodyVisible()) { // !currentView.start makes sure this never happens more than once
+ renderView();
+ }
+ }, 0);
+ }
- function changeView(v) {
- if (v != viewName) {
- ignoreWindowResize++; // because setMinHeight might change the height before render (and subsequently setSize) is reached
- viewUnselect();
+ function destroy() {
+ $(window).unbind('resize', windowResize);
+ header.destroy();
+ content.remove();
+ element.removeClass('fc fc-rtl ui-widget');
+ }
- var oldView = view,
- newViewElement;
- if (oldView) {
- if (oldView.eventsChanged) {
- eventsDirty();
- oldView.eventDirty = oldView.eventsChanged = false;
- }
- if (oldView.beforeHide) {
- oldView.beforeHide(); // called before changing min-height. if called after, scroll state is reset (in Opera)
- }
- setMinHeight(content, content.height());
- oldView.element.hide();
- } else {
- setMinHeight(content, 1); // needs to be 1 (not 0) for IE7, or else view dimensions miscalculated
- }
- content.css('overflow', 'hidden');
+ function elementVisible() {
+ return _element.offsetWidth !== 0;
+ }
- if (viewInstances[v]) {
- (view = viewInstances[v]).element.show();
- } else {
- view = viewInstances[v] = fc.views[v](
- newViewElement = absoluteViewElement =
- $("<div class='fc-view fc-view-" + v + "' style='position:absolute'/>")
- .appendTo(content),
- options
- );
- }
- if (header) {
- // update 'active' view button
- header.find('div.fc-button-' + viewName).removeClass(tm + '-state-active');
- header.find('div.fc-button-' + v).addClass(tm + '-state-active');
- }
+ function bodyVisible() {
+ return $('body')[0].offsetWidth !== 0;
+ }
- view.name = viewName = v;
- render(); // after height has been set, will make absoluteViewElement's position=relative, then set to null
- content.css('overflow', '');
- if (oldView) {
- setMinHeight(content, 1);
- }
- if (!newViewElement && view.afterShow) {
- view.afterShow(); // called after setting min-height/overflow, so in final scroll state (for Opera)
- }
- ignoreWindowResize--;
- }
- }
+ /* View Rendering
+ -----------------------------------------------------------------------------*/
+ // TODO: improve view switching (still weird transition in IE, and FF has whiteout problem)
- function render(inc) {
- if (elementVisible()) {
- ignoreWindowResize++; // because view.renderEvents might temporarily change the height before setSize is reached
+ function changeView(newViewName) {
+ if (!currentView || newViewName != currentView.name) {
+ ignoreWindowResize++; // because setMinHeight might change the height before render (and subsequently setSize) is reached
- viewUnselect();
+ unselect();
- if (suggestedViewHeight === undefined) {
- calcSize();
- }
+ var oldView = currentView;
+ var newViewElement;
- if (!view.start || inc || date < view.start || date >= view.end) {
- view.render(date, inc || 0); // responsible for clearing events
- setSize(true);
- if (!eventStart || !options.lazyFetching || view.visStart < eventStart || view.visEnd > eventEnd) {
- fetchAndRenderEvents();
- } else {
- view.renderEvents(events); // don't refetch
- }
- }
- else if (view.sizeDirty || view.eventsDirty || !options.lazyFetching) {
- view.clearEvents();
- if (view.sizeDirty) {
- setSize();
- }
- if (options.lazyFetching) {
- view.renderEvents(events); // don't refetch
- } else {
- fetchAndRenderEvents();
- }
- }
- elementOuterWidth = element.outerWidth();
- view.sizeDirty = false;
- view.eventsDirty = false;
+ if (oldView) {
+ (oldView.beforeHide || noop)(); // called before changing min-height. if called after, scroll state is reset (in Opera)
+ setMinHeight(content, content.height());
+ oldView.element.hide();
+ } else {
+ setMinHeight(content, 1); // needs to be 1 (not 0) for IE7, or else view dimensions miscalculated
+ }
+ content.css('overflow', 'hidden');
- if (header) {
- // update title text
- header.find('h2.fc-header-title').html(view.title);
- // enable/disable 'today' button
- var today = new Date();
- if (today >= view.start && today < view.end) {
- header.find('div.fc-button-today').addClass(tm + '-state-disabled');
- } else {
- header.find('div.fc-button-today').removeClass(tm + '-state-disabled');
- }
- }
+ currentView = viewInstances[newViewName];
+ if (currentView) {
+ currentView.element.show();
+ } else {
+ currentView = viewInstances[newViewName] = new fcViews[newViewName](
+ newViewElement = absoluteViewElement =
+ $("<div class='fc-view fc-view-" + newViewName + "' style='position:absolute'/>")
+ .appendTo(content),
+ t // the calendar object
+ );
+ }
- ignoreWindowResize--;
- view.trigger('viewDisplay', _element);
+ if (oldView) {
+ header.deactivateButton(oldView.name);
}
- }
+ header.activateButton(newViewName);
+ renderView(); // after height has been set, will make absoluteViewElement's position=relative, then set to null
- function elementVisible() {
- return _element.offsetWidth !== 0;
- }
+ content.css('overflow', '');
+ if (oldView) {
+ setMinHeight(content, 1);
+ }
- function bodyVisible() {
- return $('body')[0].offsetWidth !== 0;
- }
+ if (!newViewElement) {
+ (currentView.afterShow || noop)(); // called after setting min-height/overflow, so in final scroll state (for Opera)
+ }
- function viewUnselect() {
- if (view) {
- view.unselect();
- }
+ ignoreWindowResize--;
}
+ }
- // called when any event objects have been added/removed/changed, rerenders
- function eventsChanged() {
- eventsDirty();
- if (elementVisible()) {
- view.clearEvents();
- view.renderEvents(events);
- view.eventsDirty = false;
- }
- }
+ function renderView(inc) {
+ if (elementVisible()) {
+ ignoreWindowResize++; // because renderEvents might temporarily change the height before setSize is reached
- // marks other views' events as dirty
- function eventsDirty() {
- $.each(viewInstances, function() {
- this.eventsDirty = true;
- });
- }
+ unselect();
- // called when we know the element size has changed
- function sizeChanged() {
- sizesDirty();
- if (elementVisible()) {
+ if (suggestedViewHeight === undefined) {
calcSize();
+ }
+
+ var forceEventRender = false;
+ if (!currentView.start || inc || date < currentView.start || date >= currentView.end) {
+ // view must render an entire new date range (and refetch/render events)
+ currentView.render(date, inc || 0); // responsible for clearing events
+ setSize(true);
+ forceEventRender = true;
+ }
+ else if (currentView.sizeDirty) {
+ // view must resize (and rerender events)
+ currentView.clearEvents();
setSize();
- viewUnselect();
- view.rerenderEvents();
- view.sizeDirty = false;
+ forceEventRender = true;
}
+ else if (currentView.eventsDirty) {
+ currentView.clearEvents();
+ forceEventRender = true;
+ }
+ currentView.sizeDirty = false;
+ currentView.eventsDirty = false;
+ updateEvents(forceEventRender);
+
+ elementOuterWidth = element.outerWidth();
+
+ header.updateTitle(currentView.title);
+ var today = new Date();
+ if (today >= currentView.start && today < currentView.end) {
+ header.disableButton('today');
+ } else {
+ header.enableButton('today');
+ }
+
+ ignoreWindowResize--;
+ currentView.trigger('viewDisplay', _element);
}
+ }
- // marks other views' sizes as dirty
- function sizesDirty() {
- $.each(viewInstances, function() {
- this.sizeDirty = true;
- });
+
+ /* Resizing
+ -----------------------------------------------------------------------------*/
+
+
+ function updateSize() {
+ markSizesDirty();
+ if (elementVisible()) {
+ calcSize();
+ setSize();
+ unselect();
+ currentView.clearEvents();
+ currentView.renderEvents(events);
+ currentView.sizeDirty = false;
}
+ }
- /* Event Sources and Fetching
- -----------------------------------------------------------------------------*/
+ function markSizesDirty() {
+ $.each(viewInstances, function(i, inst) {
+ inst.sizeDirty = true;
+ });
+ }
- var events = [],
- eventStart, eventEnd;
- // Fetch from ALL sources. Clear 'events' array and populate
- function fetchEvents(callback) {
- events = [];
- eventStart = cloneDate(view.visStart);
- eventEnd = cloneDate(view.visEnd);
- var queued = eventSources.length,
- sourceDone = function() {
- if (!--queued) {
- if (callback) {
- callback(events);
- }
- }
- }, i = 0;
- for (; i < eventSources.length; i++) {
- fetchEventSource(eventSources[i], sourceDone);
- }
+ function calcSize() {
+ if (options.contentHeight) {
+ suggestedViewHeight = options.contentHeight;
}
+ else if (options.height) {
+ suggestedViewHeight = options.height - (headerElement ? headerElement.height() : 0) - vsides(content);
+ }
+ else {
+ suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5));
+ }
+ }
- // Fetch from a particular source. Append to the 'events' array
- function fetchEventSource(src, callback) {
- var prevViewName = view.name,
- prevDate = cloneDate(date),
- reportEvents = function(a) {
- if (prevViewName == view.name && +prevDate == +date && // protects from fast switching
- $.inArray(src, eventSources) != -1) { // makes sure source hasn't been removed
- for (var i = 0; i < a.length; i++) {
- normalizeEvent(a[i], options);
- a[i].source = src;
- }
- events = events.concat(a);
- if (callback) {
- callback(a);
- }
+
+ function setSize(dateChanged) { // todo: dateChanged?
+ ignoreWindowResize++;
+ currentView.setHeight(suggestedViewHeight, dateChanged);
+ if (absoluteViewElement) {
+ absoluteViewElement.css('position', 'relative');
+ absoluteViewElement = null;
+ }
+ currentView.setWidth(content.width(), dateChanged);
+ ignoreWindowResize--;
+ }
+
+
+ function windowResize() {
+ if (!ignoreWindowResize) {
+ if (currentView.start) { // view has already been rendered
+ var uid = ++resizeUID;
+ setTimeout(function() { // add a delay
+ if (uid == resizeUID && !ignoreWindowResize && elementVisible()) {
+ if (elementOuterWidth != (elementOuterWidth = element.outerWidth())) {
+ ignoreWindowResize++; // in case the windowResize callback changes the height
+ updateSize();
+ currentView.trigger('windowResize', _element);
+ ignoreWindowResize--;
}
- },
- reportEventsAndPop = function(a) {
- reportEvents(a);
- popLoading();
- };
- if (typeof src == 'string') {
- var params = {};
- params[options.startParam] = Math.round(eventStart.getTime() / 1000);
- params[options.endParam] = Math.round(eventEnd.getTime() / 1000);
- if (options.cacheParam) {
- params[options.cacheParam] = (new Date()).getTime(); // TODO: deprecate cacheParam
- }
- pushLoading();
- $.ajax({
- url: src,
- dataType: 'json',
- data: params,
- cache: options.cacheParam || false, // don't let jquery prevent caching if cacheParam is being used
- success: reportEventsAndPop
- });
+ }
+ }, 200);
+ } else {
+ // calendar must have been initialized in a 0x0 iframe that has just been resized
+ lateRender();
}
- else if ($.isFunction(src)) {
- pushLoading();
- src(cloneDate(eventStart), cloneDate(eventEnd), reportEventsAndPop);
- }
- else {
- reportEvents(src); // src is an array
- }
}
+ }
- // for convenience
- function fetchAndRenderEvents() {
- fetchEvents(function(events) {
- view.renderEvents(events); // maintain `this` in view
- });
+ /* Event Fetching/Rendering
+ -----------------------------------------------------------------------------*/
+
+
+ // fetches events if necessary, rerenders events if necessary (or if forced)
+ function updateEvents(forceRender) {
+ if (!options.lazyFetching || isFetchNeeded(currentView.visStart, currentView.visEnd)) {
+ refetchEvents();
}
+ else if (forceRender) {
+ rerenderEvents();
+ }
+ }
- /* Loading State
- -----------------------------------------------------------------------------*/
+ function refetchEvents() {
+ fetchEvents(currentView.visStart, currentView.visEnd); // will call reportEvents
+ }
- var loadingLevel = 0;
- function pushLoading() {
- if (!loadingLevel++) {
- view.trigger('loading', _element, true);
- }
+ // called when event data arrives
+ function reportEvents(_events) {
+ events = _events;
+ rerenderEvents();
+ }
+
+
+ // called when a single event's data has been changed
+ function reportEventChange(eventID) {
+ rerenderEvents(eventID);
+ }
+
+
+ // attempts to rerenderEvents
+ function rerenderEvents(modifiedEventID) {
+ markEventsDirty();
+ if (elementVisible()) {
+ currentView.clearEvents();
+ currentView.renderEvents(events, modifiedEventID);
+ currentView.eventsDirty = false;
}
+ }
- function popLoading() {
- if (!--loadingLevel) {
- view.trigger('loading', _element, false);
- }
+
+ function markEventsDirty() {
+ $.each(viewInstances, function(i, inst) {
+ inst.eventsDirty = true;
+ });
+ }
+
+
+ /* Selection
+ -----------------------------------------------------------------------------*/
+
+
+ function select(start, end, allDay) {
+ currentView.select(start, end, allDay === undefined ? true : allDay);
+ }
+
+
+ function unselect() { // safe to be called before renderView
+ if (currentView) {
+ currentView.unselect();
}
+ }
- /* Public Methods
- -----------------------------------------------------------------------------*/
+ /* Date
+ -----------------------------------------------------------------------------*/
- var publicMethods = {
- render: function() {
- calcSize();
- sizesDirty();
- eventsDirty();
- render();
- },
+ function prev() {
+ renderView(-1);
+ }
- changeView: changeView,
- getView: function() {
- return view;
- },
+ function next() {
+ renderView(1);
+ }
- getDate: function() {
- return date;
- },
- option: function(name, value) {
- if (value === undefined) {
- return options[name];
- }
- if (name == 'height' || name == 'contentHeight' || name == 'aspectRatio') {
- options[name] = value;
- sizeChanged();
- }
- },
+ function prevYear() {
+ addYears(date, -1);
+ renderView();
+ }
- destroy: function() {
- $(window).unbind('resize', windowResize);
- if (header) {
- header.remove();
- }
- content.remove();
- $.removeData(_element, 'fullCalendar');
- },
- //
- // Navigation
- //
+ function nextYear() {
+ addYears(date, 1);
+ renderView();
+ }
- prev: function() {
- render(-1);
- },
- next: function() {
- render(1);
- },
+ function today() {
+ date = new Date();
+ renderView();
+ }
- prevYear: function() {
- addYears(date, -1);
- render();
- },
- nextYear: function() {
- addYears(date, 1);
- render();
- },
+ function gotoDate(year, month, dateOfMonth) {
+ if (year instanceof Date) {
+ date = cloneDate(year); // provided 1 argument, a Date
+ } else {
+ setYMD(date, year, month, dateOfMonth);
+ }
+ renderView();
+ }
- today: function() {
- date = new Date();
- render();
- },
- gotoDate: function(year, month, dateNum) {
- if (typeof year == 'object') {
- date = cloneDate(year); // provided 1 argument, a Date
- } else {
- if (year !== undefined) {
- date.setFullYear(year);
- }
- if (month !== undefined) {
- date.setMonth(month);
- }
- if (dateNum !== undefined) {
- date.setDate(dateNum);
- }
- }
- render();
- },
+ function incrementDate(years, months, days) {
+ if (years !== undefined) {
+ addYears(date, years);
+ }
+ if (months !== undefined) {
+ addMonths(date, months);
+ }
+ if (days !== undefined) {
+ addDays(date, days);
+ }
+ renderView();
+ }
- incrementDate: function(years, months, days) {
- if (years !== undefined) {
- addYears(date, years);
- }
- if (months !== undefined) {
- addMonths(date, months);
- }
- if (days !== undefined) {
- addDays(date, days);
- }
- render();
- },
- //
- // Event Manipulation
- //
+ function getDate() {
+ return cloneDate(date);
+ }
- updateEvent: function(event) { // update an existing event
- var i, len = events.length, e,
- startDelta = event.start - event._start,
- endDelta = event.end ?
- (event.end - (event._end || view.defaultEventEnd(event))) // event._end would be null if event.end
- : 0; // was null and event was just resized
- for (i = 0; i < len; i++) {
- e = events[i];
- if (e._id == event._id && e != event) {
- e.start = new Date(+e.start + startDelta);
- if (event.end) {
- if (e.end) {
- e.end = new Date(+e.end + endDelta);
- } else {
- e.end = new Date(+view.defaultEventEnd(e) + endDelta);
- }
- } else {
- e.end = null;
- }
- e.title = event.title;
- e.url = event.url;
- e.allDay = event.allDay;
- e.className = event.className;
- e.editable = event.editable;
- normalizeEvent(e, options);
- }
- }
- normalizeEvent(event, options);
- eventsChanged();
- },
- renderEvent: function(event, stick) { // render a new event
- normalizeEvent(event, options);
- if (!event.source) {
- if (stick) {
- (event.source = eventSources[0]).push(event);
- }
- events.push(event);
- }
- eventsChanged();
- },
+ /* Misc
+ -----------------------------------------------------------------------------*/
- removeEvents: function(filter) {
- if (!filter) { // remove all
- events = [];
- // clear all array sources
- for (var i = 0; i < eventSources.length; i++) {
- if (typeof eventSources[i] == 'object') {
- eventSources[i] = [];
- }
- }
- } else {
- if (!$.isFunction(filter)) { // an event ID
- var id = filter + '';
- filter = function(e) {
- return e._id == id;
- };
- }
- events = $.grep(events, filter, true);
- // remove events from array sources
- for (var i = 0; i < eventSources.length; i++) {
- if (typeof eventSources[i] == 'object') {
- eventSources[i] = $.grep(eventSources[i], filter, true);
- }
- }
- }
- eventsChanged();
- },
- clientEvents: function(filter) {
- if ($.isFunction(filter)) {
- return $.grep(events, filter);
- }
- else if (filter) { // an event ID
- filter += '';
- return $.grep(events, function(e) {
- return e._id == filter;
- });
- }
- return events; // else, return all
- },
+ function getView() {
+ return currentView;
+ }
- rerenderEvents: eventsChanged, // TODO: think of renaming eventsChanged
- //
- // Event Source
- //
+ function option(name, value) {
+ if (value === undefined) {
+ return options[name];
+ }
+ if (name == 'height' || name == 'contentHeight' || name == 'aspectRatio') {
+ options[name] = value;
+ updateSize();
+ }
+ }
- addEventSource: function(source) {
- eventSources.push(source);
- fetchEventSource(source, eventsChanged);
- },
- removeEventSource: function(source) {
- eventSources = $.grep(eventSources, function(src) {
- return src != source;
+ function trigger(name, thisObj) {
+ if (options[name]) {
+ return options[name].apply(
+ thisObj || _element,
+ Array.prototype.slice.call(arguments, 2)
+ );
+ }
+ }
+
+
+ /* External Dragging
+ ------------------------------------------------------------------------*/
+
+ if (options.droppable) {
+ $(document)
+ .bind('dragstart', function(ev, ui) {
+ var _e = ev.target;
+ var e = $(_e);
+ if (!e.parents('.fc').length) { // not already inside a calendar
+ var accept = options.dropAccept;
+ if ($.isFunction(accept) ? accept.call(_e, e) : e.is(accept)) {
+ _dragElement = _e;
+ currentView.dragStart(_dragElement, ev, ui);
+ }
+ }
+ })
+ .bind('dragstop', function(ev, ui) {
+ if (_dragElement) {
+ currentView.dragStop(_dragElement, ev, ui);
+ _dragElement = null;
+ }
});
- // remove all client events from that source
- events = $.grep(events, function(e) {
- return e.source != source;
- });
- eventsChanged();
- },
+ }
- refetchEvents: function() {
- fetchEvents(eventsChanged);
- },
- //
- // selection
- //
+ }
- select: function(start, end, allDay) {
- view.select(start, end, allDay === undefined ? true : allDay);
- },
+ function Header(calendar, options) {
+ var t = this;
- unselect: function() {
- view.unselect();
- }
- };
+ // exports
+ t.render = render;
+ t.destroy = destroy;
+ t.updateTitle = updateTitle;
+ t.activateButton = activateButton;
+ t.deactivateButton = deactivateButton;
+ t.disableButton = disableButton;
+ t.enableButton = enableButton;
- $.data(this, 'fullCalendar', publicMethods); // TODO: look into memory leak implications
+ // locals
+ var element = $([]);
+ var tm;
- /* Header
- -----------------------------------------------------------------------------*/
- var header,
- sections = options.header;
+ function render() {
+ tm = options.theme ? 'ui' : 'fc';
+ var sections = options.header;
if (sections) {
- header = $("<table class='fc-header'/>")
- .append($("<tr/>")
- .append($("<td class='fc-header-left'/>").append(buildSection(sections.left)))
- .append($("<td class='fc-header-center'/>").append(buildSection(sections.center)))
- .append($("<td class='fc-header-right'/>").append(buildSection(sections.right))))
- .prependTo(element);
+ element = $("<table class='fc-header' style='width:100%'/>")
+ .append(
+ $("<tr/>")
+ .append(renderSection('left'))
+ .append(renderSection('center'))
+ .append(renderSection('right'))
+ );
+ return element;
}
- function buildSection(buttonStr) {
- if (buttonStr) {
- var tr = $("<tr/>");
- $.each(buttonStr.split(' '), function(i) {
- if (i > 0) {
- tr.append("<td><span class='fc-header-space'/></td>");
- }
- var prevButton;
- $.each(this.split(','), function(j, buttonName) {
- if (buttonName == 'title') {
- tr.append("<td><h2 class='fc-header-title'> </h2></td>");
- if (prevButton) {
- prevButton.addClass(tm + '-corner-right');
- }
- prevButton = null;
- } else {
- var buttonClick;
- if (publicMethods[buttonName]) {
- buttonClick = publicMethods[buttonName];
- }
- else if (views[buttonName]) {
- buttonClick = function() {
- button.removeClass(tm + '-state-hover');
- changeView(buttonName);
- };
- }
- if (buttonClick) {
- if (prevButton) {
- prevButton.addClass(tm + '-no-right');
- }
- var button,
- icon = options.theme ? smartProperty(options.buttonIcons, buttonName) : null,
- text = smartProperty(options.buttonText, buttonName);
- if (icon) {
- button = $("<div class='fc-button-" + buttonName + " ui-state-default'>" +
- "<a><span class='ui-icon ui-icon-" + icon + "'/></a></div>");
- }
- else if (text) {
- button = $("<div class='fc-button-" + buttonName + " " + tm + "-state-default'>" +
- "<a><span>" + text + "</span></a></div>");
- }
- if (button) {
- button
- .click(function() {
- if (!button.hasClass(tm + '-state-disabled')) {
- buttonClick();
+ }
+
+
+ function destroy() {
+ element.remove();
+ }
+
+
+ function renderSection(position) {
+ var e = $("<td class='fc-header-" + position + "'/>");
+ var buttonStr = options.header[position];
+ if (buttonStr) {
+ $.each(buttonStr.split(' '), function(i) {
+ if (i > 0) {
+ e.append("<span class='fc-header-space'/>");
+ }
+ var prevButton;
+ $.each(this.split(','), function(j, buttonName) {
+ if (buttonName == 'title') {
+ e.append("<span class='fc-header-title'><h2> </h2></span>");
+ if (prevButton) {
+ prevButton.addClass(tm + '-corner-right');
+ }
+ prevButton = null;
+ } else {
+ var buttonClick;
+ if (calendar[buttonName]) {
+ buttonClick = calendar[buttonName]; // calendar method
+ }
+ else if (fcViews[buttonName]) {
+ buttonClick = function() {
+ button.removeClass(tm + '-state-hover'); // forget why
+ calendar.changeView(buttonName);
+ };
+ }
+ if (buttonClick) {
+ var icon = options.theme ? smartProperty(options.buttonIcons, buttonName) : null; // why are we using smartProperty here?
+ var text = smartProperty(options.buttonText, buttonName); // why are we using smartProperty here?
+ var button = $(
+ "<span class='fc-button fc-button-" + buttonName + " " + tm + "-state-default'>" +
+ "<span class='fc-button-inner'>" +
+ "<span class='fc-button-content'>" +
+ (icon ?
+ "<span class='fc-icon-wrap'>" +
+ "<span class='ui-icon ui-icon-" + icon + "'/>" +
+ "</span>" :
+ text
+ ) +
+ "</span>" +
+ "<span class='fc-button-effect'><span></span></span>" +
+ "</span>" +
+ "</span>"
+ );
+ if (button) {
+ button
+ .click(function() {
+ if (!button.hasClass(tm + '-state-disabled')) {
+ buttonClick();
+ }
+ })
+ .mousedown(function() {
+ button
+ .not('.' + tm + '-state-active')
+ .not('.' + tm + '-state-disabled')
+ .addClass(tm + '-state-down');
+ })
+ .mouseup(function() {
+ button.removeClass(tm + '-state-down');
+ })
+ .hover(
+ function() {
+ button
+ .not('.' + tm + '-state-active')
+ .not('.' + tm + '-state-disabled')
+ .addClass(tm + '-state-hover');
+ },
+ function() {
+ button
+ .removeClass(tm + '-state-hover')
+ .removeClass(tm + '-state-down');
}
- })
- .mousedown(function() {
- button
- .not('.' + tm + '-state-active')
- .not('.' + tm + '-state-disabled')
- .addClass(tm + '-state-down');
- })
- .mouseup(function() {
- button.removeClass(tm + '-state-down');
- })
- .hover(
- function() {
- button
- .not('.' + tm + '-state-active')
- .not('.' + tm + '-state-disabled')
- .addClass(tm + '-state-hover');
- },
- function() {
- button
- .removeClass(tm + '-state-hover')
- .removeClass(tm + '-state-down');
- }
- )
- .appendTo($("<td/>").appendTo(tr));
- if (prevButton) {
- prevButton.addClass(tm + '-no-right');
- } else {
- button.addClass(tm + '-corner-left');
- }
- prevButton = button;
+ )
+ .appendTo(e);
+ if (!prevButton) {
+ button.addClass(tm + '-corner-left');
}
+ prevButton = button;
}
}
- });
- if (prevButton) {
- prevButton.addClass(tm + '-corner-right');
}
});
- return $("<table/>").append(tr);
- }
+ if (prevButton) {
+ prevButton.addClass(tm + '-corner-right');
+ }
+ });
}
+ return e;
+ }
- /* Resizing
- -----------------------------------------------------------------------------*/
+ function updateTitle(html) {
+ element.find('h2')
+ .html(html);
+ }
- function calcSize() {
- if (options.contentHeight) {
- suggestedViewHeight = options.contentHeight;
+ function activateButton(buttonName) {
+ element.find('span.fc-button-' + buttonName)
+ .addClass(tm + '-state-active');
+ }
+
+
+ function deactivateButton(buttonName) {
+ element.find('span.fc-button-' + buttonName)
+ .removeClass(tm + '-state-active');
+ }
+
+
+ function disableButton(buttonName) {
+ element.find('span.fc-button-' + buttonName)
+ .addClass(tm + '-state-disabled');
+ }
+
+
+ function enableButton(buttonName) {
+ element.find('span.fc-button-' + buttonName)
+ .removeClass(tm + '-state-disabled');
+ }
+
+
+ }
+
+ fc.sourceNormalizers = [];
+ fc.sourceFetchers = [];
+
+ var ajaxDefaults = {
+ dataType: 'json',
+ cache: false
+ };
+
+ var eventGUID = 1;
+
+
+ function EventManager(options, _sources) {
+ var t = this;
+
+
+ // exports
+ t.isFetchNeeded = isFetchNeeded;
+ t.fetchEvents = fetchEvents;
+ t.addEventSource = addEventSource;
+ t.removeEventSource = removeEventSource;
+ t.updateEvent = updateEvent;
+ t.renderEvent = renderEvent;
+ t.removeEvents = removeEvents;
+ t.clientEvents = clientEvents;
+ t.normalizeEvent = normalizeEvent;
+
+
+ // imports
+ var trigger = t.trigger;
+ var getView = t.getView;
+ var reportEvents = t.reportEvents;
+
+
+ // locals
+ var stickySource = { events: [] };
+ var sources = [ stickySource ];
+ var rangeStart, rangeEnd;
+ var currentFetchID = 0;
+ var pendingSourceCnt = 0;
+ var loadingLevel = 0;
+ var cache = [];
+
+
+ for (var i = 0; i < _sources.length; i++) {
+ _addEventSource(_sources[i]);
+ }
+
+
+ /* Fetching
+ -----------------------------------------------------------------------------*/
+
+
+ function isFetchNeeded(start, end) {
+ return !rangeStart || start < rangeStart || end > rangeEnd;
+ }
+
+
+ function fetchEvents(start, end) {
+ rangeStart = start;
+ rangeEnd = end;
+ cache = [];
+ var fetchID = ++currentFetchID;
+ var len = sources.length;
+ pendingSourceCnt = len;
+ for (var i = 0; i < len; i++) {
+ fetchEventSource(sources[i], fetchID);
+ }
+ }
+
+
+ function fetchEventSource(source, fetchID) {
+ _fetchEventSource(source, function(events) {
+ if (fetchID == currentFetchID) {
+ if (events) {
+ for (var i = 0; i < events.length; i++) {
+ events[i].source = source;
+ normalizeEvent(events[i]);
+ }
+ cache = cache.concat(events);
+ }
+ pendingSourceCnt--;
+ if (!pendingSourceCnt) {
+ reportEvents(cache);
+ }
}
- else if (options.height) {
- suggestedViewHeight = options.height - (header ? header.height() : 0) - vsides(content[0]);
+ });
+ }
+
+
+ function _fetchEventSource(source, callback) {
+ var i;
+ var fetchers = fc.sourceFetchers;
+ var res;
+ for (i = 0; i < fetchers.length; i++) {
+ res = fetchers[i](source, rangeStart, rangeEnd, callback);
+ if (res === true) {
+ // the fetcher is in charge. made its own async request
+ return;
}
+ else if (typeof res == 'object') {
+ // the fetcher returned a new source. process it
+ _fetchEventSource(res, callback);
+ return;
+ }
+ }
+ var events = source.events;
+ if (events) {
+ if ($.isFunction(events)) {
+ pushLoading();
+ events(cloneDate(rangeStart), cloneDate(rangeEnd), function(events) {
+ callback(events);
+ popLoading();
+ });
+ }
+ else if ($.isArray(events)) {
+ callback(events);
+ }
else {
- suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5));
+ callback();
}
+ } else {
+ var url = source.url;
+ if (url) {
+ var success = source.success;
+ var error = source.error;
+ var complete = source.complete;
+ var data = $.extend({}, source.data || {});
+ var startParam = firstDefined(source.startParam, options.startParam);
+ var endParam = firstDefined(source.endParam, options.endParam);
+ if (startParam) {
+ data[startParam] = Math.round(+rangeStart / 1000);
+ }
+ if (endParam) {
+ data[endParam] = Math.round(+rangeEnd / 1000);
+ }
+ pushLoading();
+ $.ajax($.extend({}, ajaxDefaults, source, {
+ data: data,
+ success: function(events) {
+ events = events || [];
+ var res = applyAll(success, this, arguments);
+ if ($.isArray(res)) {
+ events = res;
+ }
+ callback(events);
+ },
+ error: function() {
+ applyAll(error, this, arguments);
+ callback();
+ },
+ complete: function() {
+ applyAll(complete, this, arguments);
+ popLoading();
+ }
+ }));
+ } else {
+ callback();
+ }
}
+ }
- function setSize(dateChanged) {
- ignoreWindowResize++;
- view.setHeight(suggestedViewHeight, dateChanged);
- if (absoluteViewElement) {
- absoluteViewElement.css('position', 'relative');
- absoluteViewElement = null;
- }
- view.setWidth(content.width(), dateChanged);
- ignoreWindowResize--;
+ /* Sources
+ -----------------------------------------------------------------------------*/
+
+
+ function addEventSource(source) {
+ source = _addEventSource(source);
+ if (source) {
+ pendingSourceCnt++;
+ fetchEventSource(source, currentFetchID); // will eventually call reportEvents
}
+ }
- function windowResize() {
- if (!ignoreWindowResize) {
- if (view.start) { // view has already been rendered
- var uid = ++resizeUID;
- setTimeout(function() { // add a delay
- if (uid == resizeUID && !ignoreWindowResize && elementVisible()) {
- if (elementOuterWidth != (elementOuterWidth = element.outerWidth())) {
- ignoreWindowResize++; // in case the windowResize callback changes the height
- sizeChanged();
- view.trigger('windowResize', _element);
- ignoreWindowResize--;
- }
- }
- }, 200);
+ function _addEventSource(source) {
+ if ($.isFunction(source) || $.isArray(source)) {
+ source = { events: source };
+ }
+ else if (typeof source == 'string') {
+ source = { url: source };
+ }
+ if (typeof source == 'object') {
+ normalizeSource(source);
+ sources.push(source);
+ return source;
+ }
+ }
+
+
+ function removeEventSource(source) {
+ sources = $.grep(sources, function(src) {
+ return !isSourcesEqual(src, source);
+ });
+ // remove all client events from that source
+ cache = $.grep(cache, function(e) {
+ return !isSourcesEqual(e.source, source);
+ });
+ reportEvents(cache);
+ }
+
+
+ /* Manipulation
+ -----------------------------------------------------------------------------*/
+
+
+ function updateEvent(event) { // update an existing event
+ var i, len = cache.length, e,
+ defaultEventEnd = getView().defaultEventEnd, // getView???
+ startDelta = event.start - event._start,
+ endDelta = event.end ?
+ (event.end - (event._end || defaultEventEnd(event))) // event._end would be null if event.end
+ : 0; // was null and event was just resized
+ for (i = 0; i < len; i++) {
+ e = cache[i];
+ if (e._id == event._id && e != event) {
+ e.start = new Date(+e.start + startDelta);
+ if (event.end) {
+ if (e.end) {
+ e.end = new Date(+e.end + endDelta);
+ } else {
+ e.end = new Date(+defaultEventEnd(e) + endDelta);
+ }
} else {
- // calendar must have been initialized in a 0x0 iframe that has just been resized
- lateRender();
+ e.end = null;
}
+ e.title = event.title;
+ e.url = event.url;
+ e.allDay = event.allDay;
+ e.className = event.className;
+ e.editable = event.editable;
+ e.color = event.color;
+ e.backgroudColor = event.backgroudColor;
+ e.borderColor = event.borderColor;
+ e.textColor = event.textColor;
+ normalizeEvent(e);
}
}
+ normalizeEvent(event);
+ reportEvents(cache);
+ }
- $(window).resize(windowResize);
+ function renderEvent(event, stick) {
+ normalizeEvent(event);
+ if (!event.source) {
+ if (stick) {
+ stickySource.events.push(event);
+ event.source = stickySource;
+ }
+ cache.push(event);
+ }
+ reportEvents(cache);
+ }
- // let's begin...
- changeView(options.defaultView);
+ function removeEvents(filter) {
+ if (!filter) { // remove all
+ cache = [];
+ // clear all array sources
+ for (var i = 0; i < sources.length; i++) {
+ if ($.isArray(sources[i].events)) {
+ sources[i].events = [];
+ }
+ }
+ } else {
+ if (!$.isFunction(filter)) { // an event ID
+ var id = filter + '';
+ filter = function(e) {
+ return e._id == id;
+ };
+ }
+ cache = $.grep(cache, filter, true);
+ // remove events from array sources
+ for (var i = 0; i < sources.length; i++) {
+ if ($.isArray(sources[i].events)) {
+ sources[i].events = $.grep(sources[i].events, filter, true);
+ }
+ }
+ }
+ reportEvents(cache);
+ }
- // needed for IE in a 0x0 iframe, b/c when it is resized, never triggers a windowResize
- if (!bodyVisible()) {
- lateRender();
+
+ function clientEvents(filter) {
+ if ($.isFunction(filter)) {
+ return $.grep(cache, filter);
}
+ else if (filter) { // an event ID
+ filter += '';
+ return $.grep(cache, function(e) {
+ return e._id == filter;
+ });
+ }
+ return cache; // else, return all
+ }
- // called when we know the calendar couldn't be rendered when it was initialized,
- // but we think it's ready now
- function lateRender() {
- setTimeout(function() { // IE7 needs this so dimensions are calculated correctly
- if (!view.start && bodyVisible()) { // !view.start makes sure this never happens more than once
- render();
- }
- }, 0);
+ /* Loading State
+ -----------------------------------------------------------------------------*/
+
+
+ function pushLoading() {
+ if (!loadingLevel++) {
+ trigger('loading', null, true);
}
+ }
- });
+ function popLoading() {
+ if (!--loadingLevel) {
+ trigger('loading', null, false);
+ }
+ }
- return this;
- };
+ /* Event Normalization
+ -----------------------------------------------------------------------------*/
- /* Important Event Utilities
- -----------------------------------------------------------------------------*/
+ function normalizeEvent(event) {
+ var source = event.source || {};
+ var ignoreTimezone = firstDefined(source.ignoreTimezone, options.ignoreTimezone);
+ event._id = event._id || (event.id === undefined ? '_fc' + eventGUID++ : event.id + '');
+ if (event.date) {
+ if (!event.start) {
+ event.start = event.date;
+ }
+ delete event.date;
+ }
+ event._start = cloneDate(event.start = parseDate(event.start, ignoreTimezone));
+ event.end = parseDate(event.end, ignoreTimezone);
+ if (event.end && event.end <= event.start) {
+ event.end = null;
+ }
+ event._end = event.end ? cloneDate(event.end) : null;
+ if (event.allDay === undefined) {
+ event.allDay = firstDefined(source.allDayDefault, options.allDayDefault);
+ }
+ if (event.className) {
+ if (typeof event.className == 'string') {
+ event.className = event.className.split(/\s+/);
+ }
+ } else {
+ event.className = [];
+ }
+ // TODO: if there is no start date, return false to indicate an invalid event
+ }
- var fakeID = 0;
- function normalizeEvent(event, options) {
- event._id = event._id || (event.id === undefined ? '_fc' + fakeID++ : event.id + '');
- if (event.date) {
- if (!event.start) {
- event.start = event.date;
+ /* Utils
+ ------------------------------------------------------------------------------*/
+
+
+ function normalizeSource(source) {
+ if (source.className) {
+ // TODO: repeat code, same code for event classNames
+ if (typeof source.className == 'string') {
+ source.className = source.className.split(/\s+/);
+ }
+ } else {
+ source.className = [];
}
- delete event.date;
+ var normalizers = fc.sourceNormalizers;
+ for (var i = 0; i < normalizers.length; i++) {
+ normalizers[i](source);
+ }
}
- event._start = cloneDate(event.start = parseDate(event.start));
- event.end = parseDate(event.end);
- if (event.end && event.end <= event.start) {
- event.end = null;
+
+
+ function isSourcesEqual(source1, source2) {
+ return source1 && source2 && getSourcePrimitive(source1) == getSourcePrimitive(source2);
}
- event._end = event.end ? cloneDate(event.end) : null;
- if (event.allDay === undefined) {
- event.allDay = options.allDayDefault;
+
+
+ function getSourcePrimitive(source) {
+ return ((typeof source == 'object') ? (source.events || source.url) : '') || source;
}
- if (event.className) {
- if (typeof event.className == 'string') {
- event.className = event.className.split(/\s+/);
- }
- } else {
- event.className = [];
- }
+
+
}
- // TODO: if there is no start date, return false to indicate an invalid event
+ fc.addDays = addDays;
+ fc.cloneDate = cloneDate;
+ fc.parseDate = parseDate;
+ fc.parseISO8601 = parseISO8601;
+ fc.parseTime = parseTime;
+ fc.formatDate = formatDate;
+ fc.formatDates = formatDates;
- /* Grid-based Views: month, basicWeek, basicDay
+
+ /* Date Math
-----------------------------------------------------------------------------*/
- setDefaults({
- weekMode: 'fixed'
- });
+ var dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'],
+ DAY_MS = 86400000,
+ HOUR_MS = 3600000,
+ MINUTE_MS = 60000;
- views.month = function(element, options) {
- return new Grid(element, options, {
- render: function(date, delta) {
- if (delta) {
- addMonths(date, delta);
- date.setDate(1);
- }
- // start/end
- var start = this.start = cloneDate(date, true);
- start.setDate(1);
- this.end = addMonths(cloneDate(start), 1);
- // visStart/visEnd
- var visStart = this.visStart = cloneDate(start),
- visEnd = this.visEnd = cloneDate(this.end),
- nwe = options.weekends ? 0 : 1;
- if (nwe) {
- skipWeekend(visStart);
- skipWeekend(visEnd, -1, true);
- }
- addDays(visStart, -((visStart.getDay() - Math.max(options.firstDay, nwe) + 7) % 7));
- addDays(visEnd, (7 - visEnd.getDay() + Math.max(options.firstDay, nwe)) % 7);
- // row count
- var rowCnt = Math.round((visEnd - visStart) / (DAY_MS * 7));
- if (options.weekMode == 'fixed') {
- addDays(visEnd, (6 - rowCnt) * 7);
- rowCnt = 6;
- }
- // title
- this.title = formatDate(
- start,
- this.option('titleFormat'),
- options
- );
- // render
- this.renderGrid(
- rowCnt, options.weekends ? 7 : 5,
- this.option('columnFormat'),
- true
- );
+
+ function addYears(d, n, keepTime) {
+ d.setFullYear(d.getFullYear() + n);
+ if (!keepTime) {
+ clearTime(d);
+ }
+ return d;
+ }
+
+
+ function addMonths(d, n, keepTime) { // prevents day overflow/underflow
+ if (+d) { // prevent infinite looping on invalid dates
+ var m = d.getMonth() + n,
+ check = cloneDate(d);
+ check.setDate(1);
+ check.setMonth(m);
+ d.setMonth(m);
+ if (!keepTime) {
+ clearTime(d);
}
- });
- };
+ while (d.getMonth() != check.getMonth()) {
+ d.setDate(d.getDate() + (d < check ? 1 : -1));
+ }
+ }
+ return d;
+ }
- views.basicWeek = function(element, options) {
- return new Grid(element, options, {
- render: function(date, delta) {
- if (delta) {
- addDays(date, delta * 7);
- }
- var visStart = this.visStart = cloneDate(
- this.start = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7))
- ),
- visEnd = this.visEnd = cloneDate(
- this.end = addDays(cloneDate(visStart), 7)
- );
- if (!options.weekends) {
- skipWeekend(visStart);
- skipWeekend(visEnd, -1, true);
- }
- this.title = formatDates(
- visStart,
- addDays(cloneDate(visEnd), -1),
- this.option('titleFormat'),
- options
- );
- this.renderGrid(
- 1, options.weekends ? 7 : 5,
- this.option('columnFormat'),
- false
- );
+
+ function addDays(d, n, keepTime) { // deals with daylight savings
+ if (+d) {
+ var dd = d.getDate() + n,
+ check = cloneDate(d);
+ check.setHours(9); // set to middle of day
+ check.setDate(dd);
+ d.setDate(dd);
+ if (!keepTime) {
+ clearTime(d);
}
- });
- };
+ fixDate(d, check);
+ }
+ return d;
+ }
- views.basicDay = function(element, options) {
- return new Grid(element, options, {
- render: function(date, delta) {
- if (delta) {
- addDays(date, delta);
- if (!options.weekends) {
- skipWeekend(date, delta < 0 ? -1 : 1);
- }
- }
- this.title = formatDate(date, this.option('titleFormat'), options);
- this.start = this.visStart = cloneDate(date, true);
- this.end = this.visEnd = addDays(cloneDate(this.start), 1);
- this.renderGrid(
- 1, 1,
- this.option('columnFormat'),
- false
- );
+
+ function fixDate(d, check) { // force d to be on check's YMD, for daylight savings purposes
+ if (+d) { // prevent infinite looping on invalid dates
+ while (d.getDate() != check.getDate()) {
+ d.setTime(+d + (d < check ? 1 : -1) * HOUR_MS);
}
- });
- };
+ }
+ }
- // rendering bugs
+ function addMinutes(d, n) {
+ d.setMinutes(d.getMinutes() + n);
+ return d;
+ }
- var tdHeightBug;
+ function clearTime(d) {
+ d.setHours(0);
+ d.setMinutes(0);
+ d.setSeconds(0);
+ d.setMilliseconds(0);
+ return d;
+ }
- function Grid(element, options, methods) {
- var tm, firstDay,
- nwe, // no weekends (int)
- rtl, dis, dit, // day index sign / translate
- viewWidth, viewHeight,
- rowCnt, colCnt,
- colWidth,
- thead, tbody,
- cachedEvents = [],
- segmentContainer,
- dayContentPositions = new HorizontalPositionCache(function(dayOfWeek) {
- return tbody.find('td:eq(' + ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt) + ') div div');
- }),
- selectionManager,
- selectionMatrix,
- // ...
+ function cloneDate(d, dontKeepTime) {
+ if (dontKeepTime) {
+ return clearTime(new Date(+d));
+ }
+ return new Date(+d);
+ }
- // initialize superclass
- view = $.extend(this, viewMethods, methods, {
- renderGrid: renderGrid,
- renderEvents: renderEvents,
- rerenderEvents: rerenderEvents,
- clearEvents: clearEvents,
- setHeight: setHeight,
- setWidth: setWidth,
- defaultEventEnd: function(event) { // calculates an end if event doesnt have one, mostly for resizing
- return cloneDate(event.start);
- }
- });
- view.init(element, options);
+ function zeroDate() { // returns a Date with time 00:00:00 and dateOfMonth=1
+ var i = 0, d;
+ do {
+ d = new Date(1970, i++, 1);
+ } while (d.getHours()); // != 0
+ return d;
+ }
- /* Grid Rendering
- -----------------------------------------------------------------------------*/
+ function skipWeekend(date, inc, excl) {
+ inc = inc || 1;
+ while (!date.getDay() || (excl && date.getDay() == 1 || !excl && date.getDay() == 6)) {
+ addDays(date, inc);
+ }
+ return date;
+ }
- disableTextSelection(element.addClass('fc-grid'));
+ function dayDiff(d1, d2) { // d1 - d2
+ return Math.round((cloneDate(d1, true) - cloneDate(d2, true)) / DAY_MS);
+ }
- function renderGrid(r, c, colFormat, showNumbers) {
- rowCnt = r;
- colCnt = c;
+ function setYMD(date, y, m, d) {
+ if (y !== undefined && y != date.getFullYear()) {
+ date.setDate(1);
+ date.setMonth(0);
+ date.setFullYear(y);
+ }
+ if (m !== undefined && m != date.getMonth()) {
+ date.setDate(1);
+ date.setMonth(m);
+ }
+ if (d !== undefined) {
+ date.setDate(d);
+ }
+ }
- // update option-derived variables
- tm = options.theme ? 'ui' : 'fc';
- nwe = options.weekends ? 0 : 1;
- firstDay = options.firstDay;
- if (rtl = options.isRTL) {
- dis = -1;
- dit = colCnt - 1;
- } else {
- dis = 1;
- dit = 0;
+
+ /* Date Parsing
+ -----------------------------------------------------------------------------*/
+
+
+ function parseDate(s, ignoreTimezone) { // ignoreTimezone defaults to true
+ if (typeof s == 'object') { // already a Date object
+ return s;
+ }
+ if (typeof s == 'number') { // a UNIX timestamp
+ return new Date(s * 1000);
+ }
+ if (typeof s == 'string') {
+ if (s.match(/^\d+(\.\d+)?$/)) { // a UNIX timestamp
+ return new Date(parseFloat(s) * 1000);
}
+ if (ignoreTimezone === undefined) {
+ ignoreTimezone = true;
+ }
+ return parseISO8601(s, ignoreTimezone) || (s ? new Date(s) : null);
+ }
+ // TODO: never return invalid dates (like from new Date(<string>)), return null instead
+ return null;
+ }
- var month = view.start.getMonth(),
- today = clearTime(new Date()),
- s, i, j, d = cloneDate(view.visStart);
- if (!tbody) { // first time, build all cells from scratch
+ function parseISO8601(s, ignoreTimezone) { // ignoreTimezone defaults to false
+ // derived from http://delete.me.uk/2005/03/iso8601.html
+ // TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html
+ var m = s.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2})(:?([0-9]{2}))?))?)?)?)?$/);
+ if (!m) {
+ return null;
+ }
+ var date = new Date(m[1], 0, 1);
+ if (ignoreTimezone || !m[13]) {
+ var check = new Date(m[1], 0, 1, 9, 0);
+ if (m[3]) {
+ date.setMonth(m[3] - 1);
+ check.setMonth(m[3] - 1);
+ }
+ if (m[5]) {
+ date.setDate(m[5]);
+ check.setDate(m[5]);
+ }
+ fixDate(date, check);
+ if (m[7]) {
+ date.setHours(m[7]);
+ }
+ if (m[8]) {
+ date.setMinutes(m[8]);
+ }
+ if (m[10]) {
+ date.setSeconds(m[10]);
+ }
+ if (m[12]) {
+ date.setMilliseconds(Number("0." + m[12]) * 1000);
+ }
+ fixDate(date, check);
+ } else {
+ date.setUTCFullYear(
+ m[1],
+ m[3] ? m[3] - 1 : 0,
+ m[5] || 1
+ );
+ date.setUTCHours(
+ m[7] || 0,
+ m[8] || 0,
+ m[10] || 0,
+ m[12] ? Number("0." + m[12]) * 1000 : 0
+ );
+ if (m[14]) {
+ var offset = Number(m[16]) * 60 + (m[18] ? Number(m[18]) : 0);
+ offset *= m[15] == '-' ? 1 : -1;
+ date = new Date(+date + (offset * 60 * 1000));
+ }
+ }
+ return date;
+ }
- var table = $("<table/>").appendTo(element);
- s = "<thead><tr>";
- for (i = 0; i < colCnt; i++) {
- s += "<th class='fc-" +
- dayIDs[d.getDay()] + ' ' + // needs to be first
- tm + '-state-default' +
- (i == dit ? ' fc-leftmost' : '') +
- "'>" + formatDate(d, colFormat, options) + "</th>";
- addDays(d, 1);
- if (nwe) {
- skipWeekend(d);
- }
+ function parseTime(s) { // returns minutes since start of day
+ if (typeof s == 'number') { // an hour
+ return s * 60;
+ }
+ if (typeof s == 'object') { // a Date object
+ return s.getHours() * 60 + s.getMinutes();
+ }
+ var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/);
+ if (m) {
+ var h = parseInt(m[1], 10);
+ if (m[3]) {
+ h %= 12;
+ if (m[3].toLowerCase().charAt(0) == 'p') {
+ h += 12;
}
- thead = $(s + "</tr></thead>").appendTo(table);
+ }
+ return h * 60 + (m[2] ? parseInt(m[2], 10) : 0);
+ }
+ }
- s = "<tbody>";
- d = cloneDate(view.visStart);
- for (i = 0; i < rowCnt; i++) {
- s += "<tr class='fc-week" + i + "'>";
- for (j = 0; j < colCnt; j++) {
- s += "<td class='fc-" +
- dayIDs[d.getDay()] + ' ' + // needs to be first
- tm + '-state-default fc-day' + (i * colCnt + j) +
- (j == dit ? ' fc-leftmost' : '') +
- (rowCnt > 1 && d.getMonth() != month ? ' fc-other-month' : '') +
- (+d == +today ?
- ' fc-today ' + tm + '-state-highlight' :
- ' fc-not-today') + "'>" +
- (showNumbers ? "<div class='fc-day-number'>" + d.getDate() + "</div>" : '') +
- "<div class='fc-day-content'><div style='position:relative'> </div></div></td>";
- addDays(d, 1);
- if (nwe) {
- skipWeekend(d);
- }
- }
- s += "</tr>";
- }
- tbody = $(s + "</tbody>").appendTo(table);
- dayBind(tbody.find('td'));
- segmentContainer = $("<div style='position:absolute;z-index:8;top:0;left:0'/>").appendTo(element);
+ /* Date Formatting
+ -----------------------------------------------------------------------------*/
+// TODO: use same function formatDate(date, [date2], format, [options])
- } else { // NOT first time, reuse as many cells as possible
- clearEvents();
+ function formatDate(date, format, options) {
+ return formatDates(date, null, format, options);
+ }
- var prevRowCnt = tbody.find('tr').length;
- if (rowCnt < prevRowCnt) {
- tbody.find('tr:gt(' + (rowCnt - 1) + ')').remove(); // remove extra rows
- }
- else if (rowCnt > prevRowCnt) { // needs to create new rows...
- s = '';
- for (i = prevRowCnt; i < rowCnt; i++) {
- s += "<tr class='fc-week" + i + "'>";
- for (j = 0; j < colCnt; j++) {
- s += "<td class='fc-" +
- dayIDs[d.getDay()] + ' ' + // needs to be first
- tm + '-state-default fc-new fc-day' + (i * colCnt + j) +
- (j == dit ? ' fc-leftmost' : '') + "'>" +
- (showNumbers ? "<div class='fc-day-number'></div>" : '') +
- "<div class='fc-day-content'><div style='position:relative'> </div></div>" +
- "</td>";
- addDays(d, 1);
- if (nwe) {
- skipWeekend(d);
+
+ function formatDates(date1, date2, format, options) {
+ options = options || defaults;
+ var date = date1,
+ otherDate = date2,
+ i, len = format.length, c,
+ i2, formatter,
+ res = '';
+ for (i = 0; i < len; i++) {
+ c = format.charAt(i);
+ if (c == "'") {
+ for (i2 = i + 1; i2 < len; i2++) {
+ if (format.charAt(i2) == "'") {
+ if (date) {
+ if (i2 == i + 1) {
+ res += "'";
+ } else {
+ res += format.substring(i + 1, i2);
}
+ i = i2;
}
- s += "</tr>";
+ break;
}
- tbody.append(s);
}
- dayBind(tbody.find('td.fc-new').removeClass('fc-new'));
-
- // re-label and re-class existing cells
- d = cloneDate(view.visStart);
- tbody.find('td').each(function() {
- var td = $(this);
- if (rowCnt > 1) {
- if (d.getMonth() == month) {
- td.removeClass('fc-other-month');
- } else {
- td.addClass('fc-other-month');
+ }
+ else if (c == '(') {
+ for (i2 = i + 1; i2 < len; i2++) {
+ if (format.charAt(i2) == ')') {
+ var subres = formatDate(date, format.substring(i + 1, i2), options);
+ if (parseInt(subres.replace(/\D/, ''), 10)) {
+ res += subres;
}
+ i = i2;
+ break;
}
- if (+d == +today) {
- td.removeClass('fc-not-today')
- .addClass('fc-today')
- .addClass(tm + '-state-highlight');
- } else {
- td.addClass('fc-not-today')
- .removeClass('fc-today')
- .removeClass(tm + '-state-highlight');
+ }
+ }
+ else if (c == '[') {
+ for (i2 = i + 1; i2 < len; i2++) {
+ if (format.charAt(i2) == ']') {
+ var subformat = format.substring(i + 1, i2);
+ var subres = formatDate(date, subformat, options);
+ if (subres != formatDate(otherDate, subformat, options)) {
+ res += subres;
+ }
+ i = i2;
+ break;
}
- td.find('div.fc-day-number').text(d.getDate());
- addDays(d, 1);
- if (nwe) {
- skipWeekend(d);
+ }
+ }
+ else if (c == '{') {
+ date = date2;
+ otherDate = date1;
+ }
+ else if (c == '}') {
+ date = date1;
+ otherDate = date2;
+ }
+ else {
+ for (i2 = len; i2 > i; i2--) {
+ if (formatter = dateFormatters[format.substring(i, i2)]) {
+ if (date) {
+ res += formatter(date, options);
+ }
+ i = i2 - 1;
+ break;
}
- });
+ }
+ if (i2 == i) {
+ if (date) {
+ res += c;
+ }
+ }
+ }
+ }
+ return res;
+ }
- if (rowCnt == 1) { // more changes likely (week or day view)
+ ;
- // redo column header text and class
- d = cloneDate(view.visStart);
- thead.find('th').each(function() {
- $(this).text(formatDate(d, colFormat, options));
- this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]);
- addDays(d, 1);
- if (nwe) {
- skipWeekend(d);
- }
- });
- // redo cell day-of-weeks
- d = cloneDate(view.visStart);
- tbody.find('td').each(function() {
- this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]);
- addDays(d, 1);
- if (nwe) {
- skipWeekend(d);
- }
- });
+ var dateFormatters = {
+ s : function(d) {
+ return d.getSeconds()
+ },
+ ss : function(d) {
+ return zeroPad(d.getSeconds())
+ },
+ m : function(d) {
+ return d.getMinutes()
+ },
+ mm : function(d) {
+ return zeroPad(d.getMinutes())
+ },
+ h : function(d) {
+ return d.getHours() % 12 || 12
+ },
+ hh : function(d) {
+ return zeroPad(d.getHours() % 12 || 12)
+ },
+ H : function(d) {
+ return d.getHours()
+ },
+ HH : function(d) {
+ return zeroPad(d.getHours())
+ },
+ d : function(d) {
+ return d.getDate()
+ },
+ dd : function(d) {
+ return zeroPad(d.getDate())
+ },
+ ddd : function(d, o) {
+ return o.dayNamesShort[d.getDay()]
+ },
+ dddd: function(d, o) {
+ return o.dayNames[d.getDay()]
+ },
+ M : function(d) {
+ return d.getMonth() + 1
+ },
+ MM : function(d) {
+ return zeroPad(d.getMonth() + 1)
+ },
+ MMM : function(d, o) {
+ return o.monthNamesShort[d.getMonth()]
+ },
+ MMMM: function(d, o) {
+ return o.monthNames[d.getMonth()]
+ },
+ yy : function(d) {
+ return (d.getFullYear() + '').substring(2)
+ },
+ yyyy: function(d) {
+ return d.getFullYear()
+ },
+ t : function(d) {
+ return d.getHours() < 12 ? 'a' : 'p'
+ },
+ tt : function(d) {
+ return d.getHours() < 12 ? 'am' : 'pm'
+ },
+ T : function(d) {
+ return d.getHours() < 12 ? 'A' : 'P'
+ },
+ TT : function(d) {
+ return d.getHours() < 12 ? 'AM' : 'PM'
+ },
+ u : function(d) {
+ return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'")
+ },
+ S : function(d) {
+ var date = d.getDate();
+ if (date > 10 && date < 20) {
+ return 'th';
+ }
+ return ['st', 'nd', 'rd'][date % 10 - 1] || 'th';
+ }
+ };
- }
- }
+ fc.applyAll = applyAll;
+
+ /* Event Date Math
+ -----------------------------------------------------------------------------*/
+
+
+ function exclEndDay(event) {
+ if (event.end) {
+ return _exclEndDay(event.end, event.allDay);
+ } else {
+ return addDays(cloneDate(event.start), 1);
}
+ }
- function setHeight(height) {
- viewHeight = height;
- var leftTDs = tbody.find('tr td:first-child'),
- tbodyHeight = viewHeight - thead.height(),
- rowHeight1, rowHeight2;
- if (options.weekMode == 'variable') {
- rowHeight1 = rowHeight2 = Math.floor(tbodyHeight / (rowCnt == 1 ? 2 : 6));
- } else {
- rowHeight1 = Math.floor(tbodyHeight / rowCnt);
- rowHeight2 = tbodyHeight - rowHeight1 * (rowCnt - 1);
+ function _exclEndDay(end, allDay) {
+ end = cloneDate(end);
+ return allDay || end.getHours() || end.getMinutes() ? addDays(end, 1) : clearTime(end);
+ }
+
+
+ function segCmp(a, b) {
+ return (b.msLength - a.msLength) * 100 + (a.event.start - b.event.start);
+ }
+
+
+ function segsCollide(seg1, seg2) {
+ return seg1.end > seg2.start && seg1.start < seg2.end;
+ }
+
+
+ /* Event Sorting
+ -----------------------------------------------------------------------------*/
+
+
+// event rendering utilities
+ function sliceSegs(events, visEventEnds, start, end) {
+ var segs = [],
+ i, len = events.length, event,
+ eventStart, eventEnd,
+ segStart, segEnd,
+ isStart, isEnd;
+ for (i = 0; i < len; i++) {
+ event = events[i];
+ eventStart = event.start;
+ eventEnd = visEventEnds[i];
+ if (eventEnd > start && eventStart < end) {
+ if (eventStart < start) {
+ segStart = cloneDate(start);
+ isStart = false;
+ } else {
+ segStart = eventStart;
+ isStart = true;
+ }
+ if (eventEnd > end) {
+ segEnd = cloneDate(end);
+ isEnd = false;
+ } else {
+ segEnd = eventEnd;
+ isEnd = true;
+ }
+ segs.push({
+ event: event,
+ start: segStart,
+ end: segEnd,
+ isStart: isStart,
+ isEnd: isEnd,
+ msLength: segEnd - segStart
+ });
}
- if (tdHeightBug === undefined) {
- // bug in firefox where cell height includes padding
- var tr = tbody.find('tr:first'),
- td = tr.find('td:first');
- td.height(rowHeight1);
- tdHeightBug = rowHeight1 != td.height();
+ }
+ return segs.sort(segCmp);
+ }
+
+
+// event rendering calculation utilities
+ function stackSegs(segs) {
+ var levels = [],
+ i, len = segs.length, seg,
+ j, collide, k;
+ for (i = 0; i < len; i++) {
+ seg = segs[i];
+ j = 0; // the level index where seg should belong
+ while (true) {
+ collide = false;
+ if (levels[j]) {
+ for (k = 0; k < levels[j].length; k++) {
+ if (segsCollide(levels[j][k], seg)) {
+ collide = true;
+ break;
+ }
+ }
+ }
+ if (collide) {
+ j++;
+ } else {
+ break;
+ }
}
- if (tdHeightBug) {
- leftTDs.slice(0, -1).height(rowHeight1);
- leftTDs.slice(-1).height(rowHeight2);
+ if (levels[j]) {
+ levels[j].push(seg);
} else {
- setOuterHeight(leftTDs.slice(0, -1), rowHeight1);
- setOuterHeight(leftTDs.slice(-1), rowHeight2);
+ levels[j] = [seg];
}
}
+ return levels;
+ }
- function setWidth(width) {
- viewWidth = width;
- dayContentPositions.clear();
- setOuterWidth(
- thead.find('th').slice(0, -1),
- colWidth = Math.floor(viewWidth / colCnt)
- );
+ /* Event Element Binding
+ -----------------------------------------------------------------------------*/
+
+
+ function lazySegBind(container, segs, bindHandlers) {
+ container.unbind('mouseover').mouseover(function(ev) {
+ var parent = ev.target, e,
+ i, seg;
+ while (parent != this) {
+ e = parent;
+ parent = parent.parentNode;
+ }
+ if ((i = e._fci) !== undefined) {
+ e._fci = undefined;
+ seg = segs[i];
+ bindHandlers(seg.event, seg.element, seg);
+ $(ev.target).trigger(ev);
+ }
+ ev.stopPropagation();
+ });
+ }
+
+
+ /* Element Dimensions
+ -----------------------------------------------------------------------------*/
+
+
+ function setOuterWidth(element, width, includeMargins) {
+ for (var i = 0, e; i < element.length; i++) {
+ e = $(element[i]);
+ e.width(Math.max(0, width - hsides(e, includeMargins)));
}
+ }
- /* Event Rendering
- -----------------------------------------------------------------------------*/
+ function setOuterHeight(element, height, includeMargins) {
+ for (var i = 0, e; i < element.length; i++) {
+ e = $(element[i]);
+ e.height(Math.max(0, height - vsides(e, includeMargins)));
+ }
+ }
- function renderEvents(events) {
- view.reportEvents(cachedEvents = events);
- renderSegs(compileSegs(events));
+// TODO: curCSS has been deprecated (jQuery 1.4.3 - 10/16/2010)
+
+
+ function hsides(element, includeMargins) {
+ return hpadding(element) + hborders(element) + (includeMargins ? hmargins(element) : 0);
+ }
+
+
+ function hpadding(element) {
+ return (parseFloat($.curCSS(element[0], 'paddingLeft', true)) || 0) +
+ (parseFloat($.curCSS(element[0], 'paddingRight', true)) || 0);
+ }
+
+
+ function hmargins(element) {
+ return (parseFloat($.curCSS(element[0], 'marginLeft', true)) || 0) +
+ (parseFloat($.curCSS(element[0], 'marginRight', true)) || 0);
+ }
+
+
+ function hborders(element) {
+ return (parseFloat($.curCSS(element[0], 'borderLeftWidth', true)) || 0) +
+ (parseFloat($.curCSS(element[0], 'borderRightWidth', true)) || 0);
+ }
+
+
+ function vsides(element, includeMargins) {
+ return vpadding(element) + vborders(element) + (includeMargins ? vmargins(element) : 0);
+ }
+
+
+ function vpadding(element) {
+ return (parseFloat($.curCSS(element[0], 'paddingTop', true)) || 0) +
+ (parseFloat($.curCSS(element[0], 'paddingBottom', true)) || 0);
+ }
+
+
+ function vmargins(element) {
+ return (parseFloat($.curCSS(element[0], 'marginTop', true)) || 0) +
+ (parseFloat($.curCSS(element[0], 'marginBottom', true)) || 0);
+ }
+
+
+ function vborders(element) {
+ return (parseFloat($.curCSS(element[0], 'borderTopWidth', true)) || 0) +
+ (parseFloat($.curCSS(element[0], 'borderBottomWidth', true)) || 0);
+ }
+
+
+ function setMinHeight(element, height) {
+ height = (typeof height == 'number' ? height + 'px' : height);
+ element.each(function(i, _element) {
+ _element.style.cssText += ';min-height:' + height + ';_height:' + height;
+ // why can't we just use .css() ? i forget
+ });
+ }
+
+
+ /* Misc Utils
+ -----------------------------------------------------------------------------*/
+
+
+//TODO: arraySlice
+//TODO: isFunction, grep ?
+
+
+ function noop() {
+ }
+
+
+ function cmp(a, b) {
+ return a - b;
+ }
+
+
+ function arrayMax(a) {
+ return Math.max.apply(Math, a);
+ }
+
+
+ function zeroPad(n) {
+ return (n < 10 ? '0' : '') + n;
+ }
+
+
+ function smartProperty(obj, name) { // get a camel-cased/namespaced property of an object
+ if (obj[name] !== undefined) {
+ return obj[name];
}
+ var parts = name.split(/(?=[A-Z])/),
+ i = parts.length - 1, res;
+ for (; i >= 0; i--) {
+ res = obj[parts[i].toLowerCase()];
+ if (res !== undefined) {
+ return res;
+ }
+ }
+ return obj[''];
+ }
- function rerenderEvents(modifiedEventId) {
- clearEvents();
- renderSegs(compileSegs(cachedEvents), modifiedEventId);
+ function htmlEscape(s) {
+ return s.replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')
+ .replace(/'/g, ''')
+ .replace(/"/g, '"')
+ .replace(/\n/g, '<br />');
+ }
+
+
+ function cssKey(_element) {
+ return _element.id + '/' + _element.className + '/' + _element.style.cssText.replace(/(^|;)\s*(top|left|width|height)\s*:[^;]*/ig, '');
+ }
+
+
+ function disableTextSelection(element) {
+ element
+ .attr('unselectable', 'on')
+ .css('MozUserSelect', 'none')
+ .bind('selectstart.ui', function() {
+ return false;
+ });
+ }
+
+
+ /*
+ function enableTextSelection(element) {
+ element
+ .attr('unselectable', 'off')
+ .css('MozUserSelect', '')
+ .unbind('selectstart.ui');
+ }
+ */
+
+
+ function markFirstLast(e) {
+ e.children()
+ .removeClass('fc-first fc-last')
+ .filter(':first-child')
+ .addClass('fc-first')
+ .end()
+ .filter(':last-child')
+ .addClass('fc-last');
+ }
+
+
+ function setDayID(cell, date) {
+ cell.each(function(i, _cell) {
+ _cell.className = _cell.className.replace(/^fc-\w*/, 'fc-' + dayIDs[date.getDay()]);
+ // TODO: make a way that doesn't rely on order of classes
+ });
+ }
+
+
+ function getSkinCss(event, opt) {
+ var source = event.source || {};
+ var eventColor = event.color;
+ var sourceColor = source.color;
+ var optionColor = opt('eventColor');
+ var backgroundColor =
+ event.backgroundColor ||
+ eventColor ||
+ source.backgroundColor ||
+ sourceColor ||
+ opt('eventBackgroundColor') ||
+ optionColor;
+ var borderColor =
+ event.borderColor ||
+ eventColor ||
+ source.borderColor ||
+ sourceColor ||
+ opt('eventBorderColor') ||
+ optionColor;
+ var textColor =
+ event.textColor ||
+ source.textColor ||
+ opt('eventTextColor');
+ var statements = [];
+ if (backgroundColor) {
+ statements.push('background-color:' + backgroundColor);
}
+ if (borderColor) {
+ statements.push('border-color:' + borderColor);
+ }
+ if (textColor) {
+ statements.push('color:' + textColor);
+ }
+ return statements.join(';');
+ }
- function clearEvents() {
- view._clearEvents(); // only clears the hashes
- segmentContainer.empty();
+ function applyAll(functions, thisObj, args) {
+ if ($.isFunction(functions)) {
+ functions = [ functions ];
}
+ if (functions) {
+ var i;
+ var ret;
+ for (i = 0; i < functions.length; i++) {
+ ret = functions[i].apply(thisObj, args) || ret;
+ }
+ return ret;
+ }
+ }
- function compileSegs(events) {
- var d1 = cloneDate(view.visStart),
- d2 = addDays(cloneDate(d1), colCnt),
- visEventsEnds = $.map(events, exclEndDay),
- i, row,
- j, level,
- k, seg,
- segs = [];
- for (i = 0; i < rowCnt; i++) {
- row = stackSegs(view.sliceSegs(events, visEventsEnds, d1, d2));
- for (j = 0; j < row.length; j++) {
- level = row[j];
- for (k = 0; k < level.length; k++) {
- seg = level[k];
- seg.row = i;
- seg.level = j;
- segs.push(seg);
- }
- }
- addDays(d1, 7);
- addDays(d2, 7);
+ function firstDefined() {
+ for (var i = 0; i < arguments.length; i++) {
+ if (arguments[i] !== undefined) {
+ return arguments[i];
}
- return segs;
}
+ }
- function renderSegs(segs, modifiedEventId) {
- _renderDaySegs(
- segs,
- rowCnt,
- view,
- 0,
- viewWidth,
- function(i) {
- return tbody.find('tr:eq(' + i + ')')
- },
- dayContentPositions.left,
- dayContentPositions.right,
- segmentContainer,
- bindSegHandlers,
- modifiedEventId
- );
+ fcViews.month = MonthView;
+
+ function MonthView(element, calendar) {
+ var t = this;
+
+
+ // exports
+ t.render = render;
+
+
+ // imports
+ BasicView.call(t, element, calendar, 'month');
+ var opt = t.opt;
+ var renderBasic = t.renderBasic;
+ var formatDate = calendar.formatDate;
+
+
+ function render(date, delta) {
+ if (delta) {
+ addMonths(date, delta);
+ date.setDate(1);
+ }
+ var start = cloneDate(date, true);
+ start.setDate(1);
+ var end = addMonths(cloneDate(start), 1);
+ var visStart = cloneDate(start);
+ var visEnd = cloneDate(end);
+ var firstDay = opt('firstDay');
+ var nwe = opt('weekends') ? 0 : 1;
+ if (nwe) {
+ skipWeekend(visStart);
+ skipWeekend(visEnd, -1, true);
+ }
+ addDays(visStart, -((visStart.getDay() - Math.max(firstDay, nwe) + 7) % 7));
+ addDays(visEnd, (7 - visEnd.getDay() + Math.max(firstDay, nwe)) % 7);
+ var rowCnt = Math.round((visEnd - visStart) / (DAY_MS * 7));
+ if (opt('weekMode') == 'fixed') {
+ addDays(visEnd, (6 - rowCnt) * 7);
+ rowCnt = 6;
+ }
+ t.title = formatDate(start, opt('titleFormat'));
+ t.start = start;
+ t.end = end;
+ t.visStart = visStart;
+ t.visEnd = visEnd;
+ renderBasic(6, rowCnt, nwe ? 5 : 7, true);
}
- function bindSegHandlers(event, eventElement, seg) {
- view.eventElementHandlers(event, eventElement);
- if (event.editable || event.editable === undefined && options.editable) {
- draggableEvent(event, eventElement);
- if (seg.isEnd) {
- view.resizableDayEvent(event, eventElement, colWidth);
- }
+ }
+
+ fcViews.basicWeek = BasicWeekView;
+
+ function BasicWeekView(element, calendar) {
+ var t = this;
+
+
+ // exports
+ t.render = render;
+
+
+ // imports
+ BasicView.call(t, element, calendar, 'basicWeek');
+ var opt = t.opt;
+ var renderBasic = t.renderBasic;
+ var formatDates = calendar.formatDates;
+
+
+ function render(date, delta) {
+ if (delta) {
+ addDays(date, delta * 7);
}
+ var start = addDays(cloneDate(date), -((date.getDay() - opt('firstDay') + 7) % 7));
+ var end = addDays(cloneDate(start), 7);
+ var visStart = cloneDate(start);
+ var visEnd = cloneDate(end);
+ var weekends = opt('weekends');
+ if (!weekends) {
+ skipWeekend(visStart);
+ skipWeekend(visEnd, -1, true);
+ }
+ t.title = formatDates(
+ visStart,
+ addDays(cloneDate(visEnd), -1),
+ opt('titleFormat')
+ );
+ t.start = start;
+ t.end = end;
+ t.visStart = visStart;
+ t.visEnd = visEnd;
+ renderBasic(1, 1, weekends ? 7 : 5, false);
}
- /* Event Dragging
- -----------------------------------------------------------------------------*/
+ }
+ fcViews.basicDay = BasicDayView;
- function draggableEvent(event, eventElement) {
- if (!options.disableDragging && eventElement.draggable) {
- var matrix,
- dayDelta = 0;
- eventElement.draggable({
- zIndex: 9,
- delay: 50,
- opacity: view.option('dragOpacity'),
- revertDuration: options.dragRevertDuration,
- start: function(ev, ui) {
- view.hideEvents(event, eventElement);
- view.trigger('eventDragStart', eventElement, event, ev, ui);
- matrix = buildDayMatrix(function(cell) {
- eventElement.draggable('option', 'revert', !cell || !cell.rowDelta && !cell.colDelta);
- clearOverlays();
- if (cell) {
- dayDelta = cell.rowDelta * 7 + cell.colDelta * dis;
- renderDayOverlays(
- matrix,
- addDays(cloneDate(event.start), dayDelta),
- addDays(exclEndDay(event), dayDelta)
- );
- } else {
- dayDelta = 0;
- }
- });
- matrix.mouse(ev);
- },
- drag: function(ev) {
- matrix.mouse(ev);
- },
- stop: function(ev, ui) {
- clearOverlays();
- view.trigger('eventDragStop', eventElement, event, ev, ui);
- if (dayDelta) {
- eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link
- view.eventDrop(this, event, dayDelta, 0, event.allDay, ev, ui);
- } else {
- if ($.browser.msie) {
- eventElement.css('filter', ''); // clear IE opacity side-effects
- }
- view.showEvents(event, eventElement);
- }
- }
- });
+//TODO: when calendar's date starts out on a weekend, shouldn't happen
+
+
+ function BasicDayView(element, calendar) {
+ var t = this;
+
+
+ // exports
+ t.render = render;
+
+
+ // imports
+ BasicView.call(t, element, calendar, 'basicDay');
+ var opt = t.opt;
+ var renderBasic = t.renderBasic;
+ var formatDate = calendar.formatDate;
+
+
+ function render(date, delta) {
+ if (delta) {
+ addDays(date, delta);
+ if (!opt('weekends')) {
+ skipWeekend(date, delta < 0 ? -1 : 1);
+ }
}
+ t.title = formatDate(date, opt('titleFormat'));
+ t.start = t.visStart = cloneDate(date, true);
+ t.end = t.visEnd = addDays(cloneDate(t.start), 1);
+ renderBasic(1, 1, 1, false);
}
- /* Day clicking and binding
- ---------------------------------------------------------*/
+ }
- function dayBind(days) {
- days.click(dayClick)
- .mousedown(selectionMousedown);
+ setDefaults({
+ weekMode: 'fixed'
+ });
+
+
+ function BasicView(element, calendar, viewName) {
+ var t = this;
+
+
+ // exports
+ t.renderBasic = renderBasic;
+ t.setHeight = setHeight;
+ t.setWidth = setWidth;
+ t.renderDayOverlay = renderDayOverlay;
+ t.defaultSelectionEnd = defaultSelectionEnd;
+ t.renderSelection = renderSelection;
+ t.clearSelection = clearSelection;
+ t.reportDayClick = reportDayClick; // for selection (kinda hacky)
+ t.dragStart = dragStart;
+ t.dragStop = dragStop;
+ t.defaultEventEnd = defaultEventEnd;
+ t.getHoverListener = function() {
+ return hoverListener
+ };
+ t.colContentLeft = colContentLeft;
+ t.colContentRight = colContentRight;
+ t.dayOfWeekCol = dayOfWeekCol;
+ t.dateCell = dateCell;
+ t.cellDate = cellDate;
+ t.cellIsAllDay = function() {
+ return true
+ };
+ t.allDayRow = allDayRow;
+ t.allDayBounds = allDayBounds;
+ t.getRowCnt = function() {
+ return rowCnt
+ };
+ t.getColCnt = function() {
+ return colCnt
+ };
+ t.getColWidth = function() {
+ return colWidth
+ };
+ t.getDaySegmentContainer = function() {
+ return daySegmentContainer
+ };
+
+
+ // imports
+ View.call(t, element, calendar, viewName);
+ OverlayManager.call(t);
+ SelectionManager.call(t);
+ BasicEventRenderer.call(t);
+ var opt = t.opt;
+ var trigger = t.trigger;
+ var clearEvents = t.clearEvents;
+ var renderOverlay = t.renderOverlay;
+ var clearOverlays = t.clearOverlays;
+ var daySelectionMousedown = t.daySelectionMousedown;
+ var formatDate = calendar.formatDate;
+
+
+ // locals
+
+ var head;
+ var headCells;
+ var body;
+ var bodyRows;
+ var bodyCells;
+ var bodyFirstCells;
+ var bodyCellTopInners;
+ var daySegmentContainer;
+
+ var viewWidth;
+ var viewHeight;
+ var colWidth;
+
+ var rowCnt, colCnt;
+ var coordinateGrid;
+ var hoverListener;
+ var colContentPositions;
+
+ var rtl, dis, dit;
+ var firstDay;
+ var nwe;
+ var tm;
+ var colFormat;
+
+
+ /* Rendering
+ ------------------------------------------------------------*/
+
+
+ disableTextSelection(element.addClass('fc-grid'));
+
+
+ function renderBasic(maxr, r, c, showNumbers) {
+ rowCnt = r;
+ colCnt = c;
+ updateOptions();
+ var firstTime = !body;
+ if (firstTime) {
+ buildSkeleton(maxr, showNumbers);
+ } else {
+ clearEvents();
+ }
+ updateCells(firstTime);
}
- function dayClick(ev) {
- if (!view.option('selectable')) { // SelectionManager will worry about dayClick
- var n = parseInt(this.className.match(/fc\-day(\d+)/)[1]),
- date = addDays(
- cloneDate(view.visStart),
- Math.floor(n / colCnt) * 7 + n % colCnt
- );
- // TODO: what about weekends in middle of week?
- view.trigger('dayClick', this, date, true, ev);
+
+ function updateOptions() {
+ rtl = opt('isRTL');
+ if (rtl) {
+ dis = -1;
+ dit = colCnt - 1;
+ } else {
+ dis = 1;
+ dit = 0;
}
+ firstDay = opt('firstDay');
+ nwe = opt('weekends') ? 0 : 1;
+ tm = opt('theme') ? 'ui' : 'fc';
+ colFormat = opt('columnFormat');
}
- /* Selecting
- --------------------------------------------------------*/
+ function buildSkeleton(maxRowCnt, showNumbers) {
+ var s;
+ var headerClass = tm + "-widget-header";
+ var contentClass = tm + "-widget-content";
+ var i, j;
+ var table;
- selectionManager = new SelectionManager(
- view,
- unselect,
- function(startDate, endDate, allDay) {
- renderDayOverlays(
- selectionMatrix,
- startDate,
- addDays(cloneDate(endDate), 1)
- );
- },
- clearOverlays
- );
+ s =
+ "<table class='fc-border-separate' style='width:100%' cellspacing='0'>" +
+ "<thead>" +
+ "<tr>";
+ for (i = 0; i < colCnt; i++) {
+ s +=
+ "<th class='fc- " + headerClass + "'/>"; // need fc- for setDayID
+ }
+ s +=
+ "</tr>" +
+ "</thead>" +
+ "<tbody>";
+ for (i = 0; i < maxRowCnt; i++) {
+ s +=
+ "<tr class='fc-week" + i + "'>";
+ for (j = 0; j < colCnt; j++) {
+ s +=
+ "<td class='fc- " + contentClass + " fc-day" + (i * colCnt + j) + "'>" + // need fc- for setDayID
+ "<div>" +
+ (showNumbers ?
+ "<div class='fc-day-number'/>" :
+ ''
+ ) +
+ "<div class='fc-day-content'>" +
+ "<div style='position:relative'> </div>" +
+ "</div>" +
+ "</div>" +
+ "</td>";
+ }
+ s +=
+ "</tr>";
+ }
+ s +=
+ "</tbody>" +
+ "</table>";
+ table = $(s).appendTo(element);
- function selectionMousedown(ev) {
- if (view.option('selectable')) {
- selectionMatrix = buildDayMatrix(function(cell) {
- if (cell) {
- var d = cellDate(cell.row, cell.col);
- selectionManager.drag(d, d, true);
+ head = table.find('thead');
+ headCells = head.find('th');
+ body = table.find('tbody');
+ bodyRows = body.find('tr');
+ bodyCells = body.find('td');
+ bodyFirstCells = bodyCells.filter(':first-child');
+ bodyCellTopInners = bodyRows.eq(0).find('div.fc-day-content div');
+
+ markFirstLast(head.add(head.find('tr'))); // marks first+last tr/th's
+ markFirstLast(bodyRows); // marks first+last td's
+ bodyRows.eq(0).addClass('fc-first'); // fc-last is done in updateCells
+
+ dayBind(bodyCells);
+
+ daySegmentContainer =
+ $("<div style='position:absolute;z-index:8;top:0;left:0'/>")
+ .appendTo(element);
+ }
+
+
+ function updateCells(firstTime) {
+ var dowDirty = firstTime || rowCnt == 1; // could the cells' day-of-weeks need updating?
+ var month = t.start.getMonth();
+ var today = clearTime(new Date());
+ var cell;
+ var date;
+ var row;
+
+ if (dowDirty) {
+ headCells.each(function(i, _cell) {
+ cell = $(_cell);
+ date = indexDate(i);
+ cell.html(formatDate(date, colFormat));
+ setDayID(cell, date);
+ });
+ }
+
+ bodyCells.each(function(i, _cell) {
+ cell = $(_cell);
+ date = indexDate(i);
+ if (date.getMonth() == month) {
+ cell.removeClass('fc-other-month');
+ } else {
+ cell.addClass('fc-other-month');
+ }
+ if (+date == +today) {
+ cell.addClass(tm + '-state-highlight fc-today');
+ } else {
+ cell.removeClass(tm + '-state-highlight fc-today');
+ }
+ cell.find('div.fc-day-number').text(date.getDate());
+ if (dowDirty) {
+ setDayID(cell, date);
+ }
+ });
+
+ bodyRows.each(function(i, _row) {
+ row = $(_row);
+ if (i < rowCnt) {
+ row.show();
+ if (i == rowCnt - 1) {
+ row.addClass('fc-last');
} else {
- selectionManager.drag();
+ row.removeClass('fc-last');
}
- });
- documentDragHelp(
- function(ev) {
- selectionMatrix.mouse(ev);
- },
- function(ev) {
- selectionManager.dragStop(ev);
- }
- );
- selectionManager.dragStart(ev);
- selectionMatrix.mouse(ev);
- return false; // prevent auto-unselect and text selection
- }
+ } else {
+ row.hide();
+ }
+ });
}
- documentUnselectAuto(view, unselect);
- view.select = function(start, end, allDay) {
- if (!end) {
- end = cloneDate(start);
+ function setHeight(height) {
+ viewHeight = height;
+
+ var bodyHeight = viewHeight - head.height();
+ var rowHeight;
+ var rowHeightLast;
+ var cell;
+
+ if (opt('weekMode') == 'variable') {
+ rowHeight = rowHeightLast = Math.floor(bodyHeight / (rowCnt == 1 ? 2 : 6));
+ } else {
+ rowHeight = Math.floor(bodyHeight / rowCnt);
+ rowHeightLast = bodyHeight - rowHeight * (rowCnt - 1);
}
- selectionMatrix = buildDayMatrix();
- selectionManager.select(start, end, allDay);
- };
- function unselect() {
- selectionManager.unselect();
+ bodyFirstCells.each(function(i, _cell) {
+ if (i < rowCnt) {
+ cell = $(_cell);
+ setMinHeight(
+ cell.find('> div'),
+ (i == rowCnt - 1 ? rowHeightLast : rowHeight) - vsides(cell)
+ );
+ }
+ });
+
}
- view.unselect = unselect;
+ function setWidth(width) {
+ viewWidth = width;
+ colContentPositions.clear();
+ colWidth = Math.floor(viewWidth / colCnt);
+ setOuterWidth(headCells.slice(0, -1), colWidth);
+ }
+
+ /* Day clicking and binding
+ -----------------------------------------------------------*/
+
+
+ function dayBind(days) {
+ days.click(dayClick)
+ .mousedown(daySelectionMousedown);
+ }
+
+
+ function dayClick(ev) {
+ if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
+ var index = parseInt(this.className.match(/fc\-day(\d+)/)[1]); // TODO: maybe use .data
+ var date = indexDate(index);
+ trigger('dayClick', this, date, true, ev);
+ }
+ }
+
+
/* Semi-transparent Overlay Helpers
------------------------------------------------------*/
- function renderDayOverlays(matrix, overlayStart, overlayEnd) { // overlayEnd is exclusive
- var rowStart = cloneDate(view.visStart);
+
+ function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive
+ if (refreshCoordinateGrid) {
+ coordinateGrid.build();
+ }
+ var rowStart = cloneDate(t.visStart);
var rowEnd = addDays(cloneDate(rowStart), colCnt);
for (var i = 0; i < rowCnt; i++) {
var stretchStart = new Date(Math.max(rowStart, overlayStart));
@@ -1481,202 +2434,389 @@
colStart = dayDiff(stretchStart, rowStart);
colEnd = dayDiff(stretchEnd, rowStart);
}
- var rect = matrix.rect(i, colStart, i + 1, colEnd, element);
dayBind(
- view.renderOverlay(rect, element)
- );
+ renderCellOverlay(i, colStart, i, colEnd - 1)
+ );
}
addDays(rowStart, 7);
addDays(rowEnd, 7);
}
}
- function clearOverlays() {
- view.clearOverlays();
+
+ function renderCellOverlay(row0, col0, row1, col1) { // row1,col1 is inclusive
+ var rect = coordinateGrid.rect(row0, col0, row1, col1, element);
+ return renderOverlay(rect, element);
}
- /* Utils
- ---------------------------------------------------*/
+ /* Selection
+ -----------------------------------------------------------------------*/
- function buildDayMatrix(changeCallback) {
- var tds = tbody.find('tr:first td');
- if (rtl) {
- tds = $(tds.get().reverse());
- }
- return new HoverMatrix(tbody.find('tr'), tds, changeCallback);
+ function defaultSelectionEnd(startDate, allDay) {
+ return cloneDate(startDate);
}
- function cellDate(r, c) { // convert r,c to date
- return addDays(cloneDate(view.visStart), r * 7 + c * dis + dit);
- // TODO: what about weekends in middle of week?
+ function renderSelection(startDate, endDate, allDay) {
+ renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true); // rebuild every time???
}
- }
+ function clearSelection() {
+ clearOverlays();
+ }
- function _renderDaySegs(segs, rowCnt, view, minLeft, maxLeft, getRow, dayContentLeft, dayContentRight, segmentContainer, bindSegHandlers, modifiedEventId) {
+ function reportDayClick(date, allDay, ev) {
+ var cell = dateCell(date);
+ var _element = bodyCells[cell.row * colCnt + cell.col];
+ trigger('dayClick', _element, date, allDay, ev);
+ }
- var options = view.options,
- rtl = options.isRTL,
- i, segCnt = segs.length, seg,
- event,
- className,
- left, right,
- html = '',
- eventElements,
- eventElement,
- triggerRes,
- hsideCache = {},
- vmarginCache = {},
- key, val,
- rowI, top, levelI, levelHeight,
- rowDivs = [],
- rowDivTops = [];
- // calculate desired position/dimensions, create html
- for (i = 0; i < segCnt; i++) {
- seg = segs[i];
- event = seg.event;
- className = 'fc-event fc-event-hori ';
- if (rtl) {
- if (seg.isStart) {
- className += 'fc-corner-right ';
+ /* External Dragging
+ -----------------------------------------------------------------------*/
+
+
+ function dragStart(_dragElement, ev, ui) {
+ hoverListener.start(function(cell) {
+ clearOverlays();
+ if (cell) {
+ renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
}
- if (seg.isEnd) {
- className += 'fc-corner-left ';
- }
- left = seg.isEnd ? dayContentLeft(seg.end.getDay() - 1) : minLeft;
- right = seg.isStart ? dayContentRight(seg.start.getDay()) : maxLeft;
- } else {
- if (seg.isStart) {
- className += 'fc-corner-left ';
- }
- if (seg.isEnd) {
- className += 'fc-corner-right ';
- }
- left = seg.isStart ? dayContentLeft(seg.start.getDay()) : minLeft;
- right = seg.isEnd ? dayContentRight(seg.end.getDay() - 1) : maxLeft;
+ }, ev);
+ }
+
+
+ function dragStop(_dragElement, ev, ui) {
+ var cell = hoverListener.stop();
+ clearOverlays();
+ if (cell) {
+ var d = cellDate(cell);
+ trigger('drop', _dragElement, d, true, ev, ui);
}
- html +=
- "<div class='" + className + event.className.join(' ') + "' style='position:absolute;z-index:8;left:" + left + "px'>" +
- "<a" + (event.url ? " href='" + htmlEscape(event.url) + "'" : '') + ">" +
- (!event.allDay && seg.isStart ?
- "<span class='fc-event-time'>" +
- htmlEscape(formatDates(event.start, event.end, view.option('timeFormat'), options)) +
- "</span>"
- : '') +
- "<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" +
- "</a>" +
- ((event.editable || event.editable === undefined && options.editable) && !options.disableResizing && $.fn.resizable ?
- "<div class='ui-resizable-handle ui-resizable-" + (rtl ? 'w' : 'e') + "'></div>"
- : '') +
- "</div>";
- seg.left = left;
- seg.outerWidth = right - left;
}
- segmentContainer[0].innerHTML = html; // faster than html()
- eventElements = segmentContainer.children();
- // retrieve elements, run through eventRender callback, bind handlers
- for (i = 0; i < segCnt; i++) {
- seg = segs[i];
- eventElement = $(eventElements[i]); // faster than eq()
- event = seg.event;
- triggerRes = view.trigger('eventRender', event, event, eventElement);
- if (triggerRes === false) {
- eventElement.remove();
- } else {
- if (triggerRes && triggerRes !== true) {
- eventElement.remove();
- eventElement = $(triggerRes)
- .css({
- position: 'absolute',
- left: seg.left
- })
- .appendTo(segmentContainer);
+
+ /* Utilities
+ --------------------------------------------------------*/
+
+
+ function defaultEventEnd(event) {
+ return cloneDate(event.start);
+ }
+
+
+ coordinateGrid = new CoordinateGrid(function(rows, cols) {
+ var e, n, p;
+ headCells.each(function(i, _e) {
+ e = $(_e);
+ n = e.offset().left;
+ if (i) {
+ p[1] = n;
}
- seg.element = eventElement;
- if (event._id === modifiedEventId) {
- bindSegHandlers(event, eventElement, seg);
- } else {
- eventElement[0]._fci = i; // for lazySegBind
+ p = [n];
+ cols[i] = p;
+ });
+ p[1] = n + e.outerWidth();
+ bodyRows.each(function(i, _e) {
+ if (i < rowCnt) {
+ e = $(_e);
+ n = e.offset().top;
+ if (i) {
+ p[1] = n;
+ }
+ p = [n];
+ rows[i] = p;
}
- view.reportEventElement(event, eventElement);
- }
+ });
+ p[1] = n + e.outerHeight();
+ });
+
+
+ hoverListener = new HoverListener(coordinateGrid);
+
+
+ colContentPositions = new HorizontalPositionCache(function(col) {
+ return bodyCellTopInners.eq(col);
+ });
+
+
+ function colContentLeft(col) {
+ return colContentPositions.left(col);
}
- lazySegBind(segmentContainer, segs, bindSegHandlers);
- // record event horizontal sides
- for (i = 0; i < segCnt; i++) {
- seg = segs[i];
- if (eventElement = seg.element) {
- val = hsideCache[key = seg.key = cssKey(eventElement[0])];
- seg.hsides = val === undefined ? (hsideCache[key] = hsides(eventElement[0], true)) : val;
- }
+ function colContentRight(col) {
+ return colContentPositions.right(col);
}
- // set event widths
- for (i = 0; i < segCnt; i++) {
- seg = segs[i];
- if (eventElement = seg.element) {
- eventElement[0].style.width = seg.outerWidth - seg.hsides + 'px';
- }
+
+ function dateCell(date) {
+ return {
+ row: Math.floor(dayDiff(date, t.visStart) / 7),
+ col: dayOfWeekCol(date.getDay())
+ };
}
- // record event heights
- for (i = 0; i < segCnt; i++) {
- seg = segs[i];
- if (eventElement = seg.element) {
- val = vmarginCache[key = seg.key];
- seg.outerHeight = eventElement[0].offsetHeight + (
- val === undefined ? (vmarginCache[key] = vmargins(eventElement[0])) : val
- );
- }
+
+ function cellDate(cell) {
+ return _cellDate(cell.row, cell.col);
}
- // set row heights, calculate event tops (in relation to row top)
- for (i = 0,rowI = 0; rowI < rowCnt; rowI++) {
- top = levelI = levelHeight = 0;
- while (i < segCnt && (seg = segs[i]).row == rowI) {
- if (seg.level != levelI) {
- top += levelHeight;
- levelHeight = 0;
- levelI++;
+
+ function _cellDate(row, col) {
+ return addDays(cloneDate(t.visStart), row * 7 + col * dis + dit);
+ // what about weekends in middle of week?
+ }
+
+
+ function indexDate(index) {
+ return _cellDate(Math.floor(index / colCnt), index % colCnt);
+ }
+
+
+ function dayOfWeekCol(dayOfWeek) {
+ return ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt) * dis + dit;
+ }
+
+
+ function allDayRow(i) {
+ return bodyRows.eq(i);
+ }
+
+
+ function allDayBounds(i) {
+ return {
+ left: 0,
+ right: viewWidth
+ };
+ }
+
+
+ }
+
+ function BasicEventRenderer() {
+ var t = this;
+
+
+ // exports
+ t.renderEvents = renderEvents;
+ t.compileDaySegs = compileSegs; // for DayEventRenderer
+ t.clearEvents = clearEvents;
+ t.bindDaySeg = bindDaySeg;
+
+
+ // imports
+ DayEventRenderer.call(t);
+ var opt = t.opt;
+ var trigger = t.trigger;
+ //var setOverflowHidden = t.setOverflowHidden;
+ var isEventDraggable = t.isEventDraggable;
+ var isEventResizable = t.isEventResizable;
+ var reportEvents = t.reportEvents;
+ var reportEventClear = t.reportEventClear;
+ var eventElementHandlers = t.eventElementHandlers;
+ var showEvents = t.showEvents;
+ var hideEvents = t.hideEvents;
+ var eventDrop = t.eventDrop;
+ var getDaySegmentContainer = t.getDaySegmentContainer;
+ var getHoverListener = t.getHoverListener;
+ var renderDayOverlay = t.renderDayOverlay;
+ var clearOverlays = t.clearOverlays;
+ var getRowCnt = t.getRowCnt;
+ var getColCnt = t.getColCnt;
+ var renderDaySegs = t.renderDaySegs;
+ var resizableDayEvent = t.resizableDayEvent;
+
+
+ /* Rendering
+ --------------------------------------------------------------------*/
+
+
+ function renderEvents(events, modifiedEventId) {
+ reportEvents(events);
+ renderDaySegs(compileSegs(events), modifiedEventId);
+ }
+
+
+ function clearEvents() {
+ reportEventClear();
+ getDaySegmentContainer().empty();
+ }
+
+
+ function compileSegs(events) {
+ var rowCnt = getRowCnt(),
+ colCnt = getColCnt(),
+ d1 = cloneDate(t.visStart),
+ d2 = addDays(cloneDate(d1), colCnt),
+ visEventsEnds = $.map(events, exclEndDay),
+ i, row,
+ j, level,
+ k, seg,
+ segs = [];
+ for (i = 0; i < rowCnt; i++) {
+ row = stackSegs(sliceSegs(events, visEventsEnds, d1, d2));
+ for (j = 0; j < row.length; j++) {
+ level = row[j];
+ for (k = 0; k < level.length; k++) {
+ seg = level[k];
+ seg.row = i;
+ seg.level = j; // not needed anymore
+ segs.push(seg);
+ }
}
- levelHeight = Math.max(levelHeight, seg.outerHeight || 0);
- seg.top = top;
- i++;
+ addDays(d1, 7);
+ addDays(d2, 7);
}
- rowDivs[rowI] = getRow(rowI).find('td:first div.fc-day-content > div')// optimal selector?
- .height(top + levelHeight);
+ return segs;
}
- // calculate row tops
- for (rowI = 0; rowI < rowCnt; rowI++) {
- rowDivTops[rowI] = rowDivs[rowI][0].offsetTop;
+
+ function bindDaySeg(event, eventElement, seg) {
+ if (isEventDraggable(event)) {
+ draggableDayEvent(event, eventElement);
+ }
+ if (seg.isEnd && isEventResizable(event)) {
+ resizableDayEvent(event, eventElement, seg);
+ }
+ eventElementHandlers(event, eventElement);
+ // needs to be after, because resizableDayEvent might stopImmediatePropagation on click
}
- // set event tops
- for (i = 0; i < segCnt; i++) {
- seg = segs[i];
- if (eventElement = seg.element) {
- eventElement[0].style.top = rowDivTops[seg.row] + seg.top + 'px';
- event = seg.event;
- view.trigger('eventAfterRender', event, event, eventElement);
+
+ /* Dragging
+ ----------------------------------------------------------------------------*/
+
+
+ function draggableDayEvent(event, eventElement) {
+ var hoverListener = getHoverListener();
+ var dayDelta;
+ eventElement.draggable({
+ zIndex: 9,
+ delay: 50,
+ opacity: opt('dragOpacity'),
+ revertDuration: opt('dragRevertDuration'),
+ start: function(ev, ui) {
+ trigger('eventDragStart', eventElement, event, ev, ui);
+ hideEvents(event, eventElement);
+ hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
+ eventElement.draggable('option', 'revert', !cell || !rowDelta && !colDelta);
+ clearOverlays();
+ if (cell) {
+ //setOverflowHidden(true);
+ dayDelta = rowDelta * 7 + colDelta * (opt('isRTL') ? -1 : 1);
+ renderDayOverlay(
+ addDays(cloneDate(event.start), dayDelta),
+ addDays(exclEndDay(event), dayDelta)
+ );
+ } else {
+ //setOverflowHidden(false);
+ dayDelta = 0;
+ }
+ }, ev, 'drag');
+ },
+ stop: function(ev, ui) {
+ hoverListener.stop();
+ clearOverlays();
+ trigger('eventDragStop', eventElement, event, ev, ui);
+ if (dayDelta) {
+ eventDrop(this, event, dayDelta, 0, event.allDay, ev, ui);
+ } else {
+ eventElement.css('filter', ''); // clear IE opacity side-effects
+ showEvents(event, eventElement);
+ }
+ //setOverflowHidden(false);
+ }
+ });
+ }
+
+
+ }
+
+ fcViews.agendaWeek = AgendaWeekView;
+
+ function AgendaWeekView(element, calendar) {
+ var t = this;
+
+
+ // exports
+ t.render = render;
+
+
+ // imports
+ AgendaView.call(t, element, calendar, 'agendaWeek');
+ var opt = t.opt;
+ var renderAgenda = t.renderAgenda;
+ var formatDates = calendar.formatDates;
+
+
+ function render(date, delta) {
+ if (delta) {
+ addDays(date, delta * 7);
}
+ var start = addDays(cloneDate(date), -((date.getDay() - opt('firstDay') + 7) % 7));
+ var end = addDays(cloneDate(start), 7);
+ var visStart = cloneDate(start);
+ var visEnd = cloneDate(end);
+ var weekends = opt('weekends');
+ if (!weekends) {
+ skipWeekend(visStart);
+ skipWeekend(visEnd, -1, true);
+ }
+ t.title = formatDates(
+ visStart,
+ addDays(cloneDate(visEnd), -1),
+ opt('titleFormat')
+ );
+ t.start = start;
+ t.end = end;
+ t.visStart = visStart;
+ t.visEnd = visEnd;
+ renderAgenda(weekends ? 7 : 5);
}
+
}
+ fcViews.agendaDay = AgendaDayView;
- /* Agenda Views: agendaWeek/agendaDay
- -----------------------------------------------------------------------------*/
+ function AgendaDayView(element, calendar) {
+ var t = this;
+
+ // exports
+ t.render = render;
+
+
+ // imports
+ AgendaView.call(t, element, calendar, 'agendaDay');
+ var opt = t.opt;
+ var renderAgenda = t.renderAgenda;
+ var formatDate = calendar.formatDate;
+
+
+ function render(date, delta) {
+ if (delta) {
+ addDays(date, delta);
+ if (!opt('weekends')) {
+ skipWeekend(date, delta < 0 ? -1 : 1);
+ }
+ }
+ var start = cloneDate(date, true);
+ var end = addDays(cloneDate(start), 1);
+ t.title = formatDate(date, opt('titleFormat'));
+ t.start = t.visStart = start;
+ t.end = t.visEnd = end;
+ renderAgenda(1);
+ }
+
+
+ }
+
setDefaults({
allDaySlot: true,
allDayText: 'all-day',
@@ -1694,284 +2834,354 @@
maxTime: 24
});
- views.agendaWeek = function(element, options) {
- return new Agenda(element, options, {
- render: function(date, delta) {
- if (delta) {
- addDays(date, delta * 7);
- }
- var visStart = this.visStart = cloneDate(
- this.start = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7))
- ),
- visEnd = this.visEnd = cloneDate(
- this.end = addDays(cloneDate(visStart), 7)
- );
- if (!options.weekends) {
- skipWeekend(visStart);
- skipWeekend(visEnd, -1, true);
- }
- this.title = formatDates(
- visStart,
- addDays(cloneDate(visEnd), -1),
- this.option('titleFormat'),
- options
- );
- this.renderAgenda(
- options.weekends ? 7 : 5,
- this.option('columnFormat')
- );
- }
- });
- };
- views.agendaDay = function(element, options) {
- return new Agenda(element, options, {
- render: function(date, delta) {
- if (delta) {
- addDays(date, delta);
- if (!options.weekends) {
- skipWeekend(date, delta < 0 ? -1 : 1);
- }
- }
- this.title = formatDate(date, this.option('titleFormat'), options);
- this.start = this.visStart = cloneDate(date, true);
- this.end = this.visEnd = addDays(cloneDate(this.start), 1);
- this.renderAgenda(
- 1,
- this.option('columnFormat')
- );
- }
- });
- };
+// TODO: make it work in quirks mode (event corners, all-day height)
+// TODO: test liquid width, especially in IE6
- function Agenda(element, options, methods) {
- var head, body, bodyContent, bodyTable, bg,
- colCnt,
- slotCnt = 0, // spanning all the way across
- axisWidth, colWidth, slotHeight,
- viewWidth, viewHeight,
- savedScrollTop,
- cachedEvents = [],
- daySegmentContainer,
- slotSegmentContainer,
- tm, firstDay,
- nwe, // no weekends (int)
- rtl, dis, dit, // day index sign / translate
- minMinute, maxMinute,
- colContentPositions = new HorizontalPositionCache(function(col) {
- return bg.find('td:eq(' + col + ') div div');
- }),
- slotTopCache = {},
- daySelectionManager,
- slotSelectionManager,
- selectionHelper,
- selectionMatrix,
- // ...
+ function AgendaView(element, calendar, viewName) {
+ var t = this;
- view = $.extend(this, viewMethods, methods, {
- renderAgenda: renderAgenda,
- renderEvents: renderEvents,
- rerenderEvents: rerenderEvents,
- clearEvents: clearEvents,
- setHeight: setHeight,
- setWidth: setWidth,
- beforeHide: function() {
- savedScrollTop = body.scrollTop();
- },
- afterShow: function() {
- body.scrollTop(savedScrollTop);
- },
- defaultEventEnd: function(event) {
- var start = cloneDate(event.start);
- if (event.allDay) {
- return start;
- }
- return addMinutes(start, options.defaultEventMinutes);
- }
- });
- view.init(element, options);
+ // exports
+ t.renderAgenda = renderAgenda;
+ t.setWidth = setWidth;
+ t.setHeight = setHeight;
+ t.beforeHide = beforeHide;
+ t.afterShow = afterShow;
+ t.defaultEventEnd = defaultEventEnd;
+ t.timePosition = timePosition;
+ t.dayOfWeekCol = dayOfWeekCol;
+ t.dateCell = dateCell;
+ t.cellDate = cellDate;
+ t.cellIsAllDay = cellIsAllDay;
+ t.allDayRow = getAllDayRow;
+ t.allDayBounds = allDayBounds;
+ t.getHoverListener = function() {
+ return hoverListener
+ };
+ t.colContentLeft = colContentLeft;
+ t.colContentRight = colContentRight;
+ t.getDaySegmentContainer = function() {
+ return daySegmentContainer
+ };
+ t.getSlotSegmentContainer = function() {
+ return slotSegmentContainer
+ };
+ t.getMinMinute = function() {
+ return minMinute
+ };
+ t.getMaxMinute = function() {
+ return maxMinute
+ };
+ t.getBodyContent = function() {
+ return slotContent
+ }; // !!??
+ t.getRowCnt = function() {
+ return 1
+ };
+ t.getColCnt = function() {
+ return colCnt
+ };
+ t.getColWidth = function() {
+ return colWidth
+ };
+ t.getSlotHeight = function() {
+ return slotHeight
+ };
+ t.defaultSelectionEnd = defaultSelectionEnd;
+ t.renderDayOverlay = renderDayOverlay;
+ t.renderSelection = renderSelection;
+ t.clearSelection = clearSelection;
+ t.reportDayClick = reportDayClick; // selection mousedown hack
+ t.dragStart = dragStart;
+ t.dragStop = dragStop;
- /* Time-slot rendering
+
+ // imports
+ View.call(t, element, calendar, viewName);
+ OverlayManager.call(t);
+ SelectionManager.call(t);
+ AgendaEventRenderer.call(t);
+ var opt = t.opt;
+ var trigger = t.trigger;
+ var clearEvents = t.clearEvents;
+ var renderOverlay = t.renderOverlay;
+ var clearOverlays = t.clearOverlays;
+ var reportSelection = t.reportSelection;
+ var unselect = t.unselect;
+ var daySelectionMousedown = t.daySelectionMousedown;
+ var slotSegHtml = t.slotSegHtml;
+ var formatDate = calendar.formatDate;
+
+
+ // locals
+
+ var dayTable;
+ var dayHead;
+ var dayHeadCells;
+ var dayBody;
+ var dayBodyCells;
+ var dayBodyCellInners;
+ var dayBodyFirstCell;
+ var dayBodyFirstCellStretcher;
+ var slotLayer;
+ var daySegmentContainer;
+ var allDayTable;
+ var allDayRow;
+ var slotScroller;
+ var slotContent;
+ var slotSegmentContainer;
+ var slotTable;
+ var slotTableFirstInner;
+ var axisFirstCells;
+ var gutterCells;
+ var selectionHelper;
+
+ var viewWidth;
+ var viewHeight;
+ var axisWidth;
+ var colWidth;
+ var gutterWidth;
+ var slotHeight; // TODO: what if slotHeight changes? (see issue 650)
+ var savedScrollTop;
+
+ var colCnt;
+ var slotCnt;
+ var coordinateGrid;
+ var hoverListener;
+ var colContentPositions;
+ var slotTopCache = {};
+
+ var tm;
+ var firstDay;
+ var nwe; // no weekends (int)
+ var rtl, dis, dit; // day index sign / translate
+ var minMinute, maxMinute;
+ var colFormat;
+
+
+ /* Rendering
-----------------------------------------------------------------------------*/
disableTextSelection(element.addClass('fc-agenda'));
- function renderAgenda(c, colFormat) {
-
+ function renderAgenda(c) {
colCnt = c;
+ updateOptions();
+ if (!dayTable) {
+ buildSkeleton();
+ } else {
+ clearEvents();
+ }
+ updateCells();
+ }
- // update option-derived variables
- tm = options.theme ? 'ui' : 'fc';
- nwe = options.weekends ? 0 : 1;
- firstDay = options.firstDay;
- if (rtl = options.isRTL) {
+
+ function updateOptions() {
+ tm = opt('theme') ? 'ui' : 'fc';
+ nwe = opt('weekends') ? 0 : 1;
+ firstDay = opt('firstDay');
+ if (rtl = opt('isRTL')) {
dis = -1;
dit = colCnt - 1;
} else {
dis = 1;
dit = 0;
}
- minMinute = parseTime(options.minTime);
- maxMinute = parseTime(options.maxTime);
+ minMinute = parseTime(opt('minTime'));
+ maxMinute = parseTime(opt('maxTime'));
+ colFormat = opt('columnFormat');
+ }
- var d0 = rtl ? addDays(cloneDate(view.visEnd), -1) : cloneDate(view.visStart),
- d = cloneDate(d0),
- today = clearTime(new Date());
- if (!head) { // first time rendering, build from scratch
+ function buildSkeleton() {
+ var headerClass = tm + "-widget-header";
+ var contentClass = tm + "-widget-content";
+ var s;
+ var i;
+ var d;
+ var maxd;
+ var minutes;
+ var slotNormal = opt('slotMinutes') % 15 == 0;
- var i,
- minutes,
- slotNormal = options.slotMinutes % 15 == 0, //...
+ s =
+ "<table style='width:100%' class='fc-agenda-days fc-border-separate' cellspacing='0'>" +
+ "<thead>" +
+ "<tr>" +
+ "<th class='fc-agenda-axis " + headerClass + "'> </th>";
+ for (i = 0; i < colCnt; i++) {
+ s +=
+ "<th class='fc- fc-col" + i + ' ' + headerClass + "'/>"; // fc- needed for setDayID
+ }
+ s +=
+ "<th class='fc-agenda-gutter " + headerClass + "'> </th>" +
+ "</tr>" +
+ "</thead>" +
+ "<tbody>" +
+ "<tr>" +
+ "<th class='fc-agenda-axis " + headerClass + "'> </th>";
+ for (i = 0; i < colCnt; i++) {
+ s +=
+ "<td class='fc- fc-col" + i + ' ' + contentClass + "'>" + // fc- needed for setDayID
+ "<div>" +
+ "<div class='fc-day-content'>" +
+ "<div style='position:relative'> </div>" +
+ "</div>" +
+ "</div>" +
+ "</td>";
+ }
+ s +=
+ "<td class='fc-agenda-gutter " + contentClass + "'> </td>" +
+ "</tr>" +
+ "</tbody>" +
+ "</table>";
+ dayTable = $(s).appendTo(element);
+ dayHead = dayTable.find('thead');
+ dayHeadCells = dayHead.find('th').slice(1, -1);
+ dayBody = dayTable.find('tbody');
+ dayBodyCells = dayBody.find('td').slice(0, -1);
+ dayBodyCellInners = dayBodyCells.find('div.fc-day-content div');
+ dayBodyFirstCell = dayBodyCells.eq(0);
+ dayBodyFirstCellStretcher = dayBodyFirstCell.find('> div');
- // head
- s = "<div class='fc-agenda-head' style='position:relative;z-index:4'>" +
- "<table style='width:100%'>" +
- "<tr class='fc-first" + (options.allDaySlot ? '' : ' fc-last') + "'>" +
- "<th class='fc-leftmost " +
- tm + "-state-default'> </th>";
- for (i = 0; i < colCnt; i++) {
- s += "<th class='fc-" +
- dayIDs[d.getDay()] + ' ' + // needs to be first
- tm + '-state-default' +
- "'>" + formatDate(d, colFormat, options) + "</th>";
- addDays(d, dis);
- if (nwe) {
- skipWeekend(d, dis);
- }
- }
- s += "<th class='" + tm + "-state-default'> </th></tr>";
- if (options.allDaySlot) {
- s += "<tr class='fc-all-day'>" +
- "<th class='fc-axis fc-leftmost " + tm + "-state-default'>" + options.allDayText + "</th>" +
- "<td colspan='" + colCnt + "' class='" + tm + "-state-default'>" +
- "<div class='fc-day-content'><div style='position:relative'> </div></div></td>" +
- "<th class='" + tm + "-state-default'> </th>" +
- "</tr><tr class='fc-divider fc-last'><th colspan='" + (colCnt + 2) + "' class='" +
- tm + "-state-default fc-leftmost'><div/></th></tr>";
- }
- s += "</table></div>";
- head = $(s).appendTo(element);
- dayBind(head.find('td'));
+ markFirstLast(dayHead.add(dayHead.find('tr')));
+ markFirstLast(dayBody.add(dayBody.find('tr')));
- // all-day event container
- daySegmentContainer = $("<div style='position:absolute;z-index:8;top:0;left:0'/>").appendTo(head);
+ axisFirstCells = dayHead.find('th:first');
+ gutterCells = dayTable.find('.fc-agenda-gutter');
- // body
- d = zeroDate();
- var maxd = addMinutes(cloneDate(d), maxMinute);
- addMinutes(d, minMinute);
- s = "<table>";
- for (i = 0; d < maxd; i++) {
- minutes = d.getMinutes();
- s += "<tr class='" +
- (!i ? 'fc-first' : (!minutes ? '' : 'fc-minor')) +
- "'><th class='fc-axis fc-leftmost " + tm + "-state-default'>" +
- ((!slotNormal || !minutes) ? formatDate(d, options.axisFormat) : ' ') +
- "</th><td class='fc-slot" + i + ' ' +
- tm + "-state-default'><div style='position:relative'> </div></td></tr>";
- addMinutes(d, options.slotMinutes);
- slotCnt++;
- }
- s += "</table>";
- body = $("<div class='fc-agenda-body' style='position:relative;z-index:2;overflow:auto'/>")
- .append(bodyContent = $("<div style='position:relative;overflow:hidden'/>")
- .append(bodyTable = $(s)))
- .appendTo(element);
- slotBind(body.find('td'));
+ slotLayer =
+ $("<div style='position:absolute;z-index:2;left:0;width:100%'/>")
+ .appendTo(element);
- // slot event container
- slotSegmentContainer = $("<div style='position:absolute;z-index:8;top:0;left:0'/>").appendTo(bodyContent);
+ if (opt('allDaySlot')) {
- // background stripes
- d = cloneDate(d0);
- s = "<div class='fc-agenda-bg' style='position:absolute;z-index:1'>" +
- "<table style='width:100%;height:100%'><tr class='fc-first'>";
- for (i = 0; i < colCnt; i++) {
- s += "<td class='fc-" +
- dayIDs[d.getDay()] + ' ' + // needs to be first
- tm + '-state-default ' +
- (!i ? 'fc-leftmost ' : '') +
- (+d == +today ? tm + '-state-highlight fc-today' : 'fc-not-today') +
- "'><div class='fc-day-content'><div> </div></div></td>";
- addDays(d, dis);
- if (nwe) {
- skipWeekend(d, dis);
- }
- }
- s += "</tr></table></div>";
- bg = $(s).appendTo(element);
+ daySegmentContainer =
+ $("<div style='position:absolute;z-index:8;top:0;left:0'/>")
+ .appendTo(slotLayer);
- } else { // skeleton already built, just modify it
+ s =
+ "<table style='width:100%' class='fc-agenda-allday' cellspacing='0'>" +
+ "<tr>" +
+ "<th class='" + headerClass + " fc-agenda-axis'>" + opt('allDayText') + "</th>" +
+ "<td>" +
+ "<div class='fc-day-content'><div style='position:relative'/></div>" +
+ "</td>" +
+ "<th class='" + headerClass + " fc-agenda-gutter'> </th>" +
+ "</tr>" +
+ "</table>";
+ allDayTable = $(s).appendTo(slotLayer);
+ allDayRow = allDayTable.find('tr');
- clearEvents();
+ dayBind(allDayRow.find('td'));
- // redo column header text and class
- head.find('tr:first th').slice(1, -1).each(function() {
- $(this).text(formatDate(d, colFormat, options));
- this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]);
- addDays(d, dis);
- if (nwe) {
- skipWeekend(d, dis);
- }
- });
+ axisFirstCells = axisFirstCells.add(allDayTable.find('th:first'));
+ gutterCells = gutterCells.add(allDayTable.find('th.fc-agenda-gutter'));
- // change classes of background stripes
- d = cloneDate(d0);
- bg.find('td').each(function() {
- this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]);
- if (+d == +today) {
- $(this)
- .removeClass('fc-not-today')
- .addClass('fc-today')
- .addClass(tm + '-state-highlight');
- } else {
- $(this)
- .addClass('fc-not-today')
- .removeClass('fc-today')
- .removeClass(tm + '-state-highlight');
- }
- addDays(d, dis);
- if (nwe) {
- skipWeekend(d, dis);
- }
- });
+ slotLayer.append(
+ "<div class='fc-agenda-divider " + headerClass + "'>" +
+ "<div class='fc-agenda-divider-inner'/>" +
+ "</div>"
+ );
+ } else {
+
+ daySegmentContainer = $([]); // in jQuery 1.4, we can just do $()
+
}
+ slotScroller =
+ $("<div style='position:absolute;width:100%;overflow-x:hidden;overflow-y:auto'/>")
+ .appendTo(slotLayer);
+
+ slotContent =
+ $("<div style='position:relative;width:100%;overflow:hidden'/>")
+ .appendTo(slotScroller);
+
+ slotSegmentContainer =
+ $("<div style='position:absolute;z-index:8;top:0;left:0'/>")
+ .appendTo(slotContent);
+
+ s =
+ "<table class='fc-agenda-slots' style='width:100%' cellspacing='0'>" +
+ "<tbody>";
+ d = zeroDate();
+ maxd = addMinutes(cloneDate(d), maxMinute);
+ addMinutes(d, minMinute);
+ slotCnt = 0;
+ for (i = 0; d < maxd; i++) {
+ minutes = d.getMinutes();
+ s +=
+ "<tr class='fc-slot" + i + ' ' + (!minutes ? '' : 'fc-minor') + "'>" +
+ "<th class='fc-agenda-axis " + headerClass + "'>" +
+ ((!slotNormal || !minutes) ? formatDate(d, opt('axisFormat')) : ' ') +
+ "</th>" +
+ "<td class='" + contentClass + "'>" +
+ "<div style='position:relative'> </div>" +
+ "</td>" +
+ "</tr>";
+ addMinutes(d, opt('slotMinutes'));
+ slotCnt++;
+ }
+ s +=
+ "</tbody>" +
+ "</table>";
+ slotTable = $(s).appendTo(slotContent);
+ slotTableFirstInner = slotTable.find('div:first');
+
+ slotBind(slotTable.find('td'));
+
+ axisFirstCells = axisFirstCells.add(slotTable.find('th:first'));
}
- function resetScroll() {
- var d0 = zeroDate(),
- scrollDate = cloneDate(d0);
- scrollDate.setHours(options.firstHour);
- var top = timePosition(d0, scrollDate) + 1, // +1 for the border
- scroll = function() {
- body.scrollTop(top);
- };
- scroll();
- setTimeout(scroll, 0); // overrides any previous scroll state made by the browser
+ function updateCells() {
+ var i;
+ var headCell;
+ var bodyCell;
+ var date;
+ var today = clearTime(new Date());
+ for (i = 0; i < colCnt; i++) {
+ date = colDate(i);
+ headCell = dayHeadCells.eq(i);
+ headCell.html(formatDate(date, colFormat));
+ bodyCell = dayBodyCells.eq(i);
+ if (+date == +today) {
+ bodyCell.addClass(tm + '-state-highlight fc-today');
+ } else {
+ bodyCell.removeClass(tm + '-state-highlight fc-today');
+ }
+ setDayID(headCell.add(bodyCell), date);
+ }
}
function setHeight(height, dateChanged) {
+ if (height === undefined) {
+ height = viewHeight;
+ }
viewHeight = height;
slotTopCache = {};
- body.height(height - head.height());
+ var headHeight = dayBody.position().top;
+ var allDayHeight = slotScroller.position().top; // including divider
+ var bodyHeight = Math.min(// total body height, including borders
+ height - headHeight, // when scrollbars
+ slotTable.height() + allDayHeight + 1 // when no scrollbars. +1 for bottom border
+ );
- slotHeight = body.find('tr:first div').height() + 1;
+ dayBodyFirstCellStretcher
+ .height(bodyHeight - vsides(dayBodyFirstCell));
- bg.css({
- top: head.find('tr').height(),
- height: height
- });
+ slotLayer.css('top', headHeight);
+ slotScroller.height(bodyHeight - allDayHeight - 1);
+
+ slotHeight = slotTableFirstInner.height() + 1; // +1 for border
+
if (dateChanged) {
resetScroll();
}
@@ -1982,78 +3192,497 @@
viewWidth = width;
colContentPositions.clear();
- body.width(width);
- bodyTable.width('');
-
- var topTDs = head.find('tr:first th'),
- stripeTDs = bg.find('td'),
- clientWidth = body[0].clientWidth;
-
- bodyTable.width(clientWidth);
-
- // time-axis width
axisWidth = 0;
setOuterWidth(
- head.find('tr:lt(2) th:first').add(body.find('tr:first th'))
+ axisFirstCells
.width('')
- .each(function() {
- axisWidth = Math.max(axisWidth, $(this).outerWidth());
- }),
+ .each(function(i, _cell) {
+ axisWidth = Math.max(axisWidth, $(_cell).outerWidth());
+ }),
axisWidth
- );
+ );
- // column width
- colWidth = Math.floor((clientWidth - axisWidth) / colCnt);
- setOuterWidth(stripeTDs.slice(0, -1), colWidth);
- setOuterWidth(topTDs.slice(1, -2), colWidth);
- setOuterWidth(topTDs.slice(-2, -1), clientWidth - axisWidth - colWidth * (colCnt - 1));
+ var slotTableWidth = slotScroller[0].clientWidth; // needs to be done after axisWidth (for IE7)
+ //slotTable.width(slotTableWidth);
- bg.css({
- left: axisWidth,
- width: clientWidth - axisWidth
- });
+ gutterWidth = slotScroller.width() - slotTableWidth;
+ if (gutterWidth) {
+ setOuterWidth(gutterCells, gutterWidth);
+ gutterCells
+ .show()
+ .prev()
+ .removeClass('fc-last');
+ } else {
+ gutterCells
+ .hide()
+ .prev()
+ .addClass('fc-last');
+ }
+
+ colWidth = Math.floor((slotTableWidth - axisWidth) / colCnt);
+ setOuterWidth(dayHeadCells.slice(0, -1), colWidth);
}
+ function resetScroll() {
+ var d0 = zeroDate();
+ var scrollDate = cloneDate(d0);
+ scrollDate.setHours(opt('firstHour'));
+ var top = timePosition(d0, scrollDate) + 1; // +1 for the border
+ function scroll() {
+ slotScroller.scrollTop(top);
+ }
+
+ scroll();
+ setTimeout(scroll, 0); // overrides any previous scroll state made by the browser
+ }
+
+
+ function beforeHide() {
+ savedScrollTop = slotScroller.scrollTop();
+ }
+
+
+ function afterShow() {
+ slotScroller.scrollTop(savedScrollTop);
+ }
+
+
/* Slot/Day clicking and binding
-----------------------------------------------------------------------*/
- function dayBind(tds) {
- tds.click(slotClick)
+ function dayBind(cells) {
+ cells.click(slotClick)
.mousedown(daySelectionMousedown);
}
- function slotBind(tds) {
- tds.click(slotClick)
+ function slotBind(cells) {
+ cells.click(slotClick)
.mousedown(slotSelectionMousedown);
}
function slotClick(ev) {
- if (!view.option('selectable')) { // SelectionManager will worry about dayClick
- var col = Math.floor((ev.pageX - bg.offset().left) / colWidth),
- date = addDays(cloneDate(view.visStart), dit + dis * col),
- rowMatch = this.className.match(/fc-slot(\d+)/);
+ if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
+ var col = Math.min(colCnt - 1, Math.floor((ev.pageX - dayTable.offset().left - axisWidth) / colWidth));
+ var date = colDate(col);
+ var rowMatch = this.parentNode.className.match(/fc-slot(\d+)/); // TODO: maybe use data
if (rowMatch) {
- var mins = parseInt(rowMatch[1]) * options.slotMinutes,
- hours = Math.floor(mins / 60);
+ var mins = parseInt(rowMatch[1]) * opt('slotMinutes');
+ var hours = Math.floor(mins / 60);
date.setHours(hours);
date.setMinutes(mins % 60 + minMinute);
- view.trigger('dayClick', this, date, false, ev);
+ trigger('dayClick', dayBodyCells[col], date, false, ev);
} else {
- view.trigger('dayClick', this, date, true, ev);
+ trigger('dayClick', dayBodyCells[col], date, true, ev);
}
}
}
- /* Event Rendering
+ /* Semi-transparent Overlay Helpers
+ -----------------------------------------------------*/
+
+
+ function renderDayOverlay(startDate, endDate, refreshCoordinateGrid) { // endDate is exclusive
+ if (refreshCoordinateGrid) {
+ coordinateGrid.build();
+ }
+ var visStart = cloneDate(t.visStart);
+ var startCol, endCol;
+ if (rtl) {
+ startCol = dayDiff(endDate, visStart) * dis + dit + 1;
+ endCol = dayDiff(startDate, visStart) * dis + dit + 1;
+ } else {
+ startCol = dayDiff(startDate, visStart);
+ endCol = dayDiff(endDate, visStart);
+ }
+ startCol = Math.max(0, startCol);
+ endCol = Math.min(colCnt, endCol);
+ if (startCol < endCol) {
+ dayBind(
+ renderCellOverlay(0, startCol, 0, endCol - 1)
+ );
+ }
+ }
+
+
+ function renderCellOverlay(row0, col0, row1, col1) { // only for all-day?
+ var rect = coordinateGrid.rect(row0, col0, row1, col1, slotLayer);
+ return renderOverlay(rect, slotLayer);
+ }
+
+
+ function renderSlotOverlay(overlayStart, overlayEnd) {
+ var dayStart = cloneDate(t.visStart);
+ var dayEnd = addDays(cloneDate(dayStart), 1);
+ for (var i = 0; i < colCnt; i++) {
+ var stretchStart = new Date(Math.max(dayStart, overlayStart));
+ var stretchEnd = new Date(Math.min(dayEnd, overlayEnd));
+ if (stretchStart < stretchEnd) {
+ var col = i * dis + dit;
+ var rect = coordinateGrid.rect(0, col, 0, col, slotContent); // only use it for horizontal coords
+ var top = timePosition(dayStart, stretchStart);
+ var bottom = timePosition(dayStart, stretchEnd);
+ rect.top = top;
+ rect.height = bottom - top;
+ slotBind(
+ renderOverlay(rect, slotContent)
+ );
+ }
+ addDays(dayStart, 1);
+ addDays(dayEnd, 1);
+ }
+ }
+
+
+ /* Coordinate Utilities
-----------------------------------------------------------------------------*/
+
+ coordinateGrid = new CoordinateGrid(function(rows, cols) {
+ var e, n, p;
+ dayHeadCells.each(function(i, _e) {
+ e = $(_e);
+ n = e.offset().left;
+ if (i) {
+ p[1] = n;
+ }
+ p = [n];
+ cols[i] = p;
+ });
+ p[1] = n + e.outerWidth();
+ if (opt('allDaySlot')) {
+ e = allDayRow;
+ n = e.offset().top;
+ rows[0] = [n, n + e.outerHeight()];
+ }
+ var slotTableTop = slotContent.offset().top;
+ var slotScrollerTop = slotScroller.offset().top;
+ var slotScrollerBottom = slotScrollerTop + slotScroller.outerHeight();
+
+ function constrain(n) {
+ return Math.max(slotScrollerTop, Math.min(slotScrollerBottom, n));
+ }
+
+ for (var i = 0; i < slotCnt; i++) {
+ rows.push([
+ constrain(slotTableTop + slotHeight * i),
+ constrain(slotTableTop + slotHeight * (i + 1))
+ ]);
+ }
+ });
+
+
+ hoverListener = new HoverListener(coordinateGrid);
+
+
+ colContentPositions = new HorizontalPositionCache(function(col) {
+ return dayBodyCellInners.eq(col);
+ });
+
+
+ function colContentLeft(col) {
+ return colContentPositions.left(col);
+ }
+
+
+ function colContentRight(col) {
+ return colContentPositions.right(col);
+ }
+
+
+ function dateCell(date) { // "cell" terminology is now confusing
+ return {
+ row: Math.floor(dayDiff(date, t.visStart) / 7),
+ col: dayOfWeekCol(date.getDay())
+ };
+ }
+
+
+ function cellDate(cell) {
+ var d = colDate(cell.col);
+ var slotIndex = cell.row;
+ if (opt('allDaySlot')) {
+ slotIndex--;
+ }
+ if (slotIndex >= 0) {
+ addMinutes(d, minMinute + slotIndex * opt('slotMinutes'));
+ }
+ return d;
+ }
+
+
+ function colDate(col) { // returns dates with 00:00:00
+ return addDays(cloneDate(t.visStart), col * dis + dit);
+ }
+
+
+ function cellIsAllDay(cell) {
+ return opt('allDaySlot') && !cell.row;
+ }
+
+
+ function dayOfWeekCol(dayOfWeek) {
+ return ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt) * dis + dit;
+ }
+
+
+ // get the Y coordinate of the given time on the given day (both Date objects)
+ function timePosition(day, time) { // both date objects. day holds 00:00 of current day
+ day = cloneDate(day, true);
+ if (time < addMinutes(cloneDate(day), minMinute)) {
+ return 0;
+ }
+ if (time >= addMinutes(cloneDate(day), maxMinute)) {
+ return slotTable.height();
+ }
+ var slotMinutes = opt('slotMinutes'),
+ minutes = time.getHours() * 60 + time.getMinutes() - minMinute,
+ slotI = Math.floor(minutes / slotMinutes),
+ slotTop = slotTopCache[slotI];
+ if (slotTop === undefined) {
+ slotTop = slotTopCache[slotI] = slotTable.find('tr:eq(' + slotI + ') td div')[0].offsetTop; //.position().top; // need this optimization???
+ }
+ return Math.max(0, Math.round(
+ slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes)
+ ));
+ }
+
+
+ function allDayBounds() {
+ return {
+ left: axisWidth,
+ right: viewWidth - gutterWidth
+ }
+ }
+
+
+ function getAllDayRow(index) {
+ return allDayRow;
+ }
+
+
+ function defaultEventEnd(event) {
+ var start = cloneDate(event.start);
+ if (event.allDay) {
+ return start;
+ }
+ return addMinutes(start, opt('defaultEventMinutes'));
+ }
+
+
+ /* Selection
+ ---------------------------------------------------------------------------------*/
+
+
+ function defaultSelectionEnd(startDate, allDay) {
+ if (allDay) {
+ return cloneDate(startDate);
+ }
+ return addMinutes(cloneDate(startDate), opt('slotMinutes'));
+ }
+
+
+ function renderSelection(startDate, endDate, allDay) { // only for all-day
+ if (allDay) {
+ if (opt('allDaySlot')) {
+ renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true);
+ }
+ } else {
+ renderSlotSelection(startDate, endDate);
+ }
+ }
+
+
+ function renderSlotSelection(startDate, endDate) {
+ var helperOption = opt('selectHelper');
+ coordinateGrid.build();
+ if (helperOption) {
+ var col = dayDiff(startDate, t.visStart) * dis + dit;
+ if (col >= 0 && col < colCnt) { // only works when times are on same day
+ var rect = coordinateGrid.rect(0, col, 0, col, slotContent); // only for horizontal coords
+ var top = timePosition(startDate, startDate);
+ var bottom = timePosition(startDate, endDate);
+ if (bottom > top) { // protect against selections that are entirely before or after visible range
+ rect.top = top;
+ rect.height = bottom - top;
+ rect.left += 2;
+ rect.width -= 5;
+ if ($.isFunction(helperOption)) {
+ var helperRes = helperOption(startDate, endDate);
+ if (helperRes) {
+ rect.position = 'absolute';
+ rect.zIndex = 8;
+ selectionHelper = $(helperRes)
+ .css(rect)
+ .appendTo(slotContent);
+ }
+ } else {
+ rect.isStart = true; // conside rect a "seg" now
+ rect.isEnd = true; //
+ selectionHelper = $(slotSegHtml(
+ {
+ title: '',
+ start: startDate,
+ end: endDate,
+ className: ['fc-select-helper'],
+ editable: false
+ },
+ rect
+ ));
+ selectionHelper.css('opacity', opt('dragOpacity'));
+ }
+ if (selectionHelper) {
+ slotBind(selectionHelper);
+ slotContent.append(selectionHelper);
+ setOuterWidth(selectionHelper, rect.width, true); // needs to be after appended
+ setOuterHeight(selectionHelper, rect.height, true);
+ }
+ }
+ }
+ } else {
+ renderSlotOverlay(startDate, endDate);
+ }
+ }
+
+
+ function clearSelection() {
+ clearOverlays();
+ if (selectionHelper) {
+ selectionHelper.remove();
+ selectionHelper = null;
+ }
+ }
+
+
+ function slotSelectionMousedown(ev) {
+ if (ev.which == 1 && opt('selectable')) { // ev.which==1 means left mouse button
+ unselect(ev);
+ var dates;
+ hoverListener.start(function(cell, origCell) {
+ clearSelection();
+ if (cell && cell.col == origCell.col && !cellIsAllDay(cell)) {
+ var d1 = cellDate(origCell);
+ var d2 = cellDate(cell);
+ dates = [
+ d1,
+ addMinutes(cloneDate(d1), opt('slotMinutes')),
+ d2,
+ addMinutes(cloneDate(d2), opt('slotMinutes'))
+ ].sort(cmp);
+ renderSlotSelection(dates[0], dates[3]);
+ } else {
+ dates = null;
+ }
+ }, ev);
+ $(document).one('mouseup', function(ev) {
+ hoverListener.stop();
+ if (dates) {
+ if (+dates[0] == +dates[1]) {
+ reportDayClick(dates[0], false, ev);
+ }
+ reportSelection(dates[0], dates[3], false, ev);
+ }
+ });
+ }
+ }
+
+
+ function reportDayClick(date, allDay, ev) {
+ trigger('dayClick', dayBodyCells[dayOfWeekCol(date.getDay())], date, allDay, ev);
+ }
+
+
+ /* External Dragging
+ --------------------------------------------------------------------------------*/
+
+
+ function dragStart(_dragElement, ev, ui) {
+ hoverListener.start(function(cell) {
+ clearOverlays();
+ if (cell) {
+ if (cellIsAllDay(cell)) {
+ renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
+ } else {
+ var d1 = cellDate(cell);
+ var d2 = addMinutes(cloneDate(d1), opt('defaultEventMinutes'));
+ renderSlotOverlay(d1, d2);
+ }
+ }
+ }, ev);
+ }
+
+
+ function dragStop(_dragElement, ev, ui) {
+ var cell = hoverListener.stop();
+ clearOverlays();
+ if (cell) {
+ trigger('drop', _dragElement, cellDate(cell), cellIsAllDay(cell), ev, ui);
+ }
+ }
+
+
+ }
+
+ function AgendaEventRenderer() {
+ var t = this;
+
+
+ // exports
+ t.renderEvents = renderEvents;
+ t.compileDaySegs = compileDaySegs; // for DayEventRenderer
+ t.clearEvents = clearEvents;
+ t.slotSegHtml = slotSegHtml;
+ t.bindDaySeg = bindDaySeg;
+
+
+ // imports
+ DayEventRenderer.call(t);
+ var opt = t.opt;
+ var trigger = t.trigger;
+ //var setOverflowHidden = t.setOverflowHidden;
+ var isEventDraggable = t.isEventDraggable;
+ var isEventResizable = t.isEventResizable;
+ var eventEnd = t.eventEnd;
+ var reportEvents = t.reportEvents;
+ var reportEventClear = t.reportEventClear;
+ var eventElementHandlers = t.eventElementHandlers;
+ var setHeight = t.setHeight;
+ var getDaySegmentContainer = t.getDaySegmentContainer;
+ var getSlotSegmentContainer = t.getSlotSegmentContainer;
+ var getHoverListener = t.getHoverListener;
+ var getMaxMinute = t.getMaxMinute;
+ var getMinMinute = t.getMinMinute;
+ var timePosition = t.timePosition;
+ var colContentLeft = t.colContentLeft;
+ var colContentRight = t.colContentRight;
+ var renderDaySegs = t.renderDaySegs;
+ var resizableDayEvent = t.resizableDayEvent; // TODO: streamline binding architecture
+ var getColCnt = t.getColCnt;
+ var getColWidth = t.getColWidth;
+ var getSlotHeight = t.getSlotHeight;
+ var getBodyContent = t.getBodyContent;
+ var reportEventElement = t.reportEventElement;
+ var showEvents = t.showEvents;
+ var hideEvents = t.hideEvents;
+ var eventDrop = t.eventDrop;
+ var eventResize = t.eventResize;
+ var renderDayOverlay = t.renderDayOverlay;
+ var clearOverlays = t.clearOverlays;
+ var calendar = t.calendar;
+ var formatDate = calendar.formatDate;
+ var formatDates = calendar.formatDates;
+
+
+ /* Rendering
+ ----------------------------------------------------------------------------*/
+
+
function renderEvents(events, modifiedEventId) {
- view.reportEvents(cachedEvents = events);
+ reportEvents(events);
var i, len = events.length,
dayEvents = [],
slotEvents = [];
@@ -2064,26 +3693,23 @@
slotEvents.push(events[i]);
}
}
- renderDaySegs(compileDaySegs(dayEvents), modifiedEventId);
+ if (opt('allDaySlot')) {
+ renderDaySegs(compileDaySegs(dayEvents), modifiedEventId);
+ setHeight(); // no params means set to viewHeight
+ }
renderSlotSegs(compileSlotSegs(slotEvents), modifiedEventId);
}
- function rerenderEvents(modifiedEventId) {
- clearEvents();
- renderEvents(cachedEvents, modifiedEventId);
- }
-
-
function clearEvents() {
- view._clearEvents(); // only clears the hashes
- daySegmentContainer.empty();
- slotSegmentContainer.empty();
+ reportEventClear();
+ getDaySegmentContainer().empty();
+ getSlotSegmentContainer().empty();
}
function compileDaySegs(events) {
- var levels = stackSegs(view.sliceSegs(events, $.map(events, exclEndDay), view.visStart, view.visEnd)),
+ var levels = stackSegs(sliceSegs(events, $.map(events, exclEndDay), t.visStart, t.visEnd)),
i, levelCnt = levels.length, level,
j, seg,
segs = [];
@@ -2092,7 +3718,7 @@
for (j = 0; j < level.length; j++) {
seg = level[j];
seg.row = 0;
- seg.level = i;
+ seg.level = i; // not needed anymore
segs.push(seg);
}
}
@@ -2101,14 +3727,17 @@
function compileSlotSegs(events) {
- var d = addMinutes(cloneDate(view.visStart), minMinute),
+ var colCnt = getColCnt(),
+ minMinute = getMinMinute(),
+ maxMinute = getMaxMinute(),
+ d = addMinutes(cloneDate(t.visStart), minMinute),
visEventEnds = $.map(events, slotEventEnd),
i, col,
j, level,
k, seg,
segs = [];
for (i = 0; i < colCnt; i++) {
- col = stackSegs(view.sliceSegs(events, visEventEnds, d, addMinutes(cloneDate(d), maxMinute - minMinute)));
+ col = stackSegs(sliceSegs(events, visEventEnds, d, addMinutes(cloneDate(d), maxMinute - minMinute)));
countForwardSegs(col);
for (j = 0; j < col.length; j++) {
level = col[j];
@@ -2125,30 +3754,11 @@
}
- // renders 'all-day' events at the top
-
- function renderDaySegs(segs, modifiedEventId) {
- if (options.allDaySlot) {
- _renderDaySegs(
- segs,
- 1,
- view,
- axisWidth,
- viewWidth,
- function() {
- return head.find('tr.fc-all-day');
- },
- function(dayOfWeek) {
- return axisWidth + colContentPositions.left(dayOfWeekCol(dayOfWeek));
- },
- function(dayOfWeek) {
- return axisWidth + colContentPositions.right(dayOfWeekCol(dayOfWeek));
- },
- daySegmentContainer,
- daySegBind,
- modifiedEventId
- );
- setHeight(viewHeight); // might have pushed the body down, so resize
+ function slotEventEnd(event) {
+ if (event.end) {
+ return cloneDate(event.end);
+ } else {
+ return addMinutes(cloneDate(event.start), opt('defaultEventMinutes'));
}
}
@@ -2159,7 +3769,7 @@
var i, segCnt = segs.length, seg,
event,
- className,
+ classes,
top, bottom,
colI, levelI, forward,
leftmost,
@@ -2173,27 +3783,31 @@
vsideCache = {},
hsideCache = {},
key, val,
- titleSpan,
- height;
+ contentElement,
+ height,
+ slotSegmentContainer = getSlotSegmentContainer(),
+ rtl, dis, dit,
+ colCnt = getColCnt();
+ if (rtl = opt('isRTL')) {
+ dis = -1;
+ dit = colCnt - 1;
+ } else {
+ dis = 1;
+ dit = 0;
+ }
+
// calculate position/dimensions, create html
for (i = 0; i < segCnt; i++) {
seg = segs[i];
event = seg.event;
- className = 'fc-event fc-event-vert ';
- if (seg.isStart) {
- className += 'fc-corner-top ';
- }
- if (seg.isEnd) {
- className += 'fc-corner-bottom ';
- }
top = timePosition(seg.start, seg.start);
bottom = timePosition(seg.start, seg.end);
colI = seg.col;
levelI = seg.level;
forward = seg.forward || 0;
- leftmost = axisWidth + colContentPositions.left(colI * dis + dit);
- availWidth = axisWidth + colContentPositions.right(colI * dis + dit) - leftmost;
+ leftmost = colContentLeft(colI * dis + dit);
+ availWidth = colContentRight(colI * dis + dit) - leftmost;
availWidth = Math.min(availWidth - 6, availWidth * .95); // TODO: move this to CSS
if (levelI) {
// indented and thin
@@ -2214,7 +3828,7 @@
seg.left = left;
seg.outerWidth = outerWidth;
seg.outerHeight = bottom - top;
- html += slotSegHtml(event, seg, className);
+ html += slotSegHtml(event, seg);
}
slotSegmentContainer[0].innerHTML = html; // faster than html()
eventElements = slotSegmentContainer.children();
@@ -2224,7 +3838,7 @@
seg = segs[i];
event = seg.event;
eventElement = $(eventElements[i]); // faster than eq()
- triggerRes = view.trigger('eventRender', event, event, eventElement);
+ triggerRes = trigger('eventRender', event, event, eventElement);
if (triggerRes === false) {
eventElement.remove();
} else {
@@ -2232,35 +3846,35 @@
eventElement.remove();
eventElement = $(triggerRes)
.css({
- position: 'absolute',
- top: seg.top,
- left: seg.left
- })
+ position: 'absolute',
+ top: seg.top,
+ left: seg.left
+ })
.appendTo(slotSegmentContainer);
}
seg.element = eventElement;
if (event._id === modifiedEventId) {
- slotSegBind(event, eventElement, seg);
+ bindSlotSeg(event, eventElement, seg);
} else {
eventElement[0]._fci = i; // for lazySegBind
}
- view.reportEventElement(event, eventElement);
+ reportEventElement(event, eventElement);
}
}
- lazySegBind(slotSegmentContainer, segs, slotSegBind);
+ lazySegBind(slotSegmentContainer, segs, bindSlotSeg);
// record event sides and title positions
for (i = 0; i < segCnt; i++) {
seg = segs[i];
if (eventElement = seg.element) {
val = vsideCache[key = seg.key = cssKey(eventElement[0])];
- seg.vsides = val === undefined ? (vsideCache[key] = vsides(eventElement[0], true)) : val;
+ seg.vsides = val === undefined ? (vsideCache[key] = vsides(eventElement, true)) : val;
val = hsideCache[key];
- seg.hsides = val === undefined ? (hsideCache[key] = hsides(eventElement[0], true)) : val;
- titleSpan = eventElement.find('span.fc-event-title');
- if (titleSpan.length) {
- seg.titleTop = titleSpan[0].offsetTop;
+ seg.hsides = val === undefined ? (hsideCache[key] = hsides(eventElement, true)) : val;
+ contentElement = eventElement.find('div.fc-event-content');
+ if (contentElement.length) {
+ seg.contentTop = contentElement[0].offsetTop;
}
}
}
@@ -2269,152 +3883,196 @@
for (i = 0; i < segCnt; i++) {
seg = segs[i];
if (eventElement = seg.element) {
- eventElement[0].style.width = seg.outerWidth - seg.hsides + 'px';
- eventElement[0].style.height = (height = seg.outerHeight - seg.vsides) + 'px';
+ eventElement[0].style.width = Math.max(0, seg.outerWidth - seg.hsides) + 'px';
+ height = Math.max(0, seg.outerHeight - seg.vsides);
+ eventElement[0].style.height = height + 'px';
event = seg.event;
- if (seg.titleTop !== undefined && height - seg.titleTop < 10) {
+ if (seg.contentTop !== undefined && height - seg.contentTop < 10) {
// not enough room for title, put it in the time header
- eventElement.find('span.fc-event-time')
- .text(formatDate(event.start, view.option('timeFormat')) + ' - ' + event.title);
- eventElement.find('span.fc-event-title')
+ eventElement.find('div.fc-event-time')
+ .text(formatDate(event.start, opt('timeFormat')) + ' - ' + event.title);
+ eventElement.find('div.fc-event-title')
.remove();
}
- view.trigger('eventAfterRender', event, event, eventElement);
+ trigger('eventAfterRender', event, event, eventElement);
}
}
}
- function slotSegHtml(event, seg, className) {
- return "<div class='" + className + event.className.join(' ') + "' style='position:absolute;z-index:8;top:" + seg.top + "px;left:" + seg.left + "px'>" +
- "<a" + (event.url ? " href='" + htmlEscape(event.url) + "'" : '') + ">" +
- "<span class='fc-event-bg'></span>" +
- "<span class='fc-event-time'>" + htmlEscape(formatDates(event.start, event.end, view.option('timeFormat'))) + "</span>" +
- "<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" +
- "</a>" +
- ((event.editable || event.editable === undefined && options.editable) && !options.disableResizing && $.fn.resizable ?
- "<div class='ui-resizable-handle ui-resizable-s'>=</div>"
- : '') +
- "</div>";
+
+ function slotSegHtml(event, seg) {
+ var html = "<";
+ var url = event.url;
+ var skinCss = getSkinCss(event, opt);
+ var skinCssAttr = (skinCss ? " style='" + skinCss + "'" : '');
+ var classes = ['fc-event', 'fc-event-skin', 'fc-event-vert'];
+ if (isEventDraggable(event)) {
+ classes.push('fc-event-draggable');
+ }
+ if (seg.isStart) {
+ classes.push('fc-corner-top');
+ }
+ if (seg.isEnd) {
+ classes.push('fc-corner-bottom');
+ }
+ classes = classes.concat(event.className);
+ if (event.source) {
+ classes = classes.concat(event.source.className || []);
+ }
+ if (url) {
+ html += "a href='" + htmlEscape(event.url) + "'";
+ } else {
+ html += "div";
+ }
+ html +=
+ " class='" + classes.join(' ') + "'" +
+ " style='position:absolute;z-index:8;top:" + seg.top + "px;left:" + seg.left + "px;" + skinCss + "'" +
+ ">" +
+ "<div class='fc-event-inner fc-event-skin'" + skinCssAttr + ">" +
+ "<div class='fc-event-head fc-event-skin'" + skinCssAttr + ">" +
+ "<div class='fc-event-time'>" +
+ htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
+ "</div>" +
+ "</div>" +
+ "<div class='fc-event-content'>" +
+ "<div class='fc-event-title'>" +
+ htmlEscape(event.title) +
+ "</div>" +
+ "</div>" +
+ "<div class='fc-event-bg'></div>" +
+ "</div>"; // close inner
+ if (seg.isEnd && isEventResizable(event)) {
+ html +=
+ "<div class='ui-resizable-handle ui-resizable-s'>=</div>";
+ }
+ html +=
+ "</" + (url ? "a" : "div") + ">";
+ return html;
}
- function daySegBind(event, eventElement, seg) {
- view.eventElementHandlers(event, eventElement);
- if (event.editable || event.editable === undefined && options.editable) {
+ function bindDaySeg(event, eventElement, seg) {
+ if (isEventDraggable(event)) {
draggableDayEvent(event, eventElement, seg.isStart);
- if (seg.isEnd) {
- view.resizableDayEvent(event, eventElement, colWidth);
- }
}
+ if (seg.isEnd && isEventResizable(event)) {
+ resizableDayEvent(event, eventElement, seg);
+ }
+ eventElementHandlers(event, eventElement);
+ // needs to be after, because resizableDayEvent might stopImmediatePropagation on click
}
- function slotSegBind(event, eventElement, seg) {
- view.eventElementHandlers(event, eventElement);
- if (event.editable || event.editable === undefined && options.editable) {
- var timeElement = eventElement.find('span.fc-event-time');
+ function bindSlotSeg(event, eventElement, seg) {
+ var timeElement = eventElement.find('div.fc-event-time');
+ if (isEventDraggable(event)) {
draggableSlotEvent(event, eventElement, timeElement);
- if (seg.isEnd) {
- resizableSlotEvent(event, eventElement, timeElement);
- }
}
+ if (seg.isEnd && isEventResizable(event)) {
+ resizableSlotEvent(event, eventElement, timeElement);
+ }
+ eventElementHandlers(event, eventElement);
}
- /* Event Dragging
- -----------------------------------------------------------------------------*/
+ /* Dragging
+ -----------------------------------------------------------------------------------*/
// when event starts out FULL-DAY
function draggableDayEvent(event, eventElement, isStart) {
- if (!options.disableDragging && eventElement.draggable) {
- var origPosition, origWidth,
- resetElement,
- allDay = true,
- matrix;
- eventElement.draggable({
- zIndex: 9,
- opacity: view.option('dragOpacity', 'month'), // use whatever the month view was using
- revertDuration: options.dragRevertDuration,
- start: function(ev, ui) {
- view.hideEvents(event, eventElement);
- view.trigger('eventDragStart', eventElement, event, ev, ui);
- origPosition = eventElement.position();
- origWidth = eventElement.width();
- resetElement = function() {
- if (!allDay) {
- eventElement
- .width(origWidth)
- .height('')
- .draggable('option', 'grid', null);
- allDay = true;
- }
- };
- matrix = buildDayMatrix(function(cell) {
- eventElement.draggable('option', 'revert', !cell || !cell.rowDelta && !cell.colDelta);
- view.clearOverlays();
- if (cell) {
- if (!cell.row) {
- // on full-days
- renderDayOverlay(
- matrix,
- addDays(cloneDate(event.start), cell.colDelta),
- addDays(exclEndDay(event), cell.colDelta)
- );
- resetElement();
- } else {
- // mouse is over bottom slots
- if (isStart && allDay) {
+ var origWidth;
+ var revert;
+ var allDay = true;
+ var dayDelta;
+ var dis = opt('isRTL') ? -1 : 1;
+ var hoverListener = getHoverListener();
+ var colWidth = getColWidth();
+ var slotHeight = getSlotHeight();
+ var minMinute = getMinMinute();
+ eventElement.draggable({
+ zIndex: 9,
+ opacity: opt('dragOpacity', 'month'), // use whatever the month view was using
+ revertDuration: opt('dragRevertDuration'),
+ start: function(ev, ui) {
+ trigger('eventDragStart', eventElement, event, ev, ui);
+ hideEvents(event, eventElement);
+ origWidth = eventElement.width();
+ hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
+ clearOverlays();
+ if (cell) {
+ //setOverflowHidden(true);
+ revert = false;
+ dayDelta = colDelta * dis;
+ if (!cell.row) {
+ // on full-days
+ renderDayOverlay(
+ addDays(cloneDate(event.start), dayDelta),
+ addDays(exclEndDay(event), dayDelta)
+ );
+ resetElement();
+ } else {
+ // mouse is over bottom slots
+ if (isStart) {
+ if (allDay) {
// convert event to temporary slot-event
+ eventElement.width(colWidth - 10); // don't use entire width
setOuterHeight(
- eventElement.width(colWidth - 10), // don't use entire width
+ eventElement,
slotHeight * Math.round(
- (event.end ? ((event.end - event.start) / MINUTE_MS) : options.defaultEventMinutes)
- / options.slotMinutes)
- );
+ (event.end ? ((event.end - event.start) / MINUTE_MS) : opt('defaultEventMinutes'))
+ / opt('slotMinutes')
+ )
+ );
eventElement.draggable('option', 'grid', [colWidth, 1]);
allDay = false;
}
+ } else {
+ revert = true;
}
}
- }, true);
- matrix.mouse(ev);
- },
- drag: function(ev, ui) {
- matrix.mouse(ev);
- },
- stop: function(ev, ui) {
- view.trigger('eventDragStop', eventElement, event, ev, ui);
- view.clearOverlays();
- var cell = matrix.cell;
- var dayDelta = dis * (
- allDay ? // can't trust cell.colDelta when using slot grid
- (cell ? cell.colDelta : 0) :
- Math.floor((ui.position.left - origPosition.left) / colWidth)
- );
- if (!cell || !dayDelta && !cell.rowDelta) {
- // over nothing (has reverted)
+ revert = revert || (allDay && !dayDelta);
+ } else {
resetElement();
- if ($.browser.msie) {
- eventElement.css('filter', ''); // clear IE opacity side-effects
- }
- view.showEvents(event, eventElement);
- } else {
- eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link
- view.eventDrop(
- this, event, dayDelta,
- allDay ? 0 : // minute delta
- Math.round((eventElement.offset().top - bodyContent.offset().top) / slotHeight)
- * options.slotMinutes
- + minMinute
- - (event.start.getHours() * 60 + event.start.getMinutes()),
- allDay, ev, ui
- );
+ //setOverflowHidden(false);
+ revert = true;
}
+ eventElement.draggable('option', 'revert', revert);
+ }, ev, 'drag');
+ },
+ stop: function(ev, ui) {
+ hoverListener.stop();
+ clearOverlays();
+ trigger('eventDragStop', eventElement, event, ev, ui);
+ if (revert) {
+ // hasn't moved or is out of bounds (draggable has already reverted)
+ resetElement();
+ eventElement.css('filter', ''); // clear IE opacity side-effects
+ showEvents(event, eventElement);
+ } else {
+ // changed!
+ var minuteDelta = 0;
+ if (!allDay) {
+ minuteDelta = Math.round((eventElement.offset().top - getBodyContent().offset().top) / slotHeight)
+ * opt('slotMinutes')
+ + minMinute
+ - (event.start.getHours() * 60 + event.start.getMinutes());
+ }
+ eventDrop(this, event, dayDelta, minuteDelta, allDay, ev, ui);
}
- });
+ //setOverflowHidden(false);
+ }
+ });
+ function resetElement() {
+ if (!allDay) {
+ eventElement
+ .width(origWidth)
+ .height('')
+ .draggable('option', 'grid', null);
+ allDay = true;
+ }
}
}
@@ -2422,440 +4080,148 @@
// when event starts out IN TIMESLOTS
function draggableSlotEvent(event, eventElement, timeElement) {
- if (!options.disableDragging && eventElement.draggable) {
- var origPosition,
- resetElement,
- prevSlotDelta, slotDelta,
- allDay = false,
- matrix;
- eventElement.draggable({
- zIndex: 9,
- scroll: false,
- grid: [colWidth, slotHeight],
- axis: colCnt == 1 ? 'y' : false,
- opacity: view.option('dragOpacity'),
- revertDuration: options.dragRevertDuration,
- start: function(ev, ui) {
- view.hideEvents(event, eventElement);
- view.trigger('eventDragStart', eventElement, event, ev, ui);
- if ($.browser.msie) {
- eventElement.find('span.fc-event-bg').hide(); // nested opacities mess up in IE, just hide
- }
- origPosition = eventElement.position();
- resetElement = function() {
- // convert back to original slot-event
- if (allDay) {
- timeElement.css('display', ''); // show() was causing display=inline
- eventElement.draggable('option', 'grid', [colWidth, slotHeight]);
- allDay = false;
- }
- };
- prevSlotDelta = 0;
- matrix = buildDayMatrix(function(cell) {
- eventElement.draggable('option', 'revert', !cell);
- view.clearOverlays();
- if (cell) {
- if (!cell.row && options.allDaySlot) { // over full days
- if (!allDay) {
- // convert to temporary all-day event
- allDay = true;
- timeElement.hide();
- eventElement.draggable('option', 'grid', null);
- }
- renderDayOverlay(
- matrix,
- addDays(cloneDate(event.start), cell.colDelta),
- addDays(exclEndDay(event), cell.colDelta)
- );
- } else { // on slots
- resetElement();
+ var origPosition;
+ var allDay = false;
+ var dayDelta;
+ var minuteDelta;
+ var prevMinuteDelta;
+ var dis = opt('isRTL') ? -1 : 1;
+ var hoverListener = getHoverListener();
+ var colCnt = getColCnt();
+ var colWidth = getColWidth();
+ var slotHeight = getSlotHeight();
+ eventElement.draggable({
+ zIndex: 9,
+ scroll: false,
+ grid: [colWidth, slotHeight],
+ axis: colCnt == 1 ? 'y' : false,
+ opacity: opt('dragOpacity'),
+ revertDuration: opt('dragRevertDuration'),
+ start: function(ev, ui) {
+ trigger('eventDragStart', eventElement, event, ev, ui);
+ hideEvents(event, eventElement);
+ origPosition = eventElement.position();
+ minuteDelta = prevMinuteDelta = 0;
+ hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
+ eventElement.draggable('option', 'revert', !cell);
+ clearOverlays();
+ if (cell) {
+ dayDelta = colDelta * dis;
+ if (opt('allDaySlot') && !cell.row) {
+ // over full days
+ if (!allDay) {
+ // convert to temporary all-day event
+ allDay = true;
+ timeElement.hide();
+ eventElement.draggable('option', 'grid', null);
}
+ renderDayOverlay(
+ addDays(cloneDate(event.start), dayDelta),
+ addDays(exclEndDay(event), dayDelta)
+ );
+ } else {
+ // on slots
+ resetElement();
}
- }, true);
- matrix.mouse(ev);
- },
- drag: function(ev, ui) {
- slotDelta = Math.round((ui.position.top - origPosition.top) / slotHeight);
- if (slotDelta != prevSlotDelta) {
- if (!allDay) {
- // update time header
- var minuteDelta = slotDelta * options.slotMinutes,
- newStart = addMinutes(cloneDate(event.start), minuteDelta),
- newEnd;
- if (event.end) {
- newEnd = addMinutes(cloneDate(event.end), minuteDelta);
- }
- timeElement.text(formatDates(newStart, newEnd, view.option('timeFormat')));
- }
- prevSlotDelta = slotDelta;
}
- matrix.mouse(ev);
- },
- stop: function(ev, ui) {
- view.clearOverlays();
- view.trigger('eventDragStop', eventElement, event, ev, ui);
- var cell = matrix.cell,
- dayDelta = dis * (
- allDay ? // can't trust cell.colDelta when using slot grid
- (cell ? cell.colDelta : 0) :
- Math.floor((ui.position.left - origPosition.left) / colWidth)
- );
- if (!cell || !slotDelta && !dayDelta) {
- resetElement();
- if ($.browser.msie) {
- eventElement
- .css('filter', '')// clear IE opacity side-effects
- .find('span.fc-event-bg').css('display', ''); // .show() made display=inline
- }
- eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position
- view.showEvents(event, eventElement);
- } else {
- view.eventDrop(
- this, event, dayDelta,
- allDay ? 0 : slotDelta * options.slotMinutes, // minute delta
- allDay, ev, ui
- );
+ }, ev, 'drag');
+ },
+ drag: function(ev, ui) {
+ minuteDelta = Math.round((ui.position.top - origPosition.top) / slotHeight) * opt('slotMinutes');
+ if (minuteDelta != prevMinuteDelta) {
+ if (!allDay) {
+ updateTimeText(minuteDelta);
}
+ prevMinuteDelta = minuteDelta;
}
- });
- }
- }
-
-
- /* Event Resizing
- -----------------------------------------------------------------------------*/
-
- // for TIMESLOT events
-
- function resizableSlotEvent(event, eventElement, timeElement) {
- if (!options.disableResizing && eventElement.resizable) {
- var slotDelta, prevSlotDelta;
- eventElement.resizable({
- handles: {
- s: 'div.ui-resizable-s'
- },
- grid: slotHeight,
- start: function(ev, ui) {
- slotDelta = prevSlotDelta = 0;
- view.hideEvents(event, eventElement);
- if ($.browser.msie && $.browser.version == '6.0') {
- eventElement.css('overflow', 'hidden');
- }
- eventElement.css('z-index', 9);
- view.trigger('eventResizeStart', this, event, ev, ui);
- },
- resize: function(ev, ui) {
- // don't rely on ui.size.height, doesn't take grid into account
- slotDelta = Math.round((Math.max(slotHeight, eventElement.height()) - ui.originalSize.height) / slotHeight);
- if (slotDelta != prevSlotDelta) {
- timeElement.text(
- formatDates(
- event.start,
- (!slotDelta && !event.end) ? null : // no change, so don't display time range
- addMinutes(view.eventEnd(event), options.slotMinutes * slotDelta),
- view.option('timeFormat')
- )
- );
- prevSlotDelta = slotDelta;
- }
- },
- stop: function(ev, ui) {
- view.trigger('eventResizeStop', this, event, ev, ui);
- if (slotDelta) {
- view.eventResize(this, event, 0, options.slotMinutes * slotDelta, ev, ui);
- } else {
- eventElement.css('z-index', 8);
- view.showEvents(event, eventElement);
- // BUG: if event was really short, need to put title back in span
- }
- }
- });
- }
- }
-
-
- /* Selecting
- -----------------------------------------------------------------------------*/
-
- daySelectionManager = new SelectionManager(
- view,
- unselect,
- function(startDate, endDate, allDay) {
- renderDayOverlay(
- selectionMatrix,
- startDate,
- addDays(cloneDate(endDate), 1)
- );
},
- clearSelection
- );
-
- function daySelectionMousedown(ev) {
- if (view.option('selectable')) {
- selectionMatrix = buildDayMatrix(function(cell) {
- if (cell) {
- var d = dayColDate(cell.col);
- daySelectionManager.drag(d, d, true);
+ stop: function(ev, ui) {
+ var cell = hoverListener.stop();
+ clearOverlays();
+ trigger('eventDragStop', eventElement, event, ev, ui);
+ if (cell && (dayDelta || minuteDelta || allDay)) {
+ // changed!
+ eventDrop(this, event, dayDelta, allDay ? 0 : minuteDelta, allDay, ev, ui);
} else {
- daySelectionManager.drag();
+ // either no change or out-of-bounds (draggable has already reverted)
+ resetElement();
+ eventElement.css('filter', ''); // clear IE opacity side-effects
+ eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position
+ updateTimeText(0);
+ showEvents(event, eventElement);
}
- });
- documentDragHelp(
- function(ev) {
- selectionMatrix.mouse(ev);
- },
- function(ev) {
- daySelectionManager.dragStop(ev);
- }
- );
- daySelectionManager.dragStart(ev);
- selectionMatrix.mouse(ev);
- return false; // prevent auto-unselect and text selection
- }
- }
-
- slotSelectionManager = new SelectionManager(
- view,
- unselect,
- renderSlotSelection,
- clearSelection
- );
-
- function slotSelectionMousedown(ev) {
- if (view.option('selectable')) {
- selectionMatrix = buildSlotMatrix(function(cell) {
- if (cell) {
- var d = slotCellDate(cell.row, cell.origCol);
- slotSelectionManager.drag(d, addMinutes(cloneDate(d), options.slotMinutes), false);
- } else {
- slotSelectionManager.drag();
- }
- });
- documentDragHelp(
- function(ev) {
- selectionMatrix.mouse(ev);
- },
- function(ev) {
- slotSelectionManager.dragStop(ev);
- }
- );
- slotSelectionManager.dragStart(ev);
- selectionMatrix.mouse(ev);
- return false; // prevent auto-unselect and text selection
- }
- }
-
- documentUnselectAuto(view, unselect);
-
- this.select = function(start, end, allDay) {
- if (allDay) {
- if (options.allDaySlot) {
- if (!end) {
- end = cloneDate(start);
- }
- selectionMatrix = buildDayMatrix();
- daySelectionManager.select(start, end, allDay);
}
- } else {
- if (!end) {
- end = addMinutes(cloneDate(start), options.slotMinutes);
+ });
+ function updateTimeText(minuteDelta) {
+ var newStart = addMinutes(cloneDate(event.start), minuteDelta);
+ var newEnd;
+ if (event.end) {
+ newEnd = addMinutes(cloneDate(event.end), minuteDelta);
}
- selectionMatrix = buildSlotMatrix();
- slotSelectionManager.select(start, end, allDay);
+ timeElement.text(formatDates(newStart, newEnd, opt('timeFormat')));
}
- };
- function unselect() {
- slotSelectionManager.unselect();
- daySelectionManager.unselect();
- }
-
- this.unselect = unselect;
-
-
- /* Selecting drawing utils
- -----------------------------------------------------------------------------*/
-
- function renderSlotSelection(startDate, endDate) {
- var helperOption = view.option('selectHelper');
- if (helperOption) {
- var col = dayDiff(startDate, view.visStart);
- if (col >= 0 && col < colCnt) { // only works when times are on same day
- var rect = selectionMatrix.rect(0, col * dis + dit, 1, col * dis + dit + 1, bodyContent); // only for horizontal coords
- var top = timePosition(startDate, startDate);
- var bottom = timePosition(startDate, endDate);
- if (bottom > top) { // protect against selections that are entirely before or after visible range
- rect.top = top;
- rect.height = bottom - top;
- rect.left += 2;
- rect.width -= 5;
- if ($.isFunction(helperOption)) {
- var helperRes = helperOption(startDate, endDate);
- if (helperRes) {
- rect.position = 'absolute';
- rect.zIndex = 8;
- selectionHelper = $(helperRes)
- .css(rect)
- .appendTo(bodyContent);
- }
- } else {
- selectionHelper = $(slotSegHtml(
- {
- title: '',
- start: startDate,
- end: endDate,
- className: [],
- editable: false
- },
- rect,
- 'fc-event fc-event-vert fc-corner-top fc-corner-bottom '
- ));
- if ($.browser.msie) {
- selectionHelper.find('span.fc-event-bg').hide(); // nested opacities mess up in IE, just hide
- }
- selectionHelper.css('opacity', view.option('dragOpacity'));
- }
- if (selectionHelper) {
- slotBind(selectionHelper);
- bodyContent.append(selectionHelper);
- setOuterWidth(selectionHelper, rect.width, true); // needs to be after appended
- setOuterHeight(selectionHelper, rect.height, true);
- }
- }
+ function resetElement() {
+ // convert back to original slot-event
+ if (allDay) {
+ timeElement.css('display', ''); // show() was causing display=inline
+ eventElement.draggable('option', 'grid', [colWidth, slotHeight]);
+ allDay = false;
}
- } else {
- renderSlotOverlay(selectionMatrix, startDate, endDate);
}
}
- function clearSelection() {
- clearOverlays();
- if (selectionHelper) {
- selectionHelper.remove();
- selectionHelper = null;
- }
- }
+ /* Resizing
+ --------------------------------------------------------------------------------------*/
- /* Semi-transparent Overlay Helpers
- -----------------------------------------------------*/
- function renderDayOverlay(matrix, startDate, endDate) {
- var startCol, endCol;
- if (rtl) {
- startCol = dayDiff(endDate, view.visStart) * dis + dit + 1;
- endCol = dayDiff(startDate, view.visStart) * dis + dit + 1;
- } else {
- startCol = dayDiff(startDate, view.visStart);
- endCol = dayDiff(endDate, view.visStart);
- }
- startCol = Math.max(0, startCol);
- endCol = Math.min(colCnt, endCol);
- if (startCol < endCol) {
- var rect = matrix.rect(0, startCol, 1, endCol, head);
- dayBind(
- view.renderOverlay(rect, head)
+ function resizableSlotEvent(event, eventElement, timeElement) {
+ var slotDelta, prevSlotDelta;
+ var slotHeight = getSlotHeight();
+ eventElement.resizable({
+ handles: {
+ s: 'div.ui-resizable-s'
+ },
+ grid: slotHeight,
+ start: function(ev, ui) {
+ slotDelta = prevSlotDelta = 0;
+ hideEvents(event, eventElement);
+ eventElement.css('z-index', 9);
+ trigger('eventResizeStart', this, event, ev, ui);
+ },
+ resize: function(ev, ui) {
+ // don't rely on ui.size.height, doesn't take grid into account
+ slotDelta = Math.round((Math.max(slotHeight, eventElement.height()) - ui.originalSize.height) / slotHeight);
+ if (slotDelta != prevSlotDelta) {
+ timeElement.text(
+ formatDates(
+ event.start,
+ (!slotDelta && !event.end) ? null : // no change, so don't display time range
+ addMinutes(eventEnd(event), opt('slotMinutes') * slotDelta),
+ opt('timeFormat')
+ )
);
- }
- }
-
- function renderSlotOverlay(matrix, overlayStart, overlayEnd) {
- var dayStart = cloneDate(view.visStart);
- var dayEnd = addDays(cloneDate(dayStart), 1);
- for (var i = 0; i < colCnt; i++) {
- var stretchStart = new Date(Math.max(dayStart, overlayStart));
- var stretchEnd = new Date(Math.min(dayEnd, overlayEnd));
- if (stretchStart < stretchEnd) {
- var rect = matrix.rect(0, i * dis + dit, 1, i * dis + dit + 1, bodyContent); // only use it for horizontal coords
- var top = timePosition(dayStart, stretchStart);
- var bottom = timePosition(dayStart, stretchEnd);
- rect.top = top;
- rect.height = bottom - top;
- slotBind(
- view.renderOverlay(rect, bodyContent)
- );
+ prevSlotDelta = slotDelta;
+ }
+ },
+ stop: function(ev, ui) {
+ trigger('eventResizeStop', this, event, ev, ui);
+ if (slotDelta) {
+ eventResize(this, event, 0, opt('slotMinutes') * slotDelta, ev, ui);
+ } else {
+ eventElement.css('z-index', 8);
+ showEvents(event, eventElement);
+ // BUG: if event was really short, need to put title back in span
+ }
}
- addDays(dayStart, 1);
- addDays(dayEnd, 1);
- }
+ });
}
- function clearOverlays() {
- view.clearOverlays();
- }
-
- /* Coordinate Utilities
- -----------------------------------------------------------------------------*/
-
- // get the Y coordinate of the given time on the given day (both Date objects)
- function timePosition(day, time) { // both date objects. day holds 00:00 of current day
- day = cloneDate(day, true);
- if (time < addMinutes(cloneDate(day), minMinute)) {
- return 0;
- }
- if (time >= addMinutes(cloneDate(day), maxMinute)) {
- return bodyContent.height();
- }
- var slotMinutes = options.slotMinutes,
- minutes = time.getHours() * 60 + time.getMinutes() - minMinute,
- slotI = Math.floor(minutes / slotMinutes),
- slotTop = slotTopCache[slotI];
- if (slotTop === undefined) {
- slotTop = slotTopCache[slotI] = body.find('tr:eq(' + slotI + ') td div')[0].offsetTop;
- }
- return Math.max(0, Math.round(
- slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes)
- ));
- }
-
- function buildDayMatrix(changeCallback, includeSlotArea) {
- var rowElements = options.allDaySlot ? head.find('td') : $([]);
- if (includeSlotArea) {
- rowElements = rowElements.add(body);
- }
- return new HoverMatrix(rowElements, bg.find('td'), changeCallback);
- }
-
- function buildSlotMatrix(changeCallback) {
- return new HoverMatrix(bodyTable.find('td'), bg.find('td'), changeCallback);
- }
-
-
- /* Date Utilities
- ----------------------------------------------------*/
-
- function slotEventEnd(event) {
- if (event.end) {
- return cloneDate(event.end);
- } else {
- return addMinutes(cloneDate(event.start), options.defaultEventMinutes);
- }
- }
-
- function dayOfWeekCol(dayOfWeek) {
- return ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt) * dis + dit;
- }
-
-
- // generating dates from cell row & columns
-
- function dayColDate(col) {
- return addDays(cloneDate(view.visStart), col * dis + dit);
- }
-
- function slotCellDate(row, col) {
- var d = dayColDate(col);
- addMinutes(d, minMinute + row * options.slotMinutes);
- return d;
- }
-
-
}
- // count the number of colliding, higher-level segments (for event squishing)
-
function countForwardSegs(levels) {
var i, j, k, level, segForward, segBack;
for (i = levels.length - 1; i > 0; i--) {
@@ -2873,67 +4239,93 @@
}
- /* Methods & Utilities for All Views
- -----------------------------------------------------------------------------*/
+ function View(element, calendar, viewName) {
+ var t = this;
- var viewMethods = {
- /*
- * Objects inheriting these methods must implement the following properties/methods:
- * - title
- * - start
- * - end
- * - visStart
- * - visEnd
- * - defaultEventEnd(event)
- * - render(events)
- * - rerenderEvents()
- *
- *
- * z-index reservations:
- * 3 - day-overlay
- * 8 - events
- * 9 - dragging/resizing events
- *
- */
+ // exports
+ t.element = element;
+ t.calendar = calendar;
+ t.name = viewName;
+ t.opt = opt;
+ t.trigger = trigger;
+ //t.setOverflowHidden = setOverflowHidden;
+ t.isEventDraggable = isEventDraggable;
+ t.isEventResizable = isEventResizable;
+ t.reportEvents = reportEvents;
+ t.eventEnd = eventEnd;
+ t.reportEventElement = reportEventElement;
+ t.reportEventClear = reportEventClear;
+ t.eventElementHandlers = eventElementHandlers;
+ t.showEvents = showEvents;
+ t.hideEvents = hideEvents;
+ t.eventDrop = eventDrop;
+ t.eventResize = eventResize;
+ // t.title
+ // t.start, t.end
+ // t.visStart, t.visEnd
+ // imports
+ var defaultEventEnd = t.defaultEventEnd;
+ var normalizeEvent = calendar.normalizeEvent; // in EventManager
+ var reportEventChange = calendar.reportEventChange;
- init: function(element, options) {
- this.element = element;
- this.options = options;
- this.eventsByID = {};
- this.eventElements = [];
- this.eventElementsByID = {};
- this.usedOverlays = [];
- this.unusedOverlays = [];
- },
+ // locals
+ var eventsByID = {};
+ var eventElements = [];
+ var eventElementsByID = {};
+ var options = calendar.options;
- // triggers an event handler, always append view as last arg
-
- trigger: function(name, thisObj) {
- if (this.options[name]) {
- return this.options[name].apply(thisObj || this, Array.prototype.slice.call(arguments, 2).concat([this]));
+ function opt(name, viewNameOverride) {
+ var v = options[name];
+ if (typeof v == 'object') {
+ return smartProperty(v, viewNameOverride || viewName);
}
- },
+ return v;
+ }
+ function trigger(name, thisObj) {
+ return calendar.trigger.apply(
+ calendar,
+ [name, thisObj || t].concat(Array.prototype.slice.call(arguments, 2), [t])
+ );
+ }
- // returns a Date object for an event's end
- eventEnd: function(event) {
- return event.end ? cloneDate(event.end) : this.defaultEventEnd(event); // TODO: make sure always using copies
- },
+ /*
+ function setOverflowHidden(bool) {
+ element.css('overflow', bool ? 'hidden' : '');
+ }
+ */
+ function isEventDraggable(event) {
+ return isEventEditable(event) && !opt('disableDragging');
+ }
+
+ function isEventResizable(event) { // but also need to make sure the seg.isEnd == true
+ return isEventEditable(event) && !opt('disableResizing');
+ }
+
+
+ function isEventEditable(event) {
+ return firstDefined(event.editable, (event.source || {}).editable, opt('editable'));
+ }
+
+
+ /* Event Data
+ ------------------------------------------------------------------------------*/
+
+
// report when view receives new events
-
- reportEvents: function(events) { // events are already normalized at this point
- var i, len = events.length, event,
- eventsByID = this.eventsByID = {};
+ function reportEvents(events) { // events are already normalized at this point
+ eventsByID = {};
+ var i, len = events.length, event;
for (i = 0; i < len; i++) {
event = events[i];
if (eventsByID[event._id]) {
@@ -2942,85 +4334,132 @@
eventsByID[event._id] = [event];
}
}
- },
+ }
+ // returns a Date object for an event's end
+ function eventEnd(event) {
+ return event.end ? cloneDate(event.end) : defaultEventEnd(event);
+ }
+
+ /* Event Elements
+ ------------------------------------------------------------------------------*/
+
+
// report when view creates an element for an event
-
- reportEventElement: function(event, element) {
- this.eventElements.push(element);
- var eventElementsByID = this.eventElementsByID;
+ function reportEventElement(event, element) {
+ eventElements.push(element);
if (eventElementsByID[event._id]) {
eventElementsByID[event._id].push(element);
} else {
eventElementsByID[event._id] = [element];
}
- },
+ }
+ function reportEventClear() {
+ eventElements = [];
+ eventElementsByID = {};
+ }
- // event element manipulation
- _clearEvents: function() { // only resets hashes
- this.eventElements = [];
- this.eventElementsByID = {};
- },
+ // attaches eventClick, eventMouseover, eventMouseout
+ function eventElementHandlers(event, eventElement) {
+ eventElement
+ .click(function(ev) {
+ if (!eventElement.hasClass('ui-draggable-dragging') &&
+ !eventElement.hasClass('ui-resizable-resizing')) {
+ return trigger('eventClick', this, event, ev);
+ }
+ })
+ .hover(
+ function(ev) {
+ trigger('eventMouseover', this, event, ev);
+ },
+ function(ev) {
+ trigger('eventMouseout', this, event, ev);
+ }
+ );
+ // TODO: don't fire eventMouseover/eventMouseout *while* dragging is occuring (on subject element)
+ // TODO: same for resizing
+ }
- showEvents: function(event, exceptElement) {
- this._eee(event, exceptElement, 'show');
- },
- hideEvents: function(event, exceptElement) {
- this._eee(event, exceptElement, 'hide');
- },
+ function showEvents(event, exceptElement) {
+ eachEventElement(event, exceptElement, 'show');
+ }
- _eee: function(event, exceptElement, funcName) { // event-element-each
- var elements = this.eventElementsByID[event._id],
+
+ function hideEvents(event, exceptElement) {
+ eachEventElement(event, exceptElement, 'hide');
+ }
+
+
+ function eachEventElement(event, exceptElement, funcName) {
+ var elements = eventElementsByID[event._id],
i, len = elements.length;
for (i = 0; i < len; i++) {
- if (elements[i][0] != exceptElement[0]) { // AHAHAHAHAHAHAHAH
+ if (!exceptElement || elements[i][0] != exceptElement[0]) {
elements[i][funcName]();
}
}
- },
+ }
+ /* Event Modification Reporting
+ ---------------------------------------------------------------------------------*/
- // event modification reporting
- eventDrop: function(e, event, dayDelta, minuteDelta, allDay, ev, ui) {
- var view = this,
- oldAllDay = event.allDay,
- eventId = event._id;
- view.moveEvents(view.eventsByID[eventId], dayDelta, minuteDelta, allDay);
- view.trigger('eventDrop', e, event, dayDelta, minuteDelta, allDay, function() { // TODO: change docs
- // TODO: investigate cases where this inverse technique might not work
- view.moveEvents(view.eventsByID[eventId], -dayDelta, -minuteDelta, oldAllDay);
- view.rerenderEvents();
- }, ev, ui);
- view.eventsChanged = true;
- view.rerenderEvents(eventId);
- },
+ function eventDrop(e, event, dayDelta, minuteDelta, allDay, ev, ui) {
+ var oldAllDay = event.allDay;
+ var eventId = event._id;
+ moveEvents(eventsByID[eventId], dayDelta, minuteDelta, allDay);
+ trigger(
+ 'eventDrop',
+ e,
+ event,
+ dayDelta,
+ minuteDelta,
+ allDay,
+ function() {
+ // TODO: investigate cases where this inverse technique might not work
+ moveEvents(eventsByID[eventId], -dayDelta, -minuteDelta, oldAllDay);
+ reportEventChange(eventId);
+ },
+ ev,
+ ui
+ );
+ reportEventChange(eventId);
+ }
- eventResize: function(e, event, dayDelta, minuteDelta, ev, ui) {
- var view = this,
- eventId = event._id;
- view.elongateEvents(view.eventsByID[eventId], dayDelta, minuteDelta);
- view.trigger('eventResize', e, event, dayDelta, minuteDelta, function() {
- // TODO: investigate cases where this inverse technique might not work
- view.elongateEvents(view.eventsByID[eventId], -dayDelta, -minuteDelta);
- view.rerenderEvents();
- }, ev, ui);
- view.eventsChanged = true;
- view.rerenderEvents(eventId);
- },
+ function eventResize(e, event, dayDelta, minuteDelta, ev, ui) {
+ var eventId = event._id;
+ elongateEvents(eventsByID[eventId], dayDelta, minuteDelta);
+ trigger(
+ 'eventResize',
+ e,
+ event,
+ dayDelta,
+ minuteDelta,
+ function() {
+ // TODO: investigate cases where this inverse technique might not work
+ elongateEvents(eventsByID[eventId], -dayDelta, -minuteDelta);
+ reportEventChange(eventId);
+ },
+ ev,
+ ui
+ );
+ reportEventChange(eventId);
+ }
- // event modification
+ /* Event Modification Math
+ ---------------------------------------------------------------------------------*/
- moveEvents: function(events, dayDelta, minuteDelta, allDay) {
+
+ function moveEvents(events, dayDelta, minuteDelta, allDay) {
minuteDelta = minuteDelta || 0;
for (var e, len = events.length, i = 0; i < len; i++) {
e = events[i];
@@ -3031,856 +4470,728 @@
if (e.end) {
e.end = addMinutes(addDays(e.end, dayDelta, true), minuteDelta);
}
- normalizeEvent(e, this.options);
+ normalizeEvent(e, options);
}
- },
+ }
- elongateEvents: function(events, dayDelta, minuteDelta) {
+
+ function elongateEvents(events, dayDelta, minuteDelta) {
minuteDelta = minuteDelta || 0;
for (var e, len = events.length, i = 0; i < len; i++) {
e = events[i];
- e.end = addMinutes(addDays(this.eventEnd(e), dayDelta, true), minuteDelta);
- normalizeEvent(e, this.options);
+ e.end = addMinutes(addDays(eventEnd(e), dayDelta, true), minuteDelta);
+ normalizeEvent(e, options);
}
- },
+ }
+ }
- // semi-transparent overlay (while dragging or selecting)
+ function DayEventRenderer() {
+ var t = this;
- renderOverlay: function(rect, parent) {
- var e = this.unusedOverlays.shift();
- if (!e) {
- e = $("<div class='fc-cell-overlay' style='position:absolute;z-index:3'/>");
- }
- if (e[0].parentNode != parent[0]) {
- e.appendTo(parent);
- }
- this.usedOverlays.push(e.css(rect).show());
- return e;
- },
- clearOverlays: function() {
- var e;
- while (e = this.usedOverlays.shift()) {
- this.unusedOverlays.push(e.hide().unbind());
- }
- },
+ // exports
+ t.renderDaySegs = renderDaySegs;
+ t.resizableDayEvent = resizableDayEvent;
+ // imports
+ var opt = t.opt;
+ var trigger = t.trigger;
+ var isEventDraggable = t.isEventDraggable;
+ var isEventResizable = t.isEventResizable;
+ var eventEnd = t.eventEnd;
+ var reportEventElement = t.reportEventElement;
+ var showEvents = t.showEvents;
+ var hideEvents = t.hideEvents;
+ var eventResize = t.eventResize;
+ var getRowCnt = t.getRowCnt;
+ var getColCnt = t.getColCnt;
+ var getColWidth = t.getColWidth;
+ var allDayRow = t.allDayRow;
+ var allDayBounds = t.allDayBounds;
+ var colContentLeft = t.colContentLeft;
+ var colContentRight = t.colContentRight;
+ var dayOfWeekCol = t.dayOfWeekCol;
+ var dateCell = t.dateCell;
+ var compileDaySegs = t.compileDaySegs;
+ var getDaySegmentContainer = t.getDaySegmentContainer;
+ var bindDaySeg = t.bindDaySeg; //TODO: streamline this
+ var formatDates = t.calendar.formatDates;
+ var renderDayOverlay = t.renderDayOverlay;
+ var clearOverlays = t.clearOverlays;
+ var clearSelection = t.clearSelection;
- // common horizontal event resizing
+ /* Rendering
+ -----------------------------------------------------------------------------*/
- resizableDayEvent: function(event, eventElement, colWidth) {
- var view = this;
- if (!view.options.disableResizing && eventElement.resizable) {
- eventElement.resizable({
- handles: view.options.isRTL ? {w:'div.ui-resizable-w'} : {e:'div.ui-resizable-e'},
- grid: colWidth,
- minWidth: colWidth / 2, // need this or else IE throws errors when too small
- containment: view.element.parent().parent(), // the main element...
- // ... a fix. wouldn't allow extending to last column in agenda views (jq ui bug?)
- start: function(ev, ui) {
- eventElement.css('z-index', 9);
- view.hideEvents(event, eventElement);
- view.trigger('eventResizeStart', this, event, ev, ui);
- },
- stop: function(ev, ui) {
- view.trigger('eventResizeStop', this, event, ev, ui);
- // ui.size.width wasn't working with grid correctly, use .width()
- var dayDelta = Math.round((eventElement.width() - ui.originalSize.width) / colWidth);
- if (dayDelta) {
- view.eventResize(this, event, dayDelta, 0, ev, ui);
- } else {
- eventElement.css('z-index', 8);
- view.showEvents(event, eventElement);
- }
+
+ function renderDaySegs(segs, modifiedEventId) {
+ var segmentContainer = getDaySegmentContainer();
+ var rowDivs;
+ var rowCnt = getRowCnt();
+ var colCnt = getColCnt();
+ var i = 0;
+ var rowI;
+ var levelI;
+ var colHeights;
+ var j;
+ var segCnt = segs.length;
+ var seg;
+ var top;
+ var k;
+ segmentContainer[0].innerHTML = daySegHTML(segs); // faster than .html()
+ daySegElementResolve(segs, segmentContainer.children());
+ daySegElementReport(segs);
+ daySegHandlers(segs, segmentContainer, modifiedEventId);
+ daySegCalcHSides(segs);
+ daySegSetWidths(segs);
+ daySegCalcHeights(segs);
+ rowDivs = getRowDivs();
+ // set row heights, calculate event tops (in relation to row top)
+ for (rowI = 0; rowI < rowCnt; rowI++) {
+ levelI = 0;
+ colHeights = [];
+ for (j = 0; j < colCnt; j++) {
+ colHeights[j] = 0;
+ }
+ while (i < segCnt && (seg = segs[i]).row == rowI) {
+ // loop through segs in a row
+ top = arrayMax(colHeights.slice(seg.startCol, seg.endCol));
+ seg.top = top;
+ top += seg.outerHeight;
+ for (k = seg.startCol; k < seg.endCol; k++) {
+ colHeights[k] = top;
}
- });
+ i++;
+ }
+ rowDivs[rowI].height(arrayMax(colHeights));
}
- },
+ daySegSetTops(segs, getRowTops(rowDivs));
+ }
+ function renderTempDaySegs(segs, adjustRow, adjustTop) {
+ var tempContainer = $("<div/>");
+ var elements;
+ var segmentContainer = getDaySegmentContainer();
+ var i;
+ var segCnt = segs.length;
+ var element;
+ tempContainer[0].innerHTML = daySegHTML(segs); // faster than .html()
+ elements = tempContainer.children();
+ segmentContainer.append(elements);
+ daySegElementResolve(segs, elements);
+ daySegCalcHSides(segs);
+ daySegSetWidths(segs);
+ daySegCalcHeights(segs);
+ daySegSetTops(segs, getRowTops(getRowDivs()));
+ elements = [];
+ for (i = 0; i < segCnt; i++) {
+ element = segs[i].element;
+ if (element) {
+ if (segs[i].row === adjustRow) {
+ element.css('top', adjustTop);
+ }
+ elements.push(element[0]);
+ }
+ }
+ return $(elements);
+ }
- // attaches eventClick, eventMouseover, eventMouseout
- eventElementHandlers: function(event, eventElement) {
- var view = this;
- eventElement
- .click(function(ev) {
- if (!eventElement.hasClass('ui-draggable-dragging') &&
- !eventElement.hasClass('ui-resizable-resizing')) {
- return view.trigger('eventClick', this, event, ev);
+ function daySegHTML(segs) { // also sets seg.left and seg.outerWidth
+ var rtl = opt('isRTL');
+ var i;
+ var segCnt = segs.length;
+ var seg;
+ var event;
+ var url;
+ var classes;
+ var bounds = allDayBounds();
+ var minLeft = bounds.left;
+ var maxLeft = bounds.right;
+ var leftCol;
+ var rightCol;
+ var left;
+ var right;
+ var skinCss;
+ var html = '';
+ // calculate desired position/dimensions, create html
+ for (i = 0; i < segCnt; i++) {
+ seg = segs[i];
+ event = seg.event;
+ classes = ['fc-event', 'fc-event-skin', 'fc-event-hori'];
+ if (isEventDraggable(event)) {
+ classes.push('fc-event-draggable');
}
- })
- .hover(
- function(ev) {
- view.trigger('eventMouseover', this, event, ev);
- },
- function(ev) {
- view.trigger('eventMouseout', this, event, ev);
+ if (rtl) {
+ if (seg.isStart) {
+ classes.push('fc-corner-right');
}
- );
- },
+ if (seg.isEnd) {
+ classes.push('fc-corner-left');
+ }
+ leftCol = dayOfWeekCol(seg.end.getDay() - 1);
+ rightCol = dayOfWeekCol(seg.start.getDay());
+ left = seg.isEnd ? colContentLeft(leftCol) : minLeft;
+ right = seg.isStart ? colContentRight(rightCol) : maxLeft;
+ } else {
+ if (seg.isStart) {
+ classes.push('fc-corner-left');
+ }
+ if (seg.isEnd) {
+ classes.push('fc-corner-right');
+ }
+ leftCol = dayOfWeekCol(seg.start.getDay());
+ rightCol = dayOfWeekCol(seg.end.getDay() - 1);
+ left = seg.isStart ? colContentLeft(leftCol) : minLeft;
+ right = seg.isEnd ? colContentRight(rightCol) : maxLeft;
+ }
+ classes = classes.concat(event.className);
+ if (event.source) {
+ classes = classes.concat(event.source.className || []);
+ }
+ url = event.url;
+ skinCss = getSkinCss(event, opt);
+ if (url) {
+ html += "<a href='" + htmlEscape(url) + "'";
+ } else {
+ html += "<div";
+ }
+ html +=
+ " class='" + classes.join(' ') + "'" +
+ " style='position:absolute;z-index:8;left:" + left + "px;" + skinCss + "'" +
+ ">" +
+ "<div" +
+ " class='fc-event-inner fc-event-skin'" +
+ (skinCss ? " style='" + skinCss + "'" : '') +
+ ">";
+ if (!event.allDay && seg.isStart) {
+ html +=
+ "<span class='fc-event-time'>" +
+ htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
+ "</span>";
+ }
+ html +=
+ "<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" +
+ "</div>";
+ if (seg.isEnd && isEventResizable(event)) {
+ html +=
+ "<div class='ui-resizable-handle ui-resizable-" + (rtl ? 'w' : 'e') + "'>" +
+ " " + // makes hit area a lot better for IE6/7
+ "</div>";
+ }
+ html +=
+ "</" + (url ? "a" : "div" ) + ">";
+ seg.left = left;
+ seg.outerWidth = right - left;
+ seg.startCol = leftCol;
+ seg.endCol = rightCol + 1; // needs to be exclusive
+ }
+ return html;
+ }
+ function daySegElementResolve(segs, elements) { // sets seg.element
+ var i;
+ var segCnt = segs.length;
+ var seg;
+ var event;
+ var element;
+ var triggerRes;
+ for (i = 0; i < segCnt; i++) {
+ seg = segs[i];
+ event = seg.event;
+ element = $(elements[i]); // faster than .eq()
+ triggerRes = trigger('eventRender', event, event, element);
+ if (triggerRes === false) {
+ element.remove();
+ } else {
+ if (triggerRes && triggerRes !== true) {
+ triggerRes = $(triggerRes)
+ .css({
+ position: 'absolute',
+ left: seg.left
+ });
+ element.replaceWith(triggerRes);
+ element = triggerRes;
+ }
+ seg.element = element;
+ }
+ }
+ }
- // get a property from the 'options' object, using smart view naming
- option: function(name, viewName) {
- var v = this.options[name];
- if (typeof v == 'object') {
- return smartProperty(v, viewName || this.name);
+ function daySegElementReport(segs) {
+ var i;
+ var segCnt = segs.length;
+ var seg;
+ var element;
+ for (i = 0; i < segCnt; i++) {
+ seg = segs[i];
+ element = seg.element;
+ if (element) {
+ reportEventElement(seg.event, element);
+ }
}
- return v;
- },
+ }
-
- // event rendering utilities
-
- sliceSegs: function(events, visEventEnds, start, end) {
- var segs = [],
- i, len = events.length, event,
- eventStart, eventEnd,
- segStart, segEnd,
- isStart, isEnd;
- for (i = 0; i < len; i++) {
- event = events[i];
- eventStart = event.start;
- eventEnd = visEventEnds[i];
- if (eventEnd > start && eventStart < end) {
- if (eventStart < start) {
- segStart = cloneDate(start);
- isStart = false;
+ function daySegHandlers(segs, segmentContainer, modifiedEventId) {
+ var i;
+ var segCnt = segs.length;
+ var seg;
+ var element;
+ var event;
+ // retrieve elements, run through eventRender callback, bind handlers
+ for (i = 0; i < segCnt; i++) {
+ seg = segs[i];
+ element = seg.element;
+ if (element) {
+ event = seg.event;
+ if (event._id === modifiedEventId) {
+ bindDaySeg(event, element, seg);
} else {
- segStart = eventStart;
- isStart = true;
+ element[0]._fci = i; // for lazySegBind
}
- if (eventEnd > end) {
- segEnd = cloneDate(end);
- isEnd = false;
- } else {
- segEnd = eventEnd;
- isEnd = true;
- }
- segs.push({
- event: event,
- start: segStart,
- end: segEnd,
- isStart: isStart,
- isEnd: isEnd,
- msLength: segEnd - segStart
- });
}
}
- return segs.sort(segCmp);
+ lazySegBind(segmentContainer, segs, bindDaySeg);
}
- };
+ function daySegCalcHSides(segs) { // also sets seg.key
+ var i;
+ var segCnt = segs.length;
+ var seg;
+ var element;
+ var key, val;
+ var hsideCache = {};
+ // record event horizontal sides
+ for (i = 0; i < segCnt; i++) {
+ seg = segs[i];
+ element = seg.element;
+ if (element) {
+ key = seg.key = cssKey(element[0]);
+ val = hsideCache[key];
+ if (val === undefined) {
+ val = hsideCache[key] = hsides(element, true);
+ }
+ seg.hsides = val;
+ }
+ }
+ }
- function lazySegBind(container, segs, bindHandlers) {
- container.unbind('mouseover').mouseover(function(ev) {
- var parent = ev.target, e,
- i, seg;
- while (parent != this) {
- e = parent;
- parent = parent.parentNode;
- }
- if ((i = e._fci) !== undefined) {
- e._fci = undefined;
+ function daySegSetWidths(segs) {
+ var i;
+ var segCnt = segs.length;
+ var seg;
+ var element;
+ for (i = 0; i < segCnt; i++) {
seg = segs[i];
- bindHandlers(seg.event, seg.element, seg);
- $(ev.target).trigger(ev);
+ element = seg.element;
+ if (element) {
+ element[0].style.width = Math.max(0, seg.outerWidth - seg.hsides) + 'px';
+ }
}
- ev.stopPropagation();
- });
- }
+ }
- // event rendering calculation utilities
-
- function stackSegs(segs) {
- var levels = [],
- i, len = segs.length, seg,
- j, collide, k;
- for (i = 0; i < len; i++) {
- seg = segs[i];
- j = 0; // the level index where seg should belong
- while (true) {
- collide = false;
- if (levels[j]) {
- for (k = 0; k < levels[j].length; k++) {
- if (segsCollide(levels[j][k], seg)) {
- collide = true;
- break;
- }
+ function daySegCalcHeights(segs) {
+ var i;
+ var segCnt = segs.length;
+ var seg;
+ var element;
+ var key, val;
+ var vmarginCache = {};
+ // record event heights
+ for (i = 0; i < segCnt; i++) {
+ seg = segs[i];
+ element = seg.element;
+ if (element) {
+ key = seg.key; // created in daySegCalcHSides
+ val = vmarginCache[key];
+ if (val === undefined) {
+ val = vmarginCache[key] = vmargins(element);
}
+ seg.outerHeight = element[0].offsetHeight + val;
}
- if (collide) {
- j++;
- } else {
- break;
- }
}
- if (levels[j]) {
- levels[j].push(seg);
- } else {
- levels[j] = [seg];
+ }
+
+
+ function getRowDivs() {
+ var i;
+ var rowCnt = getRowCnt();
+ var rowDivs = [];
+ for (i = 0; i < rowCnt; i++) {
+ rowDivs[i] = allDayRow(i)
+ .find('td:first div.fc-day-content > div'); // optimal selector?
}
+ return rowDivs;
}
- return levels;
- }
- function segCmp(a, b) {
- return (b.msLength - a.msLength) * 100 + (a.event.start - b.event.start);
- }
- function segsCollide(seg1, seg2) {
- return seg1.end > seg2.start && seg1.start < seg2.end;
- }
+ function getRowTops(rowDivs) {
+ var i;
+ var rowCnt = rowDivs.length;
+ var tops = [];
+ for (i = 0; i < rowCnt; i++) {
+ tops[i] = rowDivs[i][0].offsetTop; // !!?? but this means the element needs position:relative if in a table cell!!!!
+ }
+ return tops;
+ }
- function SelectionManager(view, initFunc, displayFunc, clearFunc) {
+ function daySegSetTops(segs, rowTops) { // also triggers eventAfterRender
+ var i;
+ var segCnt = segs.length;
+ var seg;
+ var element;
+ var event;
+ for (i = 0; i < segCnt; i++) {
+ seg = segs[i];
+ element = seg.element;
+ if (element) {
+ element[0].style.top = rowTops[seg.row] + (seg.top || 0) + 'px';
+ event = seg.event;
+ trigger('eventAfterRender', event, event, element);
+ }
+ }
+ }
- var t = this;
- var selected = false;
- var initialElement;
- var initialRange;
- var start;
- var end;
- var allDay;
+ /* Resizing
+ -----------------------------------------------------------------------------------*/
- t.dragStart = function(ev) {
- initFunc();
- start = end = undefined;
- initialRange = undefined;
- initialElement = ev.currentTarget;
- };
+ function resizableDayEvent(event, element, seg) {
+ var rtl = opt('isRTL');
+ var direction = rtl ? 'w' : 'e';
+ var handle = element.find('div.ui-resizable-' + direction);
+ var isResizing = false;
- t.drag = function(currentStart, currentEnd, currentAllDay) {
- if (currentStart) {
- var range = [currentStart, currentEnd];
- if (!initialRange) {
- initialRange = range;
+ // TODO: look into using jquery-ui mouse widget for this stuff
+ disableTextSelection(element); // prevent native <a> selection for IE
+ element
+ .mousedown(function(ev) { // prevent native <a> selection for others
+ ev.preventDefault();
+ })
+ .click(function(ev) {
+ if (isResizing) {
+ ev.preventDefault(); // prevent link from being visited (only method that worked in IE6)
+ ev.stopImmediatePropagation(); // prevent fullcalendar eventClick handler from being called
+ // (eventElementHandlers needs to be bound after resizableDayEvent)
+ }
+ });
+
+ handle.mousedown(function(ev) {
+ if (ev.which != 1) {
+ return; // needs to be left mouse button
}
- var dates = initialRange.concat(range).sort(cmp);
- start = dates[0];
- end = dates[3];
- allDay = currentAllDay;
- clearFunc();
- displayFunc(cloneDate(start), cloneDate(end), allDay);
- } else {
- // called with no arguments
- start = end = undefined;
- clearFunc();
- }
- };
+ isResizing = true;
+ var hoverListener = t.getHoverListener();
+ var rowCnt = getRowCnt();
+ var colCnt = getColCnt();
+ var dis = rtl ? -1 : 1;
+ var dit = rtl ? colCnt - 1 : 0;
+ var elementTop = element.css('top');
+ var dayDelta;
+ var helpers;
+ var eventCopy = $.extend({}, event);
+ var minCell = dateCell(event.start);
+ clearSelection();
+ $('body')
+ .css('cursor', direction + '-resize')
+ .one('mouseup', mouseup);
+ trigger('eventResizeStart', this, event, ev);
+ hoverListener.start(function(cell, origCell) {
+ if (cell) {
+ var r = Math.max(minCell.row, cell.row);
+ var c = cell.col;
+ if (rowCnt == 1) {
+ r = 0; // hack for all-day area in agenda views
+ }
+ if (r == minCell.row) {
+ if (rtl) {
+ c = Math.min(minCell.col, c);
+ } else {
+ c = Math.max(minCell.col, c);
+ }
+ }
+ dayDelta = (r * 7 + c * dis + dit) - (origCell.row * 7 + origCell.col * dis + dit);
+ var newEnd = addDays(eventEnd(event), dayDelta, true);
+ if (dayDelta) {
+ eventCopy.end = newEnd;
+ var oldHelpers = helpers;
+ helpers = renderTempDaySegs(compileDaySegs([eventCopy]), seg.row, elementTop);
+ helpers.find('*').css('cursor', direction + '-resize');
+ if (oldHelpers) {
+ oldHelpers.remove();
+ }
+ hideEvents(event);
+ } else {
+ if (helpers) {
+ showEvents(event);
+ helpers.remove();
+ helpers = null;
+ }
+ }
+ clearOverlays();
+ renderDayOverlay(event.start, addDays(cloneDate(newEnd), 1)); // coordinate grid already rebuild at hoverListener.start
+ }
+ }, ev);
+ function mouseup(ev) {
+ trigger('eventResizeStop', this, event, ev);
+ $('body').css('cursor', '');
+ hoverListener.stop();
+ clearOverlays();
+ if (dayDelta) {
+ eventResize(this, event, dayDelta, 0, ev);
+ // event redraw will clear helpers
+ }
+ // otherwise, the drag handler already restored the old events
- t.dragStop = function(ev) {
- if (start) {
- if (+initialRange[0] == +start && +initialRange[1] == +end) {
- view.trigger('dayClick', initialElement, start, allDay, ev);
+ setTimeout(function() { // make this happen after the element's click event
+ isResizing = false;
+ }, 0);
}
- _select();
- }
- };
+ });
+ }
- t.select = function(newStart, newEnd, newAllDay) {
- initFunc();
- start = newStart;
- end = newEnd;
- allDay = newAllDay;
- displayFunc(cloneDate(start), cloneDate(end), allDay);
- _select();
- };
+ }
- function _select() { // just set the selected flag, and trigger
- selected = true;
- view.trigger('select', view, start, end, allDay);
- }
+//BUG: unselect needs to be triggered when events are dragged+dropped
+ function SelectionManager() {
+ var t = this;
- function unselect() {
- if (selected) {
- selected = false;
- start = end = undefined;
- clearFunc();
- view.trigger('unselect', view);
- }
- }
+ // exports
+ t.select = select;
t.unselect = unselect;
+ t.reportSelection = reportSelection;
+ t.daySelectionMousedown = daySelectionMousedown;
- }
+ // imports
+ var opt = t.opt;
+ var trigger = t.trigger;
+ var defaultSelectionEnd = t.defaultSelectionEnd;
+ var renderSelection = t.renderSelection;
+ var clearSelection = t.clearSelection;
- function documentDragHelp(mousemove, mouseup) {
- function _mouseup(ev) {
- mouseup(ev);
- $(document)
- .unbind('mousemove', mousemove)
- .unbind('mouseup', _mouseup);
- }
- $(document)
- .mousemove(mousemove)
- .mouseup(_mouseup);
- }
+ // locals
+ var selected = false;
- function documentUnselectAuto(view, unselectFunc) {
- if (view.option('selectable') && view.option('unselectAuto')) {
+ // unselectAuto
+ if (opt('selectable') && opt('unselectAuto')) {
$(document).mousedown(function(ev) {
- var ignore = view.option('unselectCancel');
+ var ignore = opt('unselectCancel');
if (ignore) {
if ($(ev.target).parents(ignore).length) { // could be optimized to stop after first match
return;
}
}
- unselectFunc();
+ unselect(ev);
});
}
- }
- /* Date Math
- -----------------------------------------------------------------------------*/
-
- var DAY_MS = 86400000,
- HOUR_MS = 3600000,
- MINUTE_MS = 60000;
-
- function addYears(d, n, keepTime) {
- d.setFullYear(d.getFullYear() + n);
- if (!keepTime) {
- clearTime(d);
- }
- return d;
- }
-
- function addMonths(d, n, keepTime) { // prevents day overflow/underflow
- if (+d) { // prevent infinite looping on invalid dates
- var m = d.getMonth() + n,
- check = cloneDate(d);
- check.setDate(1);
- check.setMonth(m);
- d.setMonth(m);
- if (!keepTime) {
- clearTime(d);
+ function select(startDate, endDate, allDay) {
+ unselect();
+ if (!endDate) {
+ endDate = defaultSelectionEnd(startDate, allDay);
}
- while (d.getMonth() != check.getMonth()) {
- d.setDate(d.getDate() + (d < check ? 1 : -1));
- }
+ renderSelection(startDate, endDate, allDay);
+ reportSelection(startDate, endDate, allDay);
}
- return d;
- }
- function addDays(d, n, keepTime) { // deals with daylight savings
- if (+d) {
- var dd = d.getDate() + n,
- check = cloneDate(d);
- check.setHours(9); // set to middle of day
- check.setDate(dd);
- d.setDate(dd);
- if (!keepTime) {
- clearTime(d);
- }
- fixDate(d, check);
- }
- return d;
- }
- fc.addDays = addDays;
-
- function fixDate(d, check) { // force d to be on check's YMD, for daylight savings purposes
- if (+d) { // prevent infinite looping on invalid dates
- while (d.getDate() != check.getDate()) {
- d.setTime(+d + (d < check ? 1 : -1) * HOUR_MS);
+ function unselect(ev) {
+ if (selected) {
+ selected = false;
+ clearSelection();
+ trigger('unselect', null, ev);
}
}
- }
- function addMinutes(d, n) {
- d.setMinutes(d.getMinutes() + n);
- return d;
- }
- function clearTime(d) {
- d.setHours(0);
- d.setMinutes(0);
- d.setSeconds(0);
- d.setMilliseconds(0);
- return d;
- }
-
- function cloneDate(d, dontKeepTime) {
- if (dontKeepTime) {
- return clearTime(new Date(+d));
+ function reportSelection(startDate, endDate, allDay, ev) {
+ selected = true;
+ trigger('select', null, startDate, endDate, allDay, ev);
}
- return new Date(+d);
- }
- fc.cloneDate = cloneDate;
- function zeroDate() { // returns a Date with time 00:00:00 and dateOfMonth=1
- var i = 0, d;
- do {
- d = new Date(1970, i++, 1);
- } while (d.getHours()); // != 0
- return d;
- }
-
- function skipWeekend(date, inc, excl) {
- inc = inc || 1;
- while (!date.getDay() || (excl && date.getDay() == 1 || !excl && date.getDay() == 6)) {
- addDays(date, inc);
- }
- return date;
- }
-
- function dayDiff(d1, d2) { // d1 - d2
- return Math.round((cloneDate(d1, true) - cloneDate(d2, true)) / DAY_MS);
- }
-
-
- /* Date Parsing
- -----------------------------------------------------------------------------*/
-
- var parseDate = fc.parseDate = function(s) {
- if (typeof s == 'object') { // already a Date object
- return s;
- }
- if (typeof s == 'number') { // a UNIX timestamp
- return new Date(s * 1000);
- }
- if (typeof s == 'string') {
- if (s.match(/^\d+$/)) { // a UNIX timestamp
- return new Date(parseInt(s) * 1000);
- }
- return parseISO8601(s, true) || (s ? new Date(s) : null);
- }
- // TODO: never return invalid dates (like from new Date(<string>)), return null instead
- return null;
- };
-
- var parseISO8601 = fc.parseISO8601 = function(s, ignoreTimezone) {
- // derived from http://delete.me.uk/2005/03/iso8601.html
- // TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html
- var m = s.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?$/);
- if (!m) {
- return null;
- }
- var date = new Date(m[1], 0, 1),
- check = new Date(m[1], 0, 1, 9, 0),
- offset = 0;
- if (m[3]) {
- date.setMonth(m[3] - 1);
- check.setMonth(m[3] - 1);
- }
- if (m[5]) {
- date.setDate(m[5]);
- check.setDate(m[5]);
- }
- fixDate(date, check);
- if (m[7]) {
- date.setHours(m[7]);
- }
- if (m[8]) {
- date.setMinutes(m[8]);
- }
- if (m[10]) {
- date.setSeconds(m[10]);
- }
- if (m[12]) {
- date.setMilliseconds(Number("0." + m[12]) * 1000);
- }
- fixDate(date, check);
- if (!ignoreTimezone) {
- if (m[14]) {
- offset = Number(m[16]) * 60 + Number(m[17]);
- offset *= m[15] == '-' ? 1 : -1;
- }
- offset -= date.getTimezoneOffset();
- }
- return new Date(+date + (offset * 60 * 1000));
- };
-
- var parseTime = fc.parseTime = function(s) { // returns minutes since start of day
- if (typeof s == 'number') { // an hour
- return s * 60;
- }
- if (typeof s == 'object') { // a Date object
- return s.getHours() * 60 + s.getMinutes();
- }
- var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/);
- if (m) {
- var h = parseInt(m[1]);
- if (m[3]) {
- h %= 12;
- if (m[3].toLowerCase().charAt(0) == 'p') {
- h += 12;
- }
- }
- return h * 60 + (m[2] ? parseInt(m[2]) : 0);
- }
- };
-
-
- /* Date Formatting
- -----------------------------------------------------------------------------*/
-
- var formatDate = fc.formatDate = function(date, format, options) {
- return formatDates(date, null, format, options);
- };
-
- var formatDates = fc.formatDates = function(date1, date2, format, options) {
- options = options || defaults;
- var date = date1,
- otherDate = date2,
- i, len = format.length, c,
- i2, formatter,
- res = '';
- for (i = 0; i < len; i++) {
- c = format.charAt(i);
- if (c == "'") {
- for (i2 = i + 1; i2 < len; i2++) {
- if (format.charAt(i2) == "'") {
- if (date) {
- if (i2 == i + 1) {
- res += "'";
- } else {
- res += format.substring(i + 1, i2);
- }
- i = i2;
- }
- break;
+ function daySelectionMousedown(ev) { // not really a generic manager method, oh well
+ var cellDate = t.cellDate;
+ var cellIsAllDay = t.cellIsAllDay;
+ var hoverListener = t.getHoverListener();
+ var reportDayClick = t.reportDayClick; // this is hacky and sort of weird
+ if (ev.which == 1 && opt('selectable')) { // which==1 means left mouse button
+ unselect(ev);
+ var _mousedownElement = this;
+ var dates;
+ hoverListener.start(function(cell, origCell) { // TODO: maybe put cellDate/cellIsAllDay info in cell
+ clearSelection();
+ if (cell && cellIsAllDay(cell)) {
+ dates = [ cellDate(origCell), cellDate(cell) ].sort(cmp);
+ renderSelection(dates[0], dates[1], true);
+ } else {
+ dates = null;
}
- }
- }
- else if (c == '(') {
- for (i2 = i + 1; i2 < len; i2++) {
- if (format.charAt(i2) == ')') {
- var subres = formatDate(date, format.substring(i + 1, i2), options);
- if (parseInt(subres.replace(/\D/, ''))) {
- res += subres;
+ }, ev);
+ $(document).one('mouseup', function(ev) {
+ hoverListener.stop();
+ if (dates) {
+ if (+dates[0] == +dates[1]) {
+ reportDayClick(dates[0], true, ev);
}
- i = i2;
- break;
+ reportSelection(dates[0], dates[1], true, ev);
}
- }
+ });
}
- else if (c == '[') {
- for (i2 = i + 1; i2 < len; i2++) {
- if (format.charAt(i2) == ']') {
- var subformat = format.substring(i + 1, i2);
- var subres = formatDate(date, subformat, options);
- if (subres != formatDate(otherDate, subformat, options)) {
- res += subres;
- }
- i = i2;
- break;
- }
- }
- }
- else if (c == '{') {
- date = date2;
- otherDate = date1;
- }
- else if (c == '}') {
- date = date1;
- otherDate = date2;
- }
- else {
- for (i2 = len; i2 > i; i2--) {
- if (formatter = dateFormatters[format.substring(i, i2)]) {
- if (date) {
- res += formatter(date, options);
- }
- i = i2 - 1;
- break;
- }
- }
- if (i2 == i) {
- if (date) {
- res += c;
- }
- }
- }
}
- return res;
- };
- var dateFormatters = {
- s : function(d) {
- return d.getSeconds()
- },
- ss : function(d) {
- return zeroPad(d.getSeconds())
- },
- m : function(d) {
- return d.getMinutes()
- },
- mm : function(d) {
- return zeroPad(d.getMinutes())
- },
- h : function(d) {
- return d.getHours() % 12 || 12
- },
- hh : function(d) {
- return zeroPad(d.getHours() % 12 || 12)
- },
- H : function(d) {
- return d.getHours()
- },
- HH : function(d) {
- return zeroPad(d.getHours())
- },
- d : function(d) {
- return d.getDate()
- },
- dd : function(d) {
- return zeroPad(d.getDate())
- },
- ddd : function(d, o) {
- return o.dayNamesShort[d.getDay()]
- },
- dddd: function(d, o) {
- return o.dayNames[d.getDay()]
- },
- M : function(d) {
- return d.getMonth() + 1
- },
- MM : function(d) {
- return zeroPad(d.getMonth() + 1)
- },
- MMM : function(d, o) {
- return o.monthNamesShort[d.getMonth()]
- },
- MMMM: function(d, o) {
- return o.monthNames[d.getMonth()]
- },
- yy : function(d) {
- return (d.getFullYear() + '').substring(2)
- },
- yyyy: function(d) {
- return d.getFullYear()
- },
- t : function(d) {
- return d.getHours() < 12 ? 'a' : 'p'
- },
- tt : function(d) {
- return d.getHours() < 12 ? 'am' : 'pm'
- },
- T : function(d) {
- return d.getHours() < 12 ? 'A' : 'P'
- },
- TT : function(d) {
- return d.getHours() < 12 ? 'AM' : 'PM'
- },
- u : function(d) {
- return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'")
- },
- S : function(d) {
- var date = d.getDate();
- if (date > 10 && date < 20) {
- return 'th';
- }
- return ['st', 'nd', 'rd'][date % 10 - 1] || 'th';
- }
- };
-
- /* Element Dimensions
- -----------------------------------------------------------------------------*/
-
- function setOuterWidth(element, width, includeMargins) {
- element.each(function(i, _element) {
- _element.style.width = width - hsides(_element, includeMargins) + 'px';
- });
}
- function setOuterHeight(element, height, includeMargins) {
- element.each(function(i, _element) {
- _element.style.height = height - vsides(_element, includeMargins) + 'px';
- });
- }
+ function OverlayManager() {
+ var t = this;
- function hsides(_element, includeMargins) {
- return (parseFloat(jQuery.curCSS(_element, 'paddingLeft', true)) || 0) +
- (parseFloat(jQuery.curCSS(_element, 'paddingRight', true)) || 0) +
- (parseFloat(jQuery.curCSS(_element, 'borderLeftWidth', true)) || 0) +
- (parseFloat(jQuery.curCSS(_element, 'borderRightWidth', true)) || 0) +
- (includeMargins ? hmargins(_element) : 0);
- }
+ // exports
+ t.renderOverlay = renderOverlay;
+ t.clearOverlays = clearOverlays;
- function hmargins(_element) {
- return (parseFloat(jQuery.curCSS(_element, 'marginLeft', true)) || 0) +
- (parseFloat(jQuery.curCSS(_element, 'marginRight', true)) || 0);
- }
- function vsides(_element, includeMargins) {
- return (parseFloat(jQuery.curCSS(_element, 'paddingTop', true)) || 0) +
- (parseFloat(jQuery.curCSS(_element, 'paddingBottom', true)) || 0) +
- (parseFloat(jQuery.curCSS(_element, 'borderTopWidth', true)) || 0) +
- (parseFloat(jQuery.curCSS(_element, 'borderBottomWidth', true)) || 0) +
- (includeMargins ? vmargins(_element) : 0);
- }
+ // locals
+ var usedOverlays = [];
+ var unusedOverlays = [];
- function vmargins(_element) {
- return (parseFloat(jQuery.curCSS(_element, 'marginTop', true)) || 0) +
- (parseFloat(jQuery.curCSS(_element, 'marginBottom', true)) || 0);
- }
+ function renderOverlay(rect, parent) {
+ var e = unusedOverlays.shift();
+ if (!e) {
+ e = $("<div class='fc-cell-overlay' style='position:absolute;z-index:3'/>");
+ }
+ if (e[0].parentNode != parent[0]) {
+ e.appendTo(parent);
+ }
+ usedOverlays.push(e.css(rect).show());
+ return e;
+ }
- function setMinHeight(element, h) {
- h = typeof h == 'number' ? h + 'px' : h;
- element[0].style.cssText += ';min-height:' + h + ';_height:' + h;
- }
+ function clearOverlays() {
+ var e;
+ while (e = usedOverlays.shift()) {
+ unusedOverlays.push(e.hide().unbind());
+ }
+ }
- /* Position Calculation
- -----------------------------------------------------------------------------*/
- // nasty bugs in opera 9.25
- // position()'s top returning incorrectly with TR/TD or elements within TD
- var topBug;
-
- function topCorrect(tr) { // tr/th/td or anything else
- if (topBug !== false) {
- var cell;
- if (tr.is('th,td')) {
- tr = (cell = tr).parent();
- }
- if (topBug === undefined && tr.is('tr')) {
- topBug = tr.position().top != tr.children().position().top;
- }
- if (topBug) {
- return tr.parent().position().top + (cell ? tr.position().top - cell.position().top : 0);
- }
- }
- return 0;
}
+ function CoordinateGrid(buildFunc) {
- /* Hover Matrix
- -----------------------------------------------------------------------------*/
+ var t = this;
+ var rows;
+ var cols;
- function HoverMatrix(rowElements, colElements, changeCallback) {
- var t = this,
- tops = [], lefts = [],
- origRow, origCol,
- currRow, currCol,
- e;
+ t.build = function() {
+ rows = [];
+ cols = [];
+ buildFunc(rows, cols);
+ };
- $.each(rowElements, function(i, _e) {
- e = $(_e);
- tops.push(e.offset().top + topCorrect(e));
- });
- tops.push(tops[tops.length - 1] + e.outerHeight());
- $.each(colElements, function(i, _e) {
- e = $(_e);
- lefts.push(e.offset().left);
- });
- lefts.push(lefts[lefts.length - 1] + e.outerWidth());
-
- t.mouse = function(ev) {
- var x = ev.pageX;
- var y = ev.pageY;
- var r, c;
- for (r = 0; r < tops.length && y >= tops[r]; r++) {
+ t.cell = function(x, y) {
+ var rowCnt = rows.length;
+ var colCnt = cols.length;
+ var i, r = -1, c = -1;
+ for (i = 0; i < rowCnt; i++) {
+ if (y >= rows[i][0] && y < rows[i][1]) {
+ r = i;
+ break;
+ }
}
- for (c = 0; c < lefts.length && x >= lefts[c]; c++) {
- }
- r = r >= tops.length ? -1 : r - 1;
- c = c >= lefts.length ? -1 : c - 1;
- if (r != currRow || c != currCol) {
- currRow = r;
- currCol = c;
- if (r == -1 || c == -1) {
- t.cell = null;
- } else {
- if (origRow === undefined) {
- origRow = r;
- origCol = c;
- }
- t.cell = {
- row: r,
- col: c,
- top: tops[r],
- left: lefts[c],
- width: lefts[c + 1] - lefts[c],
- height: tops[r + 1] - tops[r],
- origRow: origRow,
- origCol: origCol,
- isOrig: r == origRow && c == origCol,
- rowDelta: r - origRow,
- colDelta: c - origCol
- };
+ for (i = 0; i < colCnt; i++) {
+ if (x >= cols[i][0] && x < cols[i][1]) {
+ c = i;
+ break;
}
- changeCallback(t.cell);
}
+ return (r >= 0 && c >= 0) ? { row:r, col:c } : null;
};
- t.rect = function(row0, col0, row1, col1, originElement) { // row1,col1 are exclusive
+
+ t.rect = function(row0, col0, row1, col1, originElement) { // row1,col1 is inclusive
var origin = originElement.offset();
return {
- top: tops[row0] - origin.top,
- left: lefts[col0] - origin.left,
- width: lefts[col1] - lefts[col0],
- height: tops[row1] - tops[row0]
+ top: rows[row0][0] - origin.top,
+ left: cols[col0][0] - origin.left,
+ width: cols[col1][1] - cols[col0][0],
+ height: rows[row1][1] - rows[row0][0]
};
};
}
+ function HoverListener(coordinateGrid) {
- /* Misc Utils
- -----------------------------------------------------------------------------*/
- var undefined,
- dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
+ var t = this;
+ var bindType;
+ var change;
+ var firstCell;
+ var cell;
- function zeroPad(n) {
- return (n < 10 ? '0' : '') + n;
- }
- function smartProperty(obj, name) { // get a camel-cased/namespaced property of an object
- if (obj[name] !== undefined) {
- return obj[name];
- }
- var parts = name.split(/(?=[A-Z])/),
- i = parts.length - 1, res;
- for (; i >= 0; i--) {
- res = obj[parts[i].toLowerCase()];
- if (res !== undefined) {
- return res;
+ t.start = function(_change, ev, _bindType) {
+ change = _change;
+ firstCell = cell = null;
+ coordinateGrid.build();
+ mouse(ev);
+ bindType = _bindType || 'mousemove';
+ $(document).bind(bindType, mouse);
+ };
+
+
+ function mouse(ev) {
+ var newCell = coordinateGrid.cell(ev.pageX, ev.pageY);
+ if (!newCell != !cell || newCell && (newCell.row != cell.row || newCell.col != cell.col)) {
+ if (newCell) {
+ if (!firstCell) {
+ firstCell = newCell;
+ }
+ change(newCell, firstCell, newCell.row - firstCell.row, newCell.col - firstCell.col);
+ } else {
+ change(newCell, firstCell);
+ }
+ cell = newCell;
}
}
- return obj[''];
- }
- function htmlEscape(s) {
- return s
- .replace(/&/g, '&')
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/'/g, ''')
- .replace(/"/g, '"');
+
+ t.stop = function() {
+ $(document).unbind(bindType, mouse);
+ return cell;
+ };
+
+
}
-
function HorizontalPositionCache(getElement) {
var t = this,
@@ -3908,48 +5219,4 @@
}
-
- function cssKey(_element) {
- return _element.id + '/' + _element.className + '/' + _element.style.cssText.replace(/(^|;)\s*(top|left|width|height)\s*:[^;]*/ig, '');
- }
-
-
- function cmp(a, b) {
- return a - b;
- }
-
-
- function exclEndDay(event) {
- if (event.end) {
- return _exclEndDay(event.end, event.allDay);
- } else {
- return addDays(cloneDate(event.start), 1);
- }
- }
-
- function _exclEndDay(end, allDay) {
- end = cloneDate(end);
- return allDay || end.getHours() || end.getMinutes() ? addDays(end, 1) : end;
- }
-
-
- function disableTextSelection(element) {
- element
- .attr('unselectable', 'on')
- .css('MozUserSelect', 'none')
- .bind('selectstart.ui', function() {
- return false;
- });
- }
-
- /*
- function enableTextSelection(element) {
- element
- .attr('unselectable', 'off')
- .css('MozUserSelect', '')
- .unbind('selectstart.ui');
- }
- */
-
-
})(jQuery);
\ No newline at end of file
Modified: sandbox/trunk/ui/schedule/ui/src/main/resources/META-INF/resources/gcal.js
===================================================================
--- sandbox/trunk/ui/schedule/ui/src/main/resources/META-INF/resources/gcal.js 2011-11-22 06:11:32 UTC (rev 22966)
+++ sandbox/trunk/ui/schedule/ui/src/main/resources/META-INF/resources/gcal.js 2011-11-22 06:22:20 UTC (rev 22967)
@@ -1,52 +1,82 @@
/*
- * FullCalendar v1.4.5 Google Calendar Extension
+ * FullCalendar v1.5.2 Google Calendar Plugin
*
- * Copyright (c) 2009 Adam Shaw
- * Dual licensed under the MIT and GPL licenses:
- * http://www.opensource.org/licenses/mit-license.php
- * http://www.gnu.org/licenses/gpl.html
+ * Copyright (c) 2011 Adam Shaw
+ * Dual licensed under the MIT and GPL licenses, located in
+ * MIT-LICENSE.txt and GPL-LICENSE.txt respectively.
*
- * Date: Sun Feb 21 20:30:11 2010 -0800
+ * Date: Sun Aug 21 22:06:09 2011 -0700
*
*/
(function($) {
- $.fullCalendar.gcalFeed = function(feedUrl, options) {
- feedUrl = feedUrl.replace(/\/basic$/, '/full');
- options = options || {};
+ var fc = $.fullCalendar;
+ var formatDate = fc.formatDate;
+ var parseISO8601 = fc.parseISO8601;
+ var addDays = fc.addDays;
+ var applyAll = fc.applyAll;
- return function(start, end, callback) {
- var params = {
- 'start-min': $.fullCalendar.formatDate(start, 'u'),
- 'start-max': $.fullCalendar.formatDate(end, 'u'),
- 'singleevents': true,
- 'max-results': 9999
- };
- var ctz = options.currentTimezone;
- if (ctz) {
- params.ctz = ctz = ctz.replace(' ', '_');
+
+ fc.sourceNormalizers.push(function(sourceOptions) {
+ if (sourceOptions.dataType == 'gcal' || sourceOptions.dataType === undefined && (sourceOptions.url || '')
+ .match(/^(http|https):\/\/www.google.com\/calendar\/feeds\//)) {
+ sourceOptions.dataType = 'gcal';
+ if (sourceOptions.editable === undefined) {
+ sourceOptions.editable = false;
}
- $.getJSON(feedUrl + "?alt=json-in-script&callback=?", params, function(data) {
+ }
+ });
+
+
+ fc.sourceFetchers.push(function(sourceOptions, start, end) {
+ if (sourceOptions.dataType == 'gcal') {
+ return transformOptions(sourceOptions, start, end);
+ }
+ });
+
+
+ function transformOptions(sourceOptions, start, end) {
+
+ var success = sourceOptions.success;
+ var data = $.extend({}, sourceOptions.data || {}, {
+ 'start-min': formatDate(start, 'u'),
+ 'start-max': formatDate(end, 'u'),
+ 'singleevents': true,
+ 'max-results': 9999
+ });
+
+ var ctz = sourceOptions.currentTimezone;
+ if (ctz) {
+ data.ctz = ctz = ctz.replace(' ', '_');
+ }
+
+ return $.extend({}, sourceOptions, {
+ url: sourceOptions.url.replace(/\/basic$/, '/full') + '?alt=json-in-script&callback=?',
+ dataType: 'jsonp',
+ data: data,
+ startParam: false,
+ endParam: false,
+ success: function(data) {
var events = [];
if (data.feed.entry) {
$.each(data.feed.entry, function(i, entry) {
- var startStr = entry['gd$when'][0]['startTime'],
- start = $.fullCalendar.parseISO8601(startStr, true),
- end = $.fullCalendar.parseISO8601(entry['gd$when'][0]['endTime'], true),
- allDay = startStr.indexOf('T') == -1,
- url;
- $.each(entry.link, function() {
- if (this.type == 'text/html') {
- url = this.href;
+ var startStr = entry['gd$when'][0]['startTime'];
+ var start = parseISO8601(startStr, true);
+ var end = parseISO8601(entry['gd$when'][0]['endTime'], true);
+ var allDay = startStr.indexOf('T') == -1;
+ var url;
+ $.each(entry.link, function(i, link) {
+ if (link.type == 'text/html') {
+ url = link.href;
if (ctz) {
url += (url.indexOf('?') == -1 ? '?' : '&') + 'ctz=' + ctz;
}
}
});
if (allDay) {
- $.fullCalendar.addDays(end, -1); // make inclusive
+ addDays(end, -1); // make inclusive
}
events.push({
id: entry['gCal$uid']['value'],
@@ -56,16 +86,26 @@
end: end,
allDay: allDay,
location: entry['gd$where'][0]['valueString'],
- description: entry['content']['$t'],
- className: options.className,
- editable: options.editable || false
+ description: entry['content']['$t']
});
});
}
- callback(events);
- });
- }
+ var args = [events].concat(Array.prototype.slice.call(arguments, 1));
+ var res = applyAll(success, this, args);
+ if ($.isArray(res)) {
+ return res;
+ }
+ return events;
+ }
+ });
}
+
+// legacy
+ fc.gcalFeed = function(url, sourceOptions) {
+ return $.extend({}, sourceOptions, { url: url, dataType: 'gcal' });
+ };
+
+
})(jQuery);
13 years, 1 month