diff --git a/doc/developer/api_zot.md b/doc/developer/api_zot.bb
similarity index 80%
rename from doc/developer/api_zot.md
rename to doc/developer/api_zot.bb
index d75012818..f33faed17 100644
--- a/doc/developer/api_zot.md
+++ b/doc/developer/api_zot.bb
@@ -1,66 +1,24 @@
-### What is Zot?
+[h3]Zot API[/h3]
-Zot is the revolutionary protocol that powers $Projectname, providing **communications**, **identity management**, and **access control** across a fully **decentralised** network of independent websites, often called "the grid". The resulting platform is a robust system that supports privacy and security while enabling the kind of rich web services typically seen only in centralized, proprietary solutions.
+The API endpoints detailed below are relative to [code]api/z/1.0[/code], meaning that if an API is listed as [code]channel/stream[/code] the full API URL is [code][baseurl]/api/z/1.0/channel/stream[/code].
-#### Communications
-
-Communications and social networking are an integral part of the grid. Any channel (and any services provided by that channel) can make full use of feature-rich social communications on a global scale. These communications may be public or private - and private communications comprise not only fully encrypted transport, but also encrypted storage to help protect against accidental snooping and disclosure by rogue system administrators and internet service providers.
-
-Zot supports a wide array of background services in the grid, from friend suggestions to directory services. New content and data updates are propagated in the background between hubs across the grid according to access control lists and permissions specified by both sender *and* receiver channels. Data is also synchronized between an arbitrary number of channel clones, allowing hub members to access data and continue collaborating seamlessly in the event that their primary hub is inaccessible or offline.
-
-#### Identity
-
-Zot's identity layer is unique. It provides **invisible single sign-on** across all sites in the grid.
-
-It also provides **nomadic identity**, so that your communications with friends, family, and or anyone else you're communicating with won't be affected by the loss of your primary communication node - either temporarily or permanently.
-
-The important bits of your identity and relationships can be backed up to a thumb drive, or your laptop, and may appear at any node in the grid at any time - with all your friends and preferences intact.
-
-Crucially, these nomadic instances are kept in sync so any instance can take over if another one is compromised or damaged. This protects you against not only major system failure, but also temporary site overloads and governmental manipulation or censorship.
-
-Nomadic identity, single sign-on, and $Projectname's decentralisation of hubs, we believe, introduce a high degree of degree of **resiliency** and **persistence** in internet communications, that are sorely needed amidst global trends towards corporate centralization, as well as mass and indiscriminate government surveillance and censorship.
-
-As you browse the grid, viewing channels and their unique content, you are seamlessly authenticated as you go, even across completely different server hubs. No passwords to enter. Nothing to type. You're just greeted by name on every new site you visit.
-
-How does Zot do that? We call it **magic-auth**, because $Projectname hides the details of the complexities that go into single sign-on logins, and nomadic identities, from the experience of browsing on the grid. This is one of the design goals of $Projectname: to increase privacy, and freedom on the web, while reducing the complexity and tedium brought by the need to enter new passwords and login names for every different sight that someone might visit online.
-
-You login only once on your home hub (or any nomadic backup hub you have chosen). This allows you to access any authenticated services provided anywhere in the grid - such as shopping, blogs, forums, and access to private information. This is just like the services offered by large corporate providers with huge user databases; however you can be a member of this community, as well as a server on this network using a $35 Rasberry Pi. Your password isn't stored on a thousand different sites, or even worse, only on a few sites like Google and Facebook, beyond your direct control.
-
-You cannot be silenced. You cannot be removed from the grid, unless you yourself choose to exit it.
-
-#### Access Control
-
-Zot's identity layer allows you to provide fine-grained permissions to any content you wish to publish - and these permissions extend across $Projectname. This is like having one super huge website made up of an army of small individual websites - and where each channel in the grid can completely control their privacy and sharing preferences for any web resources they create.
-
-Currently, $Projectname supports access control for many types of data, including post/comment discussion threads, photo albums, events, cloud files, web pages, wikis, and more. Every object and how it is shared and with whom is completely under your control.
-
-This type of control is trivial on large corporate providers because they own the user database. Within the grid, there is no need for a huge user database on your machine - because the grid **is** your user database. It has what is essentially infinite capacity (limited by the total number of hubs online across the internet), and is spread amongst hundreds, and potentially millions of computers.
-
-Access can be granted or denied for any resource, to any channel, or any group of channels; anywhere within the grid. Others can access your content if you permit them to do so, and they do not even need to have an account on your hub.
-
-### Zot API
-
-The API endpoints detailed below are relative to `api/z/1.0`, meaning that if an
-API is listed as `channel/stream` the full API URL is
-`[baseurl]/api/z/1.0/channel/stream`
-
-### channel/export/basic
+[h3]channel/export/basic[/h3]
Export channel data
-### channel/stream
+[h3]channel/stream[/h3]
Fetch channel conversation items
-### network/stream
+[h3]network/stream[/h3]
Fetch network conversation items
-### files
+[h3]files[/h3]
List file storage (attach DB)
@@ -93,7 +51,7 @@ curl -u mychannel:mypassword https://xyz.macgirvin.com/api/z/1.0/files -d filety
Returns:
-
+[code nowrap]
{
"success": true,
@@ -163,16 +121,16 @@ Returns:
}
]
}
+[/code]
-
-### filemeta
+[h3]filemeta[/h3]
Export file metadata for any uploaded file
-### filedata
+[h3]filedata[/h3]
Provides the ability to download a file from cloud storage in chunks
@@ -206,7 +164,7 @@ Example:
https://xyz.macgirvin.com/api/z/1.0/filedata?f=&file_id=9f5217770fd&start=0&length=48
Returns:
-
+[code nowrap]
{
"attach": {
@@ -238,15 +196,15 @@ Returns:
}
}
+[/code]
+
+[h3]file/export[/h3]
-### file/export
+[h3]file[/h3]
-### file
-
-
-### albums
+[h3]albums[/h3]
Description: list photo albums
@@ -269,7 +227,7 @@ Output:
Example:
-
+[code nowrap]
{
"success": true,
@@ -312,20 +270,20 @@ Example:
]
}
+[/code]
-
-### photos
+[h3]photos[/h3]
list photo metadata
-### photo
+[h3]photo[/h3]
-### group
+[h3]group[/h3]
`GET /api/z/1.0/group`
@@ -336,7 +294,7 @@ Returns: DB tables of all privacy groups.
To use with API group_members, provide either 'group_id' from the id element returned in this call, or 'group_name' from the gname returned in this call.
-
+[code nowrap]
[
{
@@ -364,8 +322,8 @@ To use with API group_members, provide either 'group_id' from the id element ret
"gname": "Co-workers"
}
]
-
-### group_members
+[/code]
+[h3]group_members[/h3]
`GET /api/z/1.0/group_members`
@@ -379,7 +337,7 @@ Returns:
group_member+abook+xchan (DB join) for each member of the privacy group
-
+[code nowrap]
[
{
@@ -496,9 +454,9 @@ group_member+abook+xchan (DB join) for each member of the privacy group
}
]
+[/code]
-
-### xchan
+[h3]xchan[/h3]
An xchan is a global location independent channel and is the primary record for a network
@@ -513,7 +471,7 @@ Returns a portable xchan structure
Example: https://xyz.macgirvin.com/api/z/1.0/xchan?f=&address=mike@macgirvin.com
Returns:
-
+[code nowrap]
{
"hash": "jr54M_y2l5NgHX5wBvP0KqWcAHuW23p1ld-6Vn63_pGTZklrI36LF8vUHMSKJMD8xzzkz7s2xxCx4-BOLNPaVA",
"guid": "sebQ-IC4rmFn9d9iu17m4BXO-kHuNutWo2ySjeV2SIW1LzksUkss12xVo3m3fykYxN5HMcc7gUZVYv26asx-Pg",
@@ -542,8 +500,8 @@ Returns:
"pubforum": "0",
"deleted": "0"
}
-
-### item/update
+[/code]
+[h3]item/update[/h3]
Create or update an item (post, activity, webpage, etc.)
@@ -687,7 +645,7 @@ Example:
Returns:
-
+[code nowrap]
{
"success": true,
@@ -769,23 +727,23 @@ Returns:
}
}
-
-### item/full
+[/code]
+[h3]item/full[/h3]
Get all data associated with an item
-### abook
+[h3]abook[/h3]
Connections
-### abconfig
+[h3]abconfig[/h3]
Connection metadata (such as permissions)
-### perm_allowed
+[h3]perm_allowed[/h3]
Check a permission for a given xchan
diff --git a/doc/developer/zot_protocol.bb b/doc/developer/zot_protocol.bb
new file mode 100644
index 000000000..007672c70
--- /dev/null
+++ b/doc/developer/zot_protocol.bb
@@ -0,0 +1,80 @@
+[h3] What is Zot?[/h3]
+Zot is the revolutionary protocol that powers $Projectname, providing [b]communications[/b], [b]identity management[/b], and [b]access control[/b] across a fully [b]decentralised[/b] network of independent websites, often called "the grid". The resulting platform is a robust system that supports privacy and security while enabling the kind of rich web services typically seen only in centralized, proprietary solutions.
+
+[h4] Communications[/h4]
+Communications and social networking are an integral part of the grid. Any channel (and any services provided by that channel) can make full use of feature-rich social communications on a global scale. These communications may be public or private - and private communications comprise not only fully encrypted transport, but also encrypted storage to help protect against accidental snooping and disclosure by rogue system administrators and internet service providers.
+
+Zot supports a wide array of background services in the grid, from friend suggestions to directory services. New content and data updates are propagated in the background between hubs across the grid according to access control lists and permissions specified by both sender [i]and[/i] receiver channels. Data is also synchronized between an arbitrary number of channel clones, allowing hub members to access data and continue collaborating seamlessly in the event that their primary hub is inaccessible or offline.
+
+[h4] Identity [/h4]
+Zot's identity layer is unique. It provides [b]invisible single sign-on[/b] across all sites in the grid.
+
+It also provides [b]nomadic identity[/b], so that your communications with friends, family, and or anyone else you're communicating with won't be affected by the loss of your primary communication node - either temporarily or permanently.
+
+The important bits of your identity and relationships can be backed up to a thumb drive, or your laptop, and may appear at any node in the grid at any time - with all your friends and preferences intact.
+
+Crucially, these nomadic instances are kept in sync so any instance can take over if another one is compromised or damaged. This protects you against not only major system failure, but also temporary site overloads and governmental manipulation or censorship.
+
+Nomadic identity, single sign-on, and $Projectname's decentralisation of hubs, we believe, introduce a high degree of degree of [b]resiliency[/b] and [b]persistence[/b] in internet communications, that are sorely needed amidst global trends towards corporate centralization, as well as mass and indiscriminate government surveillance and censorship.
+
+As you browse the grid, viewing channels and their unique content, you are seamlessly authenticated as you go, even across completely different server hubs. No passwords to enter. Nothing to type. You're just greeted by name on every new site you visit.
+
+How does Zot do that? We call it [b]magic-auth[/b], because $Projectname hides the details of the complexities that go into single sign-on logins, and nomadic identities, from the experience of browsing on the grid. This is one of the design goals of $Projectname: to increase privacy, and freedom on the web, while reducing the complexity and tedium brought by the need to enter new passwords and login names for every different sight that someone might visit online. You login only once on your home hub (or any nomadic backup hub you have chosen). This allows you to access any authenticated services provided anywhere in the grid - such as shopping, blogs, forums, and access to private information. Your password isn't stored on a thousand different sites; it is stored on servers that you control or that you have chosen to trust.
+
+You cannot be silenced. You cannot be removed from the grid unless you yourself choose to exit it.
+
+[h4] Access Control[/h4]
+Zot's identity layer allows you to provide fine-grained permissions to any content you wish to publish - and these permissions extend across the grid. This is like having one super huge website made up of an army of small individual websites - and where each channel in the grid can completely control their privacy and sharing preferences for any web resources they create.
+
+Currently, $Projectname supports access control for many types of data, including post/comment discussion threads, photo albums, events, cloud files, web pages, wikis, and more. Every object and how it is shared and with whom is completely under your control.
+
+This type of control is trivial on large corporate providers because they own the user database. Within the grid, there is no need for a huge user database on your machine - because the grid [b]is[/b] your user database. It has what is essentially infinite capacity (limited by the total number of hubs online across the internet), and is spread amongst hundreds, and potentially millions of computers.
+
+Access can be granted or denied for any resource, to any channel, or any group of channels; anywhere within the grid. Others can access your content if you permit them to do so, and they do not even need to have an account on your hub.
+
+[h3]Zot Structures[/h3]
+[h4]Zot Signatures[/h4]
+All signed data in Zot is accomplished by performing an RSA sign operation using the private key of the initiator. The binary result is then base64url encoded for transport.
+[h4]Zot Encryption[/h4]
+Encryption is currently provided by AES256-CBC, the Advanced Encryption Standard using 256-bit keys and the Cipher Block Chaining mode of operation. Additional algorithms MAY be supported. A 32-octet key and 16-octet initialisation vector are randomly generated. The desired data is then encrypted using these generated strings and the result base64url encoded. Then we build an array:
+
+[dl terms="b"]
+[*= data]The base64url encoded encrypted data
+[*= alg]The chosen algorithm, in this case the string 'aes256cbc'.
+[*= key]The randomly generated key, RSA encrypted using the recipients public key, and the result base64url encoded
+[*= iv]The randomly generated initialization vector, RSA encrypted using the recipient's public key, and the result base64url encoded
+[/dl]
+
+[h4]Basic Zot Packet[/h4]
+Used for initiating a dialogue with another Zot site. This packet MAY be encrypted. The presence of an array element 'iv' indicates encryption has been applied. When sending an 'auth_check' packet type, this packet MUST be encrypted, using the public key of the destination site (the site key, as opposed to a sender key).
+
+[code nowrap]
+ {
+ "type":"notify",
+ "sender":{
+ "guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
+ "guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q",
+ "url":"http:\/\/podunk.edu",
+ "url_sig":"T8Bp7j5DHHhQDCFcAHXfuhUfGk2P3inPbImwaXXF1xJd3TGgluoXyyKDx6WDm07x0hqbupoAoZB1qBP3_WfvWiJVAK4N1FD77EOYttUEHZ7L43xy5PCpojJQmkppGbPJc2jnTIc_F1vvGvw5fv8gBWZvPqTdb6LWF6FLrzwesZpi7j2rsioZ3wyUkqb5TDZaNNeWQrIEYXrEnWkRI_qTSOzx0dRTsGO6SpU1fPWuOOYMZG8Nh18nay0kLpxReuHCiCdxjXRVvk5k9rkcMbDBJcBovhiSioPKv_yJxcZVBATw3z3TTE95kGi4wxCEenxwhSpvouwa5b0hT7NS4Ay70QaxoKiLb3ZjhZaUUn4igCyZM0h6fllR5I6J_sAQxiMYD0v5ouIlb0u8YVMni93j3zlqMWdDUZ4WgTI7NNbo8ug9NQDHd92TPmSE1TytPTgya3tsFMzwyq0LZ0b-g-zSXWIES__jKQ7vAtIs9EwlPxqJXEDDniZ2AJ6biXRYgE2Kd6W_nmI7w31igwQTms3ecXe5ENI3ckEPUAq__llNnND7mxp5ZrdXzd5HHU9slXwDShYcW3yDeQLEwAVomTGSFpBrCX8W77n9hF3JClkWaeS4QcZ3xUtsSS81yLrp__ifFfQqx9_Be89WVyIOoF4oydr08EkZ8zwlAsbZLG7eLXY"
+ },
+ "callback":"\/post",
+ "version":1,
+ "secret":"1eaa6613699be6ebb2adcefa5379c61a3678aa0df89025470fac871431b70467",
+ "secret_sig":"0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q"
+ }
+[/code]
+
+[dl terms="b"]
+[*= type] The message type: [b]notify, purge, refresh, force_refresh, auth_check, ping[/b] or [b]pickup[/b]. The packet contents vary by message type. Here we will describe the [b]notify[/b] packet.
+[*= callback]A string to be appended onto the url which identifies the Zot communications endpoint on this system. It is typically the string "/post".
+[*= version]The Zot protocol identifier, to allow future protocol revisions to co-exist.
+[*= secret]A 64-char string which is randomly generated by the sending site.
+[*= secret_sig]The RSA signature of the secret, signed with the sender's private key.
+[*= sender] An array of four components that provide a portable identity. We can contact the URL provided and download a Zot info packet to obtain the public key of the sender, and use that to verify the sender guid and the posting URL signatures.
+ [dl terms="b"]
+ [*= guid]Typically a 64 character base64url encoded string. This is generated when an identity is created and an attempt is made that it be unique; though this isn't required.
+ [*= guid_sig]The RSA signature of the guid, signed by the sender's private key.
+ [*= url]The base url of the location this post is originating from.
+ [*= url_sig]The RSA signature of url, signed by the sender's private key.
+ [/dl]
+[/dl]
diff --git a/doc/member/member_guide.bb b/doc/member/member_guide.bb
index 3c1376282..6edd8a0a4 100644
--- a/doc/member/member_guide.bb
+++ b/doc/member/member_guide.bb
@@ -296,6 +296,181 @@ $Projectname supports several markup languages for advanced formatting of conten
[tr][td]Webpage element[/td][td][url=[baseurl]/help/member/bbcode]BBcode[/url], Markdown, HTML[/td][/tr]
[/table]
+[h3]Web Pages[/h3]
+
+Hubzilla enables users to create static webpages. To activate this feature, enable the [b]Web Pages[/b] feature in your [b][url=[baseurl]/settings/features/]Additional Features[/url][/b] section.
+
+Once enabled, a new tab will appear on your channel page labeled "Webpages". Clicking this link will take you to the webpage editor. Pages will be accessible at [b][baseurl]/page/[observer=1][observer.webname][/observer][observer=0]channelname[/observer]/pagelinktitle[/b]
+
+The "page link title" box allows a user to specify the "pagelinktitle" of this URL. If no page link title is set, we will set one for you automatically, using the message ID of the item.
+
+Beneath the page creation box, a list of existing pages will appear with an "edit" link. Clicking this will take you to an editor, similar to that of the post editor, where you can make changes to your webpages.
+
+[h4]Using Blocks[/h4]
+
+Blocks can be parts of webpages. The basic HTML of a block looks like this
+[code]
+
+ Block Content
+
+
+[/code]
+
+If a block has text/html content type it can also contain menu elements. Sample content of
+[code]
+ HTML block content
+ [menu]menuname[/menu]
+
+[/code]
+will produce HTML like this
+[code]
+
+
+[/code]
+
+Via the $content macro a block can also contain the actual webpage content. For this create a block with only
+[code]
+ $content
+
+[/code]as content.
+
+To make a block appear in the webpage it must be defined in the page layout inside a region.
+[code]
+ [region=aside]
+ [block]blockname[/block]
+ [/region]
+
+[/code]
+
+The block appearance can be manipulated in the page layout.
+
+Custom classes can be assigned
+[code]
+ [region=aside]
+ [block=myclass]blockname[/block]
+ [/region]
+
+[/code]
+will produce this HTML
+[code]
+
+ Block Content
+
+
+[/code]
+
+Via the wrap variable a block can be stripped off its wrapping
tag
+[code]
+ [region=aside]
+ [block][var=wrap]none[/var]blockname[/block]
+ [/region]
+
+[/code]
+will produce this HTML
+[code]
+ Block Content
+[/code]
+
+[h4]Webpage element import tool[/h4]
+
+There are two methods of importing webpage elements: uploading a zip file or referencing a local cloud files folder. Both methods require that the webpage elements are specified using a specific folder structure. The import tool makes it possible to import all the elements necessary to construct an entire website or set of websites. The goal is to accommodate external development of webpages as well as tools to simplify and automate deployment on a hub.
+
+[h5] Folder structure [/h5]
+Element definitions must be stored in the repo root under folders called
+[code]
+ /pages/
+ /blocks/
+ /layouts/
+[/code]
+
+Each element of these types must be defined in an individual subfolder using two files: one JSON-formatted file for the metadata and one plain text file for the element content.
+
+[h5] Page elements [/h5]
+Page element metadata is specified in a JSON-formatted file called [code]page.json[/code] with the following properties:
+[list]
+[*] title
+[*] pagelink
+[*] mimetype
+[*] layout
+[*] contentfile
+[/list]
+[b]Example[/b]
+
+Files:
+[code]
+ /pages/my-page/page.json
+ /pages/my-page/my-page.bbcode
+[/code]
+Content of [code]page.json[/code]:
+[code]
+ {
+ "title": "My Page",
+ "pagelink": "mypage",
+ "mimetype": "text/bbcode",
+ "layout": "my-layout",
+ "contentfile": "my-page.bbcode"
+ }
+[/code]
+[h5] Layout elements [/h5]
+
+Layout element metadata is specified in a JSON-formatted file called [code]layout.json[/code] with the following properties:
+[list]
+[*] name
+[*] description
+[*] contentfile
+[/list]
+[b]Example[/b]
+
+Files:
+[code]
+ /layouts/my-layout/layout.json
+ /layouts/my-layout/my-layout.bbcode
+[/code]
+Content of [code]layout.json[/code]:
+[code]
+ {
+ "name": "my-layout",
+ "description": "Layout for my project page",
+ "contentfile": "my-layout.bbcode"
+ }
+[/code]
+
+[h5] Block elements [/h5]
+
+Block element metadata is specified in a JSON-formatted file called [code]block.json[/code] with the following properties:
+[list]
+[*] name
+[*] title
+[*] mimetype
+[*] contentfile
+[/list]
+[b]Example[/b]
+
+Files:
+[code]
+ /blocks/my-block/block.json
+ /blocks/my-block/my-block.html
+[/code]
+Content of [code]block.json[/code]:
+
+[code]
+ {
+ "name": "my-block",
+ "title": "",
+ "mimetype": "text/html",
+ "contentfile": "my-block.html"
+ }
+[/code]
+
[h3]Personal Cloud Storage[/h3]
$Projectname provides an ability to store privately and/or share arbitrary files with friends.
diff --git a/doc/toc.html b/doc/toc.html
index ba26d17e3..eeb0fe437 100644
--- a/doc/toc.html
+++ b/doc/toc.html
@@ -49,7 +49,8 @@
@@ -67,7 +68,7 @@
diff --git a/include/bbcode.php b/include/bbcode.php
index f0cf861f3..00d22d26d 100644
--- a/include/bbcode.php
+++ b/include/bbcode.php
@@ -511,6 +511,20 @@ function bb_code($match) {
return '' . trim($match[1]) . '
';
}
+function bb_code_options($match) {
+ if(strpos($match[0], " ")) {
+ $class = "";
+ } else {
+ $class = "inline-code";
+ }
+ if(strpos($match[1], 'nowrap')) {
+ $style = "overflow-x: auto; white-space: pre;";
+ } else {
+ $style = "";
+ }
+ return '' . trim($match[2]) . '
';
+}
+
function bb_highlight($match) {
$lang = ((in_array(strtolower($match[1]),['php','css','mysql','sql','abap','diff','html','perl','ruby',
'vbscript','avrc','dtd','java','xml','cpp','python','javascript','js','json','sh']))
@@ -933,6 +947,11 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false)
$Text = preg_replace_callback("/\[code\](.*?)\[\/code\]/ism", 'bb_code', $Text);
}
+ // Check for [code options] text
+ if (strpos($Text,'[code ') !== false) {
+ $Text = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism", 'bb_code_options', $Text);
+ }
+
// Check for [spoiler] text
$endlessloop = 0;
while ((strpos($Text, "[/spoiler]")!== false) and (strpos($Text, "[spoiler]") !== false) and (++$endlessloop < 20)) {
diff --git a/library/fullcalendar/CHANGELOG.txt b/library/fullcalendar/CHANGELOG.txt
index e91ce3bd6..ed2eddb8a 100644
--- a/library/fullcalendar/CHANGELOG.txt
+++ b/library/fullcalendar/CHANGELOG.txt
@@ -1,4 +1,27 @@
+v3.1.0 (2016-12-05)
+-------------------
+
+- experimental support for implicitly batched ("debounced") event rendering (#2938)
+ - `eventRenderWait` (off by default)
+- new `footer` option, similar to header toolbar (#654, #3299)
+- event rendering batch methods (#3351):
+ - `renderEvents`
+ - `updateEvents`
+- more granular touch settings (#3377):
+ - `eventLongPressDelay`
+ - `selectLongPressDelay`
+- eventDestroy not called when removing the popover (#3416, #3419)
+- print stylesheet and gcal extension now offered as minified (#3415)
+- fc-today in agenda header cells (#3361, #3365)
+- height-related options in tandem with other options (#3327, #3384)
+- Kazakh locale (#3394)
+- Afrikaans locale (#3390)
+- internal refactor related to timing of rendering and firing handlers.
+ calls to rerender the current date-range and events from within handlers
+ might not execute immediately. instead, will execute after handler finishes.
+
+
v3.0.1 (2016-09-26)
-------------------
diff --git a/library/fullcalendar/fullcalendar.css b/library/fullcalendar/fullcalendar.css
index 7a3f39923..5620e0bd6 100644
--- a/library/fullcalendar/fullcalendar.css
+++ b/library/fullcalendar/fullcalendar.css
@@ -1,5 +1,5 @@
/*!
- * FullCalendar v3.0.1 Stylesheet
+ * FullCalendar v3.1.0 Stylesheet
* Docs & License: http://fullcalendar.io/
* (c) 2016 Adam Shaw
*/
@@ -49,7 +49,7 @@ body .fc { /* extra precedence to overcome jqui */
color: #666;
}
-.fc-unthemed .fc-today {
+.fc-unthemed td.fc-today {
background: #fcf8e3;
}
@@ -811,9 +811,16 @@ a.fc-more:hover {
.fc-toolbar {
text-align: center;
+}
+
+.fc-toolbar.fc-header-toolbar {
margin-bottom: 1em;
}
+.fc-toolbar.fc-footer-toolbar {
+ margin-top: 1em;
+}
+
.fc-toolbar .fc-left {
float: left;
}
diff --git a/library/fullcalendar/fullcalendar.js b/library/fullcalendar/fullcalendar.js
index 3c2c380bc..b7371e25f 100644
--- a/library/fullcalendar/fullcalendar.js
+++ b/library/fullcalendar/fullcalendar.js
@@ -1,5 +1,5 @@
/*!
- * FullCalendar v3.0.1
+ * FullCalendar v3.1.0
* Docs & License: http://fullcalendar.io/
* (c) 2016 Adam Shaw
*/
@@ -19,8 +19,8 @@
;;
var FC = $.fullCalendar = {
- version: "3.0.1",
- internalApiVersion: 6
+ version: "3.1.0",
+ internalApiVersion: 7
};
var fcViews = FC.views = {};
@@ -53,13 +53,14 @@ $.fn.fullCalendar = function(options) {
calendar.render();
}
});
-
+
return res;
};
var complexOptions = [ // names of options that are objects whose properties should be combined
'header',
+ 'footer',
'buttonText',
'buttonIcons',
'themeButtonIcons'
@@ -848,6 +849,7 @@ function createObject(proto) {
f.prototype = proto;
return new f();
}
+FC.createObject = createObject;
function copyOwnProps(src, dest) {
@@ -1004,20 +1006,6 @@ function debounce(func, wait, immediate) {
};
}
-
-// HACK around jQuery's now A+ promises: execute callback synchronously if already resolved.
-// thenFunc shouldn't accept args.
-// similar to whenResources in Scheduler plugin.
-function syncThen(promise, thenFunc) {
- // not a promise, or an already-resolved promise?
- if (!promise || !promise.then || promise.state() === 'resolved') {
- return $.when(thenFunc()); // resolve immediately
- }
- else if (thenFunc) {
- return promise.then(thenFunc);
- }
-}
-
;;
/*
@@ -1681,6 +1669,201 @@ function mixIntoClass(theClass, members) {
}
;;
+/*
+Wrap jQuery's Deferred Promise object to be slightly more Promise/A+ compliant.
+With the added non-standard feature of synchronously executing handlers on resolved promises,
+which doesn't always happen otherwise (esp with nested .then handlers!?),
+so, this makes things a lot easier, esp because jQuery 3 changed the synchronicity for Deferred objects.
+
+TODO: write tests and more comments
+*/
+
+function Promise(executor) {
+ var deferred = $.Deferred();
+ var promise = deferred.promise();
+
+ if (typeof executor === 'function') {
+ executor(
+ function(value) { // resolve
+ if (Promise.immediate) {
+ promise._value = value;
+ }
+ deferred.resolve(value);
+ },
+ function() { // reject
+ deferred.reject();
+ }
+ );
+ }
+
+ if (Promise.immediate) {
+ var origThen = promise.then;
+
+ promise.then = function(onFulfilled, onRejected) {
+ var state = promise.state();
+
+ if (state === 'resolved') {
+ if (typeof onFulfilled === 'function') {
+ return Promise.resolve(onFulfilled(promise._value));
+ }
+ }
+ else if (state === 'rejected') {
+ if (typeof onRejected === 'function') {
+ onRejected();
+ return promise; // already rejected
+ }
+ }
+
+ return origThen.call(promise, onFulfilled, onRejected);
+ };
+ }
+
+ return promise; // instanceof Promise will break :( TODO: make Promise a real class
+}
+
+FC.Promise = Promise;
+
+Promise.immediate = true;
+
+
+Promise.resolve = function(value) {
+ if (value && typeof value.resolve === 'function') {
+ return value.promise();
+ }
+ if (value && typeof value.then === 'function') {
+ return value;
+ }
+ else {
+ var deferred = $.Deferred().resolve(value);
+ var promise = deferred.promise();
+
+ if (Promise.immediate) {
+ var origThen = promise.then;
+
+ promise._value = value;
+
+ promise.then = function(onFulfilled, onRejected) {
+ if (typeof onFulfilled === 'function') {
+ return Promise.resolve(onFulfilled(value));
+ }
+ return origThen.call(promise, onFulfilled, onRejected);
+ };
+ }
+
+ return promise;
+ }
+};
+
+
+Promise.reject = function() {
+ return $.Deferred().reject().promise();
+};
+
+
+Promise.all = function(inputs) {
+ var hasAllValues = false;
+ var values;
+ var i, input;
+
+ if (Promise.immediate) {
+ hasAllValues = true;
+ values = [];
+
+ for (i = 0; i < inputs.length; i++) {
+ input = inputs[i];
+
+ if (input && typeof input.state === 'function' && input.state() === 'resolved' && ('_value' in input)) {
+ values.push(input._value);
+ }
+ else if (input && typeof input.then === 'function') {
+ hasAllValues = false;
+ break;
+ }
+ else {
+ values.push(input);
+ }
+ }
+ }
+
+ if (hasAllValues) {
+ return Promise.resolve(values);
+ }
+ else {
+ return $.when.apply($.when, inputs).then(function() {
+ return $.when($.makeArray(arguments));
+ });
+ }
+};
+
+;;
+
+// TODO: write tests and clean up code
+
+function TaskQueue(debounceWait) {
+ var q = []; // array of runFuncs
+
+ function addTask(taskFunc) {
+ return new Promise(function(resolve) {
+
+ // should run this function when it's taskFunc's turn to run.
+ // responsible for popping itself off the queue.
+ var runFunc = function() {
+ Promise.resolve(taskFunc()) // result might be async, coerce to promise
+ .then(resolve) // resolve TaskQueue::push's promise, for the caller. will receive result of taskFunc.
+ .then(function() {
+ q.shift(); // pop itself off
+
+ // run the next task, if any
+ if (q.length) {
+ q[0]();
+ }
+ });
+ };
+
+ // always put the task at the end of the queue, BEFORE running the task
+ q.push(runFunc);
+
+ // if it's the only task in the queue, run immediately
+ if (q.length === 1) {
+ runFunc();
+ }
+ });
+ }
+
+ this.add = // potentially debounce, for the public method
+ typeof debounceWait === 'number' ?
+ debounce(addTask, debounceWait) :
+ addTask; // if not a number (null/undefined/false), no debounce at all
+
+ this.addQuickly = addTask; // guaranteed no debounce
+}
+
+FC.TaskQueue = TaskQueue;
+
+/*
+q = new TaskQueue();
+
+function work(i) {
+ return q.push(function() {
+ trigger();
+ console.log('work' + i);
+ });
+}
+
+var cnt = 0;
+
+function trigger() {
+ if (cnt < 5) {
+ cnt++;
+ work(cnt);
+ }
+}
+
+work(9);
+*/
+
+;;
+
var EmitterMixin = FC.EmitterMixin = {
// jQuery-ification via $(this) allows a non-DOM object to have
@@ -1688,7 +1871,18 @@ var EmitterMixin = FC.EmitterMixin = {
on: function(types, handler) {
+ $(this).on(types, this._prepareIntercept(handler));
+ return this; // for chaining
+ },
+
+ one: function(types, handler) {
+ $(this).one(types, this._prepareIntercept(handler));
+ return this; // for chaining
+ },
+
+
+ _prepareIntercept: function(handler) {
// handlers are always called with an "event" object as their first param.
// sneak the `this` context and arguments into the extra parameter object
// and forward them on to the original handler.
@@ -1708,9 +1902,7 @@ var EmitterMixin = FC.EmitterMixin = {
}
intercept.guid = handler.guid;
- $(this).on(types, intercept);
-
- return this; // for chaining
+ return intercept;
},
@@ -2039,9 +2231,15 @@ var CoordCache = FC.CoordCache = Class.extend({
// Queries the els for coordinates and stores them.
// Call this method before using and of the get* methods below.
build: function() {
- var offsetParentEl = this.forcedOffsetParentEl || this.els.eq(0).offsetParent();
+ var offsetParentEl = this.forcedOffsetParentEl;
+ if (!offsetParentEl && this.els.length > 0) {
+ offsetParentEl = this.els.eq(0).offsetParent();
+ }
+
+ this.origin = offsetParentEl ?
+ offsetParentEl.offset() :
+ null;
- this.origin = offsetParentEl.offset();
this.boundingRect = this.queryBoundingRect();
if (this.isHorizontal) {
@@ -2224,12 +2422,19 @@ var CoordCache = FC.CoordCache = Class.extend({
// Compute and return what the elements' bounding rectangle is, from the user's perspective.
// Right now, only returns a rectangle if constrained by an overflow:scroll element.
+ // Returns null if there are no elements
queryBoundingRect: function() {
- var scrollParentEl = getScrollParent(this.els.eq(0));
+ var scrollParentEl;
- if (!scrollParentEl.is(document)) {
- return getClientRect(scrollParentEl);
+ if (this.els.length > 0) {
+ scrollParentEl = getScrollParent(this.els.eq(0));
+
+ if (!scrollParentEl.is(document)) {
+ return getClientRect(scrollParentEl);
+ }
}
+
+ return null;
},
isPointInBounds: function(leftOffset, topOffset) {
@@ -3448,6 +3653,7 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, MouseIgnorerMixin, {
dayTouchStart: function(ev) {
var view = this.view;
+ var selectLongPressDelay = view.opt('selectLongPressDelay');
// HACK to prevent a user's clickaway for unselecting a range or an event
// from causing a dayClick.
@@ -3455,8 +3661,12 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, MouseIgnorerMixin, {
this.tempIgnoreMouse();
}
+ if (selectLongPressDelay == null) {
+ selectLongPressDelay = view.opt('longPressDelay'); // fallback
+ }
+
this.dayDragListener.startInteraction(ev, {
- delay: this.view.opt('longPressDelay')
+ delay: selectLongPressDelay
});
},
@@ -3793,7 +4003,7 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, MouseIgnorerMixin, {
// Computes HTML classNames for a single-day element
- getDayClasses: function(date) {
+ getDayClasses: function(date, noThemeHighlight) {
var view = this.view;
var today = view.calendar.getNow();
var classes = [ 'fc-' + dayIDs[date.day()] ];
@@ -3806,10 +4016,11 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, MouseIgnorerMixin, {
}
if (date.isSame(today, 'day')) {
- classes.push(
- 'fc-today',
- view.highlightStateClass
- );
+ classes.push('fc-today');
+
+ if (noThemeHighlight !== true) {
+ classes.push(view.highlightStateClass);
+ }
}
else if (date < today) {
classes.push('fc-past');
@@ -4005,16 +4216,33 @@ Grid.mixin({
// Compute business hour segs for the grid's current date range.
// Caller must ask if whole-day business hours are needed.
- buildBusinessHourSegs: function(wholeDay) {
- var events = this.view.calendar.getCurrentBusinessHourEvents(wholeDay);
+ // If no `businessHours` configuration value is specified, assumes the calendar default.
+ buildBusinessHourSegs: function(wholeDay, businessHours) {
+ return this.eventsToSegs(
+ this.buildBusinessHourEvents(wholeDay, businessHours)
+ );
+ },
+
+
+ // Compute business hour *events* for the grid's current date range.
+ // Caller must ask if whole-day business hours are needed.
+ // If no `businessHours` configuration value is specified, assumes the calendar default.
+ buildBusinessHourEvents: function(wholeDay, businessHours) {
+ var calendar = this.view.calendar;
+ var events;
+
+ if (businessHours == null) {
+ // fallback
+ // access from calendawr. don't access from view. doesn't update with dynamic options.
+ businessHours = calendar.options.businessHours;
+ }
+
+ events = calendar.computeBusinessHourEvents(wholeDay, businessHours);
// HACK. Eventually refactor business hours "events" system.
// If no events are given, but businessHours is activated, this means the entire visible range should be
// marked as *not* business-hours, via inverse-background rendering.
- if (
- !events.length &&
- this.view.calendar.options.businessHours // don't access view option. doesn't update with dynamic options
- ) {
+ if (!events.length && businessHours) {
events = [
$.extend({}, BUSINESS_HOUR_EVENT_DEFAULTS, {
start: this.view.end, // guaranteed out-of-range
@@ -4024,7 +4252,7 @@ Grid.mixin({
];
}
- return this.eventsToSegs(events);
+ return events;
},
@@ -4066,7 +4294,7 @@ Grid.mixin({
handleSegClick: function(seg, ev) {
- var res = this.view.trigger('eventClick', seg.el[0], seg.event, ev); // can return `false` to cancel
+ var res = this.view.publiclyTrigger('eventClick', seg.el[0], seg.event, ev); // can return `false` to cancel
if (res === false) {
ev.preventDefault();
}
@@ -4083,7 +4311,7 @@ Grid.mixin({
if (this.view.isEventResizable(seg.event)) {
seg.el.addClass('fc-allow-mouse-resize');
}
- this.view.trigger('eventMouseover', seg.el[0], seg.event, ev);
+ this.view.publiclyTrigger('eventMouseover', seg.el[0], seg.event, ev);
}
},
@@ -4099,7 +4327,7 @@ Grid.mixin({
if (this.view.isEventResizable(seg.event)) {
seg.el.removeClass('fc-allow-mouse-resize');
}
- this.view.trigger('eventMouseout', seg.el[0], seg.event, ev);
+ this.view.publiclyTrigger('eventMouseout', seg.el[0], seg.event, ev);
}
},
@@ -4124,6 +4352,7 @@ Grid.mixin({
var isResizable = view.isEventResizable(event);
var isResizing = false;
var dragListener;
+ var eventLongPressDelay;
if (isSelected && isResizable) {
// only allow resizing of the event is selected
@@ -4132,12 +4361,17 @@ Grid.mixin({
if (!isResizing && (isDraggable || isResizable)) { // allowed to be selected?
+ eventLongPressDelay = view.opt('eventLongPressDelay');
+ if (eventLongPressDelay == null) {
+ eventLongPressDelay = view.opt('longPressDelay'); // fallback
+ }
+
dragListener = isDraggable ?
this.buildSegDragListener(seg) :
this.buildSegSelectListener(seg); // seg isn't draggable, but still needs to be selected
dragListener.startInteraction(ev, { // won't start if already started
- delay: isSelected ? 0 : this.view.opt('longPressDelay') // do delay if not already selected
+ delay: isSelected ? 0 : eventLongPressDelay // do delay if not already selected
});
}
@@ -4270,11 +4504,15 @@ Grid.mixin({
mouseFollower.stop(!dropLocation, function() {
if (isDragging) {
view.unrenderDrag();
- view.showEvent(event);
_this.segDragStop(seg, ev);
}
+
if (dropLocation) {
- view.reportEventDrop(event, dropLocation, this.largeUnit, el, ev);
+ // no need to re-show original, will rerender all anyways. esp important if eventRenderWait
+ view.reportEventDrop(event, dropLocation, _this.largeUnit, el, ev);
+ }
+ else {
+ view.showEvent(event);
}
});
_this.segDragListener = null;
@@ -4316,14 +4554,14 @@ Grid.mixin({
// Called before event segment dragging starts
segDragStart: function(seg, ev) {
this.isDraggingSeg = true;
- this.view.trigger('eventDragStart', seg.el[0], seg.event, ev, {}); // last argument is jqui dummy
+ this.view.publiclyTrigger('eventDragStart', seg.el[0], seg.event, ev, {}); // last argument is jqui dummy
},
// Called after event segment dragging stops
segDragStop: function(seg, ev) {
this.isDraggingSeg = false;
- this.view.trigger('eventDragStop', seg.el[0], seg.event, ev, {}); // last argument is jqui dummy
+ this.view.publiclyTrigger('eventDragStop', seg.el[0], seg.event, ev, {}); // last argument is jqui dummy
},
@@ -4561,18 +4799,23 @@ Grid.mixin({
},
hitOut: function() { // called before mouse moves to a different hit OR moved out of all hits
resizeLocation = null;
+ view.showEvent(event); // for when out-of-bounds. show original
},
hitDone: function() { // resets the rendering to show the original event
_this.unrenderEventResize();
- view.showEvent(event);
enableCursor();
},
interactionEnd: function(ev) {
if (isDragging) {
_this.segResizeStop(seg, ev);
}
+
if (resizeLocation) { // valid date to resize to?
- view.reportEventResize(event, resizeLocation, this.largeUnit, el, ev);
+ // no need to re-show original, will rerender all anyways. esp important if eventRenderWait
+ view.reportEventResize(event, resizeLocation, _this.largeUnit, el, ev);
+ }
+ else {
+ view.showEvent(event);
}
_this.segResizeListener = null;
}
@@ -4585,14 +4828,14 @@ Grid.mixin({
// Called before event segment resizing starts
segResizeStart: function(seg, ev) {
this.isResizingSeg = true;
- this.view.trigger('eventResizeStart', seg.el[0], seg.event, ev, {}); // last argument is jqui dummy
+ this.view.publiclyTrigger('eventResizeStart', seg.el[0], seg.event, ev, {}); // last argument is jqui dummy
},
// Called after event segment resizing stops
segResizeStop: function(seg, ev) {
this.isResizingSeg = false;
- this.view.trigger('eventResizeStop', seg.el[0], seg.event, ev, {}); // last argument is jqui dummy
+ this.view.publiclyTrigger('eventResizeStop', seg.el[0], seg.event, ev, {}); // last argument is jqui dummy
},
@@ -5143,7 +5386,7 @@ var DayTableMixin = FC.DayTableMixin = {
this.dayIndices = dayIndices;
this.daysPerRow = daysPerRow;
this.rowCnt = rowCnt;
-
+
this.updateDayTableCols();
},
@@ -5381,9 +5624,25 @@ var DayTableMixin = FC.DayTableMixin = {
// (colspan should be no different)
renderHeadDateCellHtml: function(date, colspan, otherAttrs) {
var view = this.view;
+ var classNames = [
+ 'fc-day-header',
+ view.widgetHeaderClass
+ ];
+
+ // if only one row of days, the classNames on the header can represent the specific days beneath
+ if (this.rowCnt === 1) {
+ classNames = classNames.concat(
+ // includes the day-of-week class
+ // noThemeHighlight=true (don't highlight the header)
+ this.getDayClasses(date, true)
+ );
+ }
+ else {
+ classNames.push('fc-' + dayIDs[date.day()]); // only add the day-of-week class
+ }
return '' +
- '");
+ }
+ else {
+ el.empty();
+ }
+ el.append(renderSection('left'))
+ .append(renderSection('right'))
+ .append(renderSection('center'))
+ .append('
');
+ }
+ else {
+ removeElement();
+ }
+ }
+
+
+ function removeElement() {
+ if (el) {
+ el.remove();
+ el = t.el = null;
+ }
+ }
+
+
+ function renderSection(position) {
+ var sectionEl = $('
');
+ var buttonStr = toolbarOptions.layout[position];
+
+ if (buttonStr) {
+ $.each(buttonStr.split(' '), function(i) {
+ var groupChildren = $();
+ var isOnlyButtons = true;
+ var groupEl;
+
+ $.each(this.split(','), function(j, buttonName) {
+ var customButtonProps;
+ var viewSpec;
+ var buttonClick;
+ var overrideText; // text explicitly set by calendar's constructor options. overcomes icons
+ var defaultText;
+ var themeIcon;
+ var normalIcon;
+ var innerHtml;
+ var classes;
+ var button; // the element
+
+ if (buttonName == 'title') {
+ groupChildren = groupChildren.add($(' ')); // we always want it to take up height
+ isOnlyButtons = false;
+ }
+ else {
+ if ((customButtonProps = (calendar.options.customButtons || {})[buttonName])) {
+ buttonClick = function(ev) {
+ if (customButtonProps.click) {
+ customButtonProps.click.call(button[0], ev);
+ }
+ };
+ overrideText = ''; // icons will override text
+ defaultText = customButtonProps.text;
+ }
+ else if ((viewSpec = calendar.getViewSpec(buttonName))) {
+ buttonClick = function() {
+ calendar.changeView(buttonName);
+ };
+ viewsWithButtons.push(buttonName);
+ overrideText = viewSpec.buttonTextOverride;
+ defaultText = viewSpec.buttonTextDefault;
+ }
+ else if (calendar[buttonName]) { // a calendar method
+ buttonClick = function() {
+ calendar[buttonName]();
+ };
+ overrideText = (calendar.overrides.buttonText || {})[buttonName];
+ defaultText = calendar.options.buttonText[buttonName]; // everything else is considered default
+ }
+
+ if (buttonClick) {
+
+ themeIcon =
+ customButtonProps ?
+ customButtonProps.themeIcon :
+ calendar.options.themeButtonIcons[buttonName];
+
+ normalIcon =
+ customButtonProps ?
+ customButtonProps.icon :
+ calendar.options.buttonIcons[buttonName];
+
+ if (overrideText) {
+ innerHtml = htmlEscape(overrideText);
+ }
+ else if (themeIcon && calendar.options.theme) {
+ innerHtml = " ";
+ }
+ else if (normalIcon && !calendar.options.theme) {
+ innerHtml = " ";
+ }
+ else {
+ innerHtml = htmlEscape(defaultText);
+ }
+
+ classes = [
+ 'fc-' + buttonName + '-button',
+ tm + '-button',
+ tm + '-state-default'
+ ];
+
+ button = $( // type="button" so that it doesn't submit a form
+ '' +
+ innerHtml +
+ ' '
+ )
+ .click(function(ev) {
+ // don't process clicks for disabled buttons
+ if (!button.hasClass(tm + '-state-disabled')) {
+
+ buttonClick(ev);
+
+ // after the click action, if the button becomes the "active" tab, or disabled,
+ // it should never have a hover class, so remove it now.
+ if (
+ button.hasClass(tm + '-state-active') ||
+ button.hasClass(tm + '-state-disabled')
+ ) {
+ button.removeClass(tm + '-state-hover');
+ }
+ }
+ })
+ .mousedown(function() {
+ // the *down* effect (mouse pressed in).
+ // only on buttons that are not the "active" tab, or disabled
+ button
+ .not('.' + tm + '-state-active')
+ .not('.' + tm + '-state-disabled')
+ .addClass(tm + '-state-down');
+ })
+ .mouseup(function() {
+ // undo the *down* effect
+ button.removeClass(tm + '-state-down');
+ })
+ .hover(
+ function() {
+ // the *hover* effect.
+ // only on buttons that are not the "active" tab, or disabled
+ button
+ .not('.' + tm + '-state-active')
+ .not('.' + tm + '-state-disabled')
+ .addClass(tm + '-state-hover');
+ },
+ function() {
+ // undo the *hover* effect
+ button
+ .removeClass(tm + '-state-hover')
+ .removeClass(tm + '-state-down'); // if mouseleave happens before mouseup
+ }
+ );
+
+ groupChildren = groupChildren.add(button);
+ }
+ }
+ });
+
+ if (isOnlyButtons) {
+ groupChildren
+ .first().addClass(tm + '-corner-left').end()
+ .last().addClass(tm + '-corner-right').end();
+ }
+
+ if (groupChildren.length > 1) {
+ groupEl = $('
');
+ if (isOnlyButtons) {
+ groupEl.addClass('fc-button-group');
+ }
+ groupEl.append(groupChildren);
+ sectionEl.append(groupEl);
+ }
+ else {
+ sectionEl.append(groupChildren); // 1 or 0 children
+ }
+ });
+ }
+
+ return sectionEl;
+ }
+
+
+ function updateTitle(text) {
+ if (el) {
+ el.find('h2').text(text);
+ }
+ }
+
+
+ function activateButton(buttonName) {
+ if (el) {
+ el.find('.fc-' + buttonName + '-button')
+ .addClass(tm + '-state-active');
+ }
+ }
+
+
+ function deactivateButton(buttonName) {
+ if (el) {
+ el.find('.fc-' + buttonName + '-button')
+ .removeClass(tm + '-state-active');
+ }
+ }
+
+
+ function disableButton(buttonName) {
+ if (el) {
+ el.find('.fc-' + buttonName + '-button')
+ .prop('disabled', true)
+ .addClass(tm + '-state-disabled');
+ }
+ }
+
+
+ function enableButton(buttonName) {
+ if (el) {
+ el.find('.fc-' + buttonName + '-button')
+ .prop('disabled', false)
+ .removeClass(tm + '-state-disabled');
+ }
+ }
+
+
+ function getViewsWithButtons() {
+ return viewsWithButtons;
+ }
+
+}
+
;;
var Calendar = FC.Calendar = Class.extend({
@@ -9087,6 +9946,7 @@ var Calendar = FC.Calendar = Class.extend({
viewSpecCache: null, // cache of view definitions
view: null, // current View object
header: null,
+ footer: null,
loadingLevel: 0, // number of simultaneous loading tasks
@@ -9155,7 +10015,7 @@ var Calendar = FC.Calendar = Class.extend({
if ($.inArray(unit, intervalUnits) != -1) {
// put views that have buttons first. there will be duplicates, but oh well
- viewTypes = this.header.getViewsWithButtons();
+ viewTypes = this.header.getViewsWithButtons(); // TODO: include footer as well?
$.each(FC.views, function(viewType) { // all views
viewTypes.push(viewType);
});
@@ -9302,7 +10162,7 @@ var Calendar = FC.Calendar = Class.extend({
// Should be called when any type of async data fetching begins
pushLoading: function() {
if (!(this.loadingLevel++)) {
- this.trigger('loading', null, true, this.view);
+ this.publiclyTrigger('loading', null, true, this.view);
}
},
@@ -9310,7 +10170,7 @@ var Calendar = FC.Calendar = Class.extend({
// Should be called when any type of async data fetching completes
popLoading: function() {
if (!(--this.loadingLevel)) {
- this.trigger('loading', null, false, this.view);
+ this.publiclyTrigger('loading', null, false, this.view);
}
},
@@ -9348,11 +10208,7 @@ function Calendar_constructor(element, overrides) {
t.render = render;
t.destroy = destroy;
- t.refetchEvents = refetchEvents;
- t.refetchEventSources = refetchEventSources;
- t.reportEvents = reportEvents;
- t.reportEventChange = reportEventChange;
- t.rerenderEvents = renderEvents; // `renderEvents` serves as a rerender. an API method
+ t.rerenderEvents = rerenderEvents;
t.changeView = renderView; // `renderView` will switch to another view
t.select = select;
t.unselect = unselect;
@@ -9368,7 +10224,7 @@ function Calendar_constructor(element, overrides) {
t.getCalendar = getCalendar;
t.getView = getView;
t.option = option; // getter/setter method
- t.trigger = trigger;
+ t.publiclyTrigger = publiclyTrigger;
// Options
@@ -9561,15 +10417,12 @@ function Calendar_constructor(element, overrides) {
};
-
+
// Imports
// -----------------------------------------------------------------------------------
EventManager.call(t);
- var isFetchNeeded = t.isFetchNeeded;
- var fetchEvents = t.fetchEvents;
- var fetchEventSources = t.fetchEventSources;
@@ -9578,7 +10431,9 @@ function Calendar_constructor(element, overrides) {
var _element = element[0];
+ var toolbarsManager;
var header;
+ var footer;
var content;
var tm; // for making theme classes
var currentView; // NOTE: keep this in sync with this.view
@@ -9586,11 +10441,10 @@ function Calendar_constructor(element, overrides) {
var suggestedViewHeight;
var windowResizeProxy; // wraps the windowResize function
var ignoreWindowResize = 0;
- var events = [];
var date; // unzoned
-
-
-
+
+
+
// Main Rendering
// -----------------------------------------------------------------------------------
@@ -9602,8 +10456,8 @@ function Calendar_constructor(element, overrides) {
else {
date = t.getNow(); // getNow already returns unzoned
}
-
-
+
+
function render() {
if (!content) {
initialRender();
@@ -9614,8 +10468,8 @@ function Calendar_constructor(element, overrides) {
renderView();
}
}
-
-
+
+
function initialRender() {
element.addClass('fc');
@@ -9656,9 +10510,14 @@ function Calendar_constructor(element, overrides) {
content = $("
").prependTo(element);
- header = t.header = new Header(t);
- renderHeader();
+ var toolbars = buildToolbars();
+ toolbarsManager = new Iterator(toolbars);
+ header = t.header = toolbars[0];
+ footer = t.footer = toolbars[1];
+
+ renderHeader();
+ renderFooter();
renderView(t.options.defaultView);
if (t.options.handleWindowResize) {
@@ -9668,15 +10527,6 @@ function Calendar_constructor(element, overrides) {
}
- // can be called repeatedly and Header will rerender
- function renderHeader() {
- header.render();
- if (header.el) {
- element.prepend(header.el);
- }
- }
-
-
function destroy() {
if (currentView) {
@@ -9686,7 +10536,7 @@ function Calendar_constructor(element, overrides) {
// It is still the "current" view, just not rendered.
}
- header.removeElement();
+ toolbarsManager.proxyCall('removeElement');
content.remove();
element.removeClass('fc fc-ltr fc-rtl fc-unthemed ui-widget');
@@ -9696,13 +10546,13 @@ function Calendar_constructor(element, overrides) {
$(window).unbind('resize', windowResizeProxy);
}
}
-
-
+
+
function elementVisible() {
return element.is(':visible');
}
-
-
+
+
// View Rendering
// -----------------------------------------------------------------------------------
@@ -9711,11 +10561,13 @@ function Calendar_constructor(element, overrides) {
// Renders a view because of a date change, view-type change, or for the first time.
// If not given a viewType, keep the current view but render different dates.
// Accepts an optional scroll state to restore to.
- function renderView(viewType, explicitScrollState) {
+ function renderView(viewType, forcedScroll) {
ignoreWindowResize++;
+ var needsClearView = currentView && viewType && currentView.type !== viewType;
+
// if viewType is changing, remove the old view's rendering
- if (currentView && viewType && currentView.type !== viewType) {
+ if (needsClearView) {
freezeContentHeight(); // prevent a scroll jump when view element is removed
clearView();
}
@@ -9729,7 +10581,7 @@ function Calendar_constructor(element, overrides) {
currentView.setElement(
$("
").appendTo(content)
);
- header.activateButton(viewType);
+ toolbarsManager.proxyCall('activateButton', viewType);
}
if (currentView) {
@@ -9739,7 +10591,7 @@ function Calendar_constructor(element, overrides) {
// render or rerender the view
if (
- !currentView.displaying ||
+ !currentView.isDateSet ||
!( // NOT within interval range signals an implicit date window change
date >= currentView.intervalStart &&
date < currentView.intervalEnd
@@ -9747,19 +10599,27 @@ function Calendar_constructor(element, overrides) {
) {
if (elementVisible()) {
- currentView.display(date, explicitScrollState); // will call freezeContentHeight
- unfreezeContentHeight(); // immediately unfreeze regardless of whether display is async
+ if (forcedScroll) {
+ currentView.captureInitialScroll(forcedScroll);
+ }
+
+ currentView.setDate(date, forcedScroll);
+
+ if (forcedScroll) {
+ currentView.releaseScroll();
+ }
// need to do this after View::render, so dates are calculated
- updateHeaderTitle();
- updateTodayButton();
-
- getAndRenderEvents();
+ // NOTE: view updates title text proactively
+ updateToolbarsTodayButton();
}
}
}
- unfreezeContentHeight(); // undo any lone freezeContentHeight calls
+ if (needsClearView) {
+ thawContentHeight();
+ }
+
ignoreWindowResize--;
}
@@ -9767,7 +10627,7 @@ function Calendar_constructor(element, overrides) {
// Unrenders the current view and reflects this change in the Header.
// Unregsiters the `currentView`, but does not remove from viewByType hash.
function clearView() {
- header.deactivateButton(currentView.type);
+ toolbarsManager.proxyCall('deactivateButton', currentView.type);
currentView.removeElement();
currentView = t.view = null;
}
@@ -9783,13 +10643,14 @@ function Calendar_constructor(element, overrides) {
var viewType = currentView.type;
var scrollState = currentView.queryScroll();
clearView();
+ calcSize();
renderView(viewType, scrollState);
- unfreezeContentHeight();
+ thawContentHeight();
ignoreWindowResize--;
}
-
+
// Resizing
// -----------------------------------------------------------------------------------
@@ -9806,8 +10667,8 @@ function Calendar_constructor(element, overrides) {
t.isHeightAuto = function() {
return t.options.contentHeight === 'auto' || t.options.height === 'auto';
};
-
-
+
+
function updateSize(shouldRecalc) {
if (elementVisible()) {
@@ -9829,8 +10690,8 @@ function Calendar_constructor(element, overrides) {
_calcSize();
}
}
-
-
+
+
function _calcSize() { // assumes elementVisible
var contentHeightInput = t.options.contentHeight;
var heightInput = t.options.height;
@@ -9842,13 +10703,13 @@ function Calendar_constructor(element, overrides) {
suggestedViewHeight = contentHeightInput();
}
else if (typeof heightInput === 'number') { // exists and not 'auto'
- suggestedViewHeight = heightInput - queryHeaderHeight();
+ suggestedViewHeight = heightInput - queryToolbarsHeight();
}
else if (typeof heightInput === 'function') { // exists and is a function
- suggestedViewHeight = heightInput() - queryHeaderHeight();
+ suggestedViewHeight = heightInput() - queryToolbarsHeight();
}
else if (heightInput === 'parent') { // set to height of parent element
- suggestedViewHeight = element.parent().height() - queryHeaderHeight();
+ suggestedViewHeight = element.parent().height() - queryToolbarsHeight();
}
else {
suggestedViewHeight = Math.round(content.width() / Math.max(t.options.aspectRatio, .5));
@@ -9856,11 +10717,14 @@ function Calendar_constructor(element, overrides) {
}
- function queryHeaderHeight() {
- return header.el ? header.el.outerHeight(true) : 0; // includes margin
+ function queryToolbarsHeight() {
+ return toolbarsManager.items.reduce(function(accumulator, toolbar) {
+ var toolbarHeight = toolbar.el ? toolbar.el.outerHeight(true) : 0; // includes margin
+ return accumulator + toolbarHeight;
+ }, 0);
}
-
-
+
+
function windowResize(ev) {
if (
!ignoreWindowResize &&
@@ -9868,94 +10732,93 @@ function Calendar_constructor(element, overrides) {
currentView.start // view has already been rendered
) {
if (updateSize(true)) {
- currentView.trigger('windowResize', _element);
+ currentView.publiclyTrigger('windowResize', _element);
}
}
}
-
-
-
- /* Event Fetching/Rendering
+
+
+
+ /* Event Rendering
-----------------------------------------------------------------------------*/
- // TODO: going forward, most of this stuff should be directly handled by the view
- function refetchEvents() { // can be called as an API method
- fetchAndRenderEvents();
- }
-
-
- // TODO: move this into EventManager?
- function refetchEventSources(matchInputs) {
- fetchEventSources(t.getEventSourcesByMatchArray(matchInputs));
- }
-
-
- function renderEvents() { // destroys old events if previously rendered
+ function rerenderEvents() { // API method. destroys old events if previously rendered.
if (elementVisible()) {
- freezeContentHeight();
- currentView.displayEvents(events);
- unfreezeContentHeight();
- }
- }
-
-
- function getAndRenderEvents() {
- if (!t.options.lazyFetching || isFetchNeeded(currentView.start, currentView.end)) {
- fetchAndRenderEvents();
- }
- else {
- renderEvents();
+ t.reportEventChange(); // will re-trasmit events to the view, causing a rerender
}
}
- function fetchAndRenderEvents() {
- fetchEvents(currentView.start, currentView.end);
- // ... will call reportEvents
- // ... which will call renderEvents
- }
-
- // called when event data arrives
- function reportEvents(_events) {
- events = _events;
- renderEvents();
- }
-
-
- // called when a single event's data has been changed
- function reportEventChange() {
- renderEvents();
- }
-
-
-
- /* Header Updating
+ /* Toolbars
-----------------------------------------------------------------------------*/
- function updateHeaderTitle() {
- header.updateTitle(currentView.title);
+ function buildToolbars() {
+ return [
+ new Toolbar(t, computeHeaderOptions()),
+ new Toolbar(t, computeFooterOptions())
+ ];
}
- function updateTodayButton() {
- var now = t.getNow();
+ function computeHeaderOptions() {
+ return {
+ extraClasses: 'fc-header-toolbar',
+ layout: t.options.header
+ };
+ }
+
+ function computeFooterOptions() {
+ return {
+ extraClasses: 'fc-footer-toolbar',
+ layout: t.options.footer
+ };
+ }
+
+
+ // can be called repeatedly and Header will rerender
+ function renderHeader() {
+ header.setToolbarOptions(computeHeaderOptions());
+ header.render();
+ if (header.el) {
+ element.prepend(header.el);
+ }
+ }
+
+
+ // can be called repeatedly and Footer will rerender
+ function renderFooter() {
+ footer.setToolbarOptions(computeFooterOptions());
+ footer.render();
+ if (footer.el) {
+ element.append(footer.el);
+ }
+ }
+
+
+ t.setToolbarsTitle = function(title) {
+ toolbarsManager.proxyCall('updateTitle', title);
+ };
+
+
+ function updateToolbarsTodayButton() {
+ var now = t.getNow();
if (now >= currentView.intervalStart && now < currentView.intervalEnd) {
- header.disableButton('today');
+ toolbarsManager.proxyCall('disableButton', 'today');
}
else {
- header.enableButton('today');
+ toolbarsManager.proxyCall('enableButton', 'today');
}
}
-
+
/* Selection
-----------------------------------------------------------------------------*/
-
+
// this public method receives start/end dates in any format, with any timezone
function select(zonedStartInput, zonedEndInput) {
@@ -9963,56 +10826,56 @@ function Calendar_constructor(element, overrides) {
t.buildSelectSpan.apply(t, arguments)
);
}
-
+
function unselect() { // safe to be called before renderView
if (currentView) {
currentView.unselect();
}
}
-
-
-
+
+
+
/* Date
-----------------------------------------------------------------------------*/
-
-
+
+
function prev() {
date = currentView.computePrevDate(date);
renderView();
}
-
-
+
+
function next() {
date = currentView.computeNextDate(date);
renderView();
}
-
-
+
+
function prevYear() {
date.add(-1, 'years');
renderView();
}
-
-
+
+
function nextYear() {
date.add(1, 'years');
renderView();
}
-
-
+
+
function today() {
date = t.getNow();
renderView();
}
-
-
+
+
function gotoDate(zonedDateInput) {
date = t.moment(zonedDateInput).stripZone();
renderView();
}
-
-
+
+
function incrementDate(delta) {
date.add(moment.duration(delta));
renderView();
@@ -10030,8 +10893,8 @@ function Calendar_constructor(element, overrides) {
date = newDate.clone();
renderView(spec ? spec.type : null);
}
-
-
+
+
// for external API
function getDate() {
return t.applyTimezone(date); // infuse the calendar's timezone
@@ -10041,45 +10904,51 @@ function Calendar_constructor(element, overrides) {
/* Height "Freezing"
-----------------------------------------------------------------------------*/
- // TODO: move this into the view
+
t.freezeContentHeight = freezeContentHeight;
- t.unfreezeContentHeight = unfreezeContentHeight;
+ t.thawContentHeight = thawContentHeight;
+
+ var freezeContentHeightDepth = 0;
function freezeContentHeight() {
- content.css({
- width: '100%',
- height: content.height(),
- overflow: 'hidden'
- });
+ if (!(freezeContentHeightDepth++)) {
+ content.css({
+ width: '100%',
+ height: content.height(),
+ overflow: 'hidden'
+ });
+ }
}
- function unfreezeContentHeight() {
- content.css({
- width: '',
- height: '',
- overflow: ''
- });
+ function thawContentHeight() {
+ if (!(--freezeContentHeightDepth)) {
+ content.css({
+ width: '',
+ height: '',
+ overflow: ''
+ });
+ }
}
-
-
-
+
+
+
/* Misc
-----------------------------------------------------------------------------*/
-
+
function getCalendar() {
return t;
}
-
+
function getView() {
return currentView;
}
-
-
+
+
function option(name, value) {
var newOptionHash;
@@ -10135,19 +11004,20 @@ function Calendar_constructor(element, overrides) {
}
else if (optionName === 'timezone') {
t.rezoneArrayEventSources();
- refetchEvents();
+ t.refetchEvents();
return;
}
}
- // catch-all. rerender the header and rebuild/rerender the current view
+ // catch-all. rerender the header and footer and rebuild/rerender the current view
renderHeader();
+ renderFooter();
viewsByType = {}; // even non-current views will be affected by this option change. do before rerender
reinitView();
}
-
-
- function trigger(name, thisObj) { // overrides the Emitter's trigger method :(
+
+
+ function publiclyTrigger(name, thisObj) {
var args = Array.prototype.slice.call(arguments, 2);
thisObj = thisObj || _element;
@@ -10310,6 +11180,7 @@ Calendar.defaults = {
dropAccept: '*',
eventOrder: 'title',
+ //eventRenderWait: null,
eventLimit: false,
eventLimitText: 'more',
@@ -10549,275 +11420,6 @@ FC.locale('en', Calendar.englishDefaults);
;;
-/* Top toolbar area with buttons and title
-----------------------------------------------------------------------------------------------------------------------*/
-// TODO: rename all header-related things to "toolbar"
-
-function Header(calendar) {
- var t = this;
-
- // exports
- t.render = render;
- t.removeElement = removeElement;
- t.updateTitle = updateTitle;
- t.activateButton = activateButton;
- t.deactivateButton = deactivateButton;
- t.disableButton = disableButton;
- t.enableButton = enableButton;
- t.getViewsWithButtons = getViewsWithButtons;
- t.el = null; // mirrors local `el`
-
- // locals
- var el;
- var viewsWithButtons = [];
- var tm;
-
-
- // can be called repeatedly and will rerender
- function render() {
- var options = calendar.options;
- var sections = options.header;
-
- tm = options.theme ? 'ui' : 'fc';
-
- if (sections) {
- if (!el) {
- el = this.el = $("
");
- }
- else {
- el.empty();
- }
- el.append(renderSection('left'))
- .append(renderSection('right'))
- .append(renderSection('center'))
- .append('
');
- }
- else {
- removeElement();
- }
- }
-
-
- function removeElement() {
- if (el) {
- el.remove();
- el = t.el = null;
- }
- }
-
-
- function renderSection(position) {
- var sectionEl = $('
');
- var options = calendar.options;
- var buttonStr = options.header[position];
-
- if (buttonStr) {
- $.each(buttonStr.split(' '), function(i) {
- var groupChildren = $();
- var isOnlyButtons = true;
- var groupEl;
-
- $.each(this.split(','), function(j, buttonName) {
- var customButtonProps;
- var viewSpec;
- var buttonClick;
- var overrideText; // text explicitly set by calendar's constructor options. overcomes icons
- var defaultText;
- var themeIcon;
- var normalIcon;
- var innerHtml;
- var classes;
- var button; // the element
-
- if (buttonName == 'title') {
- groupChildren = groupChildren.add($(' ')); // we always want it to take up height
- isOnlyButtons = false;
- }
- else {
- if ((customButtonProps = (options.customButtons || {})[buttonName])) {
- buttonClick = function(ev) {
- if (customButtonProps.click) {
- customButtonProps.click.call(button[0], ev);
- }
- };
- overrideText = ''; // icons will override text
- defaultText = customButtonProps.text;
- }
- else if ((viewSpec = calendar.getViewSpec(buttonName))) {
- buttonClick = function() {
- calendar.changeView(buttonName);
- };
- viewsWithButtons.push(buttonName);
- overrideText = viewSpec.buttonTextOverride;
- defaultText = viewSpec.buttonTextDefault;
- }
- else if (calendar[buttonName]) { // a calendar method
- buttonClick = function() {
- calendar[buttonName]();
- };
- overrideText = (calendar.overrides.buttonText || {})[buttonName];
- defaultText = options.buttonText[buttonName]; // everything else is considered default
- }
-
- if (buttonClick) {
-
- themeIcon =
- customButtonProps ?
- customButtonProps.themeIcon :
- options.themeButtonIcons[buttonName];
-
- normalIcon =
- customButtonProps ?
- customButtonProps.icon :
- options.buttonIcons[buttonName];
-
- if (overrideText) {
- innerHtml = htmlEscape(overrideText);
- }
- else if (themeIcon && options.theme) {
- innerHtml = " ";
- }
- else if (normalIcon && !options.theme) {
- innerHtml = " ";
- }
- else {
- innerHtml = htmlEscape(defaultText);
- }
-
- classes = [
- 'fc-' + buttonName + '-button',
- tm + '-button',
- tm + '-state-default'
- ];
-
- button = $( // type="button" so that it doesn't submit a form
- '' +
- innerHtml +
- ' '
- )
- .click(function(ev) {
- // don't process clicks for disabled buttons
- if (!button.hasClass(tm + '-state-disabled')) {
-
- buttonClick(ev);
-
- // after the click action, if the button becomes the "active" tab, or disabled,
- // it should never have a hover class, so remove it now.
- if (
- button.hasClass(tm + '-state-active') ||
- button.hasClass(tm + '-state-disabled')
- ) {
- button.removeClass(tm + '-state-hover');
- }
- }
- })
- .mousedown(function() {
- // the *down* effect (mouse pressed in).
- // only on buttons that are not the "active" tab, or disabled
- button
- .not('.' + tm + '-state-active')
- .not('.' + tm + '-state-disabled')
- .addClass(tm + '-state-down');
- })
- .mouseup(function() {
- // undo the *down* effect
- button.removeClass(tm + '-state-down');
- })
- .hover(
- function() {
- // the *hover* effect.
- // only on buttons that are not the "active" tab, or disabled
- button
- .not('.' + tm + '-state-active')
- .not('.' + tm + '-state-disabled')
- .addClass(tm + '-state-hover');
- },
- function() {
- // undo the *hover* effect
- button
- .removeClass(tm + '-state-hover')
- .removeClass(tm + '-state-down'); // if mouseleave happens before mouseup
- }
- );
-
- groupChildren = groupChildren.add(button);
- }
- }
- });
-
- if (isOnlyButtons) {
- groupChildren
- .first().addClass(tm + '-corner-left').end()
- .last().addClass(tm + '-corner-right').end();
- }
-
- if (groupChildren.length > 1) {
- groupEl = $('
');
- if (isOnlyButtons) {
- groupEl.addClass('fc-button-group');
- }
- groupEl.append(groupChildren);
- sectionEl.append(groupEl);
- }
- else {
- sectionEl.append(groupChildren); // 1 or 0 children
- }
- });
- }
-
- return sectionEl;
- }
-
-
- function updateTitle(text) {
- if (el) {
- el.find('h2').text(text);
- }
- }
-
-
- function activateButton(buttonName) {
- if (el) {
- el.find('.fc-' + buttonName + '-button')
- .addClass(tm + '-state-active');
- }
- }
-
-
- function deactivateButton(buttonName) {
- if (el) {
- el.find('.fc-' + buttonName + '-button')
- .removeClass(tm + '-state-active');
- }
- }
-
-
- function disableButton(buttonName) {
- if (el) {
- el.find('.fc-' + buttonName + '-button')
- .prop('disabled', true)
- .addClass(tm + '-state-disabled');
- }
- }
-
-
- function enableButton(buttonName) {
- if (el) {
- el.find('.fc-' + buttonName + '-button')
- .prop('disabled', false)
- .removeClass(tm + '-state-disabled');
- }
- }
-
-
- function getViewsWithButtons() {
- return viewsWithButtons;
- }
-
-}
-
-;;
-
FC.sourceNormalizers = [];
FC.sourceFetchers = [];
@@ -10831,38 +11433,39 @@ var eventGUID = 1;
function EventManager() { // assumed to be a calendar
var t = this;
-
-
+
+
// exports
+ t.requestEvents = requestEvents;
+ t.reportEventChange = reportEventChange;
t.isFetchNeeded = isFetchNeeded;
t.fetchEvents = fetchEvents;
t.fetchEventSources = fetchEventSources;
+ t.refetchEvents = refetchEvents;
+ t.refetchEventSources = refetchEventSources;
t.getEventSources = getEventSources;
t.getEventSourceById = getEventSourceById;
- t.getEventSourcesByMatchArray = getEventSourcesByMatchArray;
- t.getEventSourcesByMatch = getEventSourcesByMatch;
t.addEventSource = addEventSource;
t.removeEventSource = removeEventSource;
t.removeEventSources = removeEventSources;
t.updateEvent = updateEvent;
+ t.updateEvents = updateEvents;
t.renderEvent = renderEvent;
+ t.renderEvents = renderEvents;
t.removeEvents = removeEvents;
t.clientEvents = clientEvents;
t.mutateEvent = mutateEvent;
t.normalizeEventDates = normalizeEventDates;
t.normalizeEventTimes = normalizeEventTimes;
-
-
- // imports
- var reportEvents = t.reportEvents;
-
-
+
+
// locals
var stickySource = { events: [] };
var sources = [ stickySource ];
var rangeStart, rangeEnd;
var pendingSourceCnt = 0; // outstanding fetch requests, max one per source
var cache = []; // holds events that have already been expanded
+ var prunedCache; // like cache, but only events that intersect with rangeStart/rangeEnd
$.each(
@@ -10874,9 +11477,55 @@ function EventManager() { // assumed to be a calendar
}
}
);
-
-
-
+
+
+
+ function requestEvents(start, end) {
+ if (!t.options.lazyFetching || isFetchNeeded(start, end)) {
+ return fetchEvents(start, end);
+ }
+ else {
+ return Promise.resolve(prunedCache);
+ }
+ }
+
+
+ function reportEventChange() {
+ prunedCache = filterEventsWithinRange(cache);
+ t.trigger('eventsReset', prunedCache);
+ }
+
+
+ function filterEventsWithinRange(events) {
+ var filteredEvents = [];
+ var i, event;
+
+ for (i = 0; i < events.length; i++) {
+ event = events[i];
+
+ if (
+ event.start.clone().stripZone() < rangeEnd &&
+ t.getEventEnd(event).stripZone() > rangeStart
+ ) {
+ filteredEvents.push(event);
+ }
+ }
+
+ return filteredEvents;
+ }
+
+
+ t.getEventCache = function() {
+ return cache;
+ };
+
+
+ t.getPrunedEventCache = function() {
+ return prunedCache;
+ };
+
+
+
/* Fetching
-----------------------------------------------------------------------------*/
@@ -10886,12 +11535,24 @@ function EventManager() { // assumed to be a calendar
return !rangeStart || // nothing has been fetched yet?
start < rangeStart || end > rangeEnd; // is part of the new range outside of the old range?
}
-
-
+
+
function fetchEvents(start, end) {
rangeStart = start;
rangeEnd = end;
- fetchEventSources(sources, 'reset');
+ return refetchEvents();
+ }
+
+
+ // poorly named. fetches all sources with current `rangeStart` and `rangeEnd`.
+ function refetchEvents() {
+ return fetchEventSources(sources, 'reset');
+ }
+
+
+ // poorly named. fetches a subset of event sources.
+ function refetchEventSources(matchInputs) {
+ return fetchEventSources(getEventSourcesByMatchArray(matchInputs));
}
@@ -10921,9 +11582,17 @@ function EventManager() { // assumed to be a calendar
for (i = 0; i < specificSources.length; i++) {
source = specificSources[i];
-
tryFetchEventSource(source, source._fetchId);
}
+
+ if (pendingSourceCnt) {
+ return new Promise(function(resolve) {
+ t.one('eventsReceived', resolve); // will send prunedCache
+ });
+ }
+ else { // executed all synchronously, or no sources at all
+ return Promise.resolve(prunedCache);
+ }
}
@@ -10956,7 +11625,7 @@ function EventManager() { // assumed to be a calendar
}
if (abstractEvent) { // not false (an invalid event)
- cache.push.apply(
+ cache.push.apply( // append
cache,
expandEvent(abstractEvent) // add individual expanded events to the cache
);
@@ -10984,11 +11653,12 @@ function EventManager() { // assumed to be a calendar
function decrementPendingSourceCnt() {
pendingSourceCnt--;
if (!pendingSourceCnt) {
- reportEvents(cache);
+ reportEventChange(cache); // updates prunedCache
+ t.trigger('eventsReceived', prunedCache);
}
}
-
-
+
+
function _fetchEventSource(source, callback) {
var i;
var fetchers = FC.sourceFetchers;
@@ -11097,9 +11767,9 @@ function EventManager() { // assumed to be a calendar
}
}
}
-
-
-
+
+
+
/* Sources
-----------------------------------------------------------------------------*/
@@ -11108,7 +11778,7 @@ function EventManager() { // assumed to be a calendar
var source = buildEventSource(sourceInput);
if (source) {
sources.push(source);
- fetchEventSources([ source ], 'add'); // will eventually call reportEvents
+ fetchEventSources([ source ], 'add'); // will eventually call reportEventChange
}
}
@@ -11204,7 +11874,7 @@ function EventManager() { // assumed to be a calendar
cache = excludeEventsBySources(cache, targetSources);
}
- reportEvents(cache);
+ reportEventChange();
}
@@ -11298,27 +11968,39 @@ function EventManager() { // assumed to be a calendar
return true; // keep
});
}
-
-
-
+
+
+
/* Manipulation
-----------------------------------------------------------------------------*/
// Only ever called from the externally-facing API
function updateEvent(event) {
+ updateEvents([ event ]);
+ }
- // massage start/end values, even if date string values
- event.start = t.moment(event.start);
- if (event.end) {
- event.end = t.moment(event.end);
- }
- else {
- event.end = null;
+
+ // Only ever called from the externally-facing API
+ function updateEvents(events) {
+ var i, event;
+
+ for (i = 0; i < events.length; i++) {
+ event = events[i];
+
+ // massage start/end values, even if date string values
+ event.start = t.moment(event.start);
+ if (event.end) {
+ event.end = t.moment(event.end);
+ }
+ else {
+ event.end = null;
+ }
+
+ mutateEvent(event, getMiscEventProps(event)); // will handle start/end/allDay normalization
}
- mutateEvent(event, getMiscEventProps(event)); // will handle start/end/allDay normalization
- reportEvents(cache); // reports event modifications (so we can redraw)
+ reportEventChange(); // reports event modifications (so we can redraw)
}
@@ -11342,37 +12024,50 @@ function EventManager() { // assumed to be a calendar
return !/^_|^(id|allDay|start|end)$/.test(name);
}
-
+
// returns the expanded events that were created
function renderEvent(eventInput, stick) {
- var abstractEvent = buildEventFromInput(eventInput);
- var events;
- var i, event;
+ return renderEvents([ eventInput ], stick);
+ }
- if (abstractEvent) { // not false (a valid input)
- events = expandEvent(abstractEvent);
- for (i = 0; i < events.length; i++) {
- event = events[i];
+ // returns the expanded events that were created
+ function renderEvents(eventInputs, stick) {
+ var renderedEvents = [];
+ var renderableEvents;
+ var abstractEvent;
+ var i, j, event;
- if (!event.source) {
- if (stick) {
- stickySource.events.push(event);
- event.source = stickySource;
+ for (i = 0; i < eventInputs.length; i++) {
+ abstractEvent = buildEventFromInput(eventInputs[i]);
+
+ if (abstractEvent) { // not false (a valid input)
+ renderableEvents = expandEvent(abstractEvent);
+
+ for (j = 0; j < renderableEvents.length; j++) {
+ event = renderableEvents[j];
+
+ if (!event.source) {
+ if (stick) {
+ stickySource.events.push(event);
+ event.source = stickySource;
+ }
+ cache.push(event);
}
- cache.push(event);
}
+
+ renderedEvents = renderedEvents.concat(renderableEvents);
}
-
- reportEvents(cache);
-
- return events;
}
- return [];
+ if (renderedEvents.length) { // any new events rendered?
+ reportEventChange();
+ }
+
+ return renderedEvents;
}
-
-
+
+
function removeEvents(filter) {
var eventID;
var i;
@@ -11399,10 +12094,10 @@ function EventManager() { // assumed to be a calendar
}
}
- reportEvents(cache);
+ reportEventChange();
}
-
+
function clientEvents(filter) {
if ($.isFunction(filter)) {
return $.grep(cache, filter);
@@ -11442,8 +12137,8 @@ function EventManager() { // assumed to be a calendar
}
backupEventDates(event);
}
-
-
+
+
/* Event Normalization
-----------------------------------------------------------------------------*/
@@ -11853,11 +12548,6 @@ function EventManager() { // assumed to be a calendar
};
}
-
- t.getEventCache = function() {
- return cache;
- };
-
}
@@ -12386,13 +13076,18 @@ var BasicView = FC.BasicView = View.extend({
------------------------------------------------------------------------------------------------------------------*/
- queryScroll: function() {
- return this.scroller.getScrollTop();
+ computeInitialScroll: function() {
+ return { top: 0 };
},
- setScroll: function(top) {
- this.scroller.setScrollTop(top);
+ queryScroll: function() {
+ return { top: this.scroller.getScrollTop() };
+ },
+
+
+ setScroll: function(scroll) {
+ this.scroller.setScrollTop(scroll.top);
},
@@ -12909,17 +13604,17 @@ var AgendaView = FC.AgendaView = View.extend({
top++; // to overcome top border that slots beyond the first have. looks better
}
- return top;
+ return { top: top };
},
queryScroll: function() {
- return this.scroller.getScrollTop();
+ return { top: this.scroller.getScrollTop() };
},
- setScroll: function(top) {
- this.scroller.setScrollTop(top);
+ setScroll: function(scroll) {
+ this.scroller.setScrollTop(scroll.top);
},
diff --git a/library/fullcalendar/fullcalendar.min.css b/library/fullcalendar/fullcalendar.min.css
index 87ee16e69..1339120b4 100644
--- a/library/fullcalendar/fullcalendar.min.css
+++ b/library/fullcalendar/fullcalendar.min.css
@@ -1,5 +1,5 @@
/*!
- * FullCalendar v3.0.1 Stylesheet
+ * FullCalendar v3.1.0 Stylesheet
* Docs & License: http://fullcalendar.io/
* (c) 2016 Adam Shaw
- */.fc-icon,body .fc{font-size:1em}.fc-button-group,.fc-icon{display:inline-block}.fc-bg,.fc-row .fc-bgevent-skeleton,.fc-row .fc-highlight-skeleton{bottom:0}.fc-icon,.fc-unselectable{-khtml-user-select:none;-webkit-touch-callout:none}.fc{direction:ltr;text-align:left}.fc-rtl{text-align:right}.fc th,.fc-basic-view td.fc-week-number,.fc-icon,.fc-toolbar{text-align:center}.fc-unthemed .fc-content,.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-list-view,.fc-unthemed .fc-popover,.fc-unthemed .fc-row,.fc-unthemed tbody,.fc-unthemed td,.fc-unthemed th,.fc-unthemed thead{border-color:#ddd}.fc-unthemed .fc-popover{background-color:#fff}.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-popover .fc-header{background:#eee}.fc-unthemed .fc-popover .fc-header .fc-close{color:#666}.fc-unthemed .fc-today{background:#fcf8e3}.fc-highlight{background:#bce8f1;opacity:.3}.fc-bgevent{background:#8fdf82;opacity:.3}.fc-nonbusiness{background:#d7d7d7}.fc-icon{height:1em;line-height:1em;overflow:hidden;font-family:"Courier New",Courier,monospace;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fc-icon:after{position:relative}.fc-icon-left-single-arrow:after{content:"\02039";font-weight:700;font-size:200%;top:-7%}.fc-icon-right-single-arrow:after{content:"\0203A";font-weight:700;font-size:200%;top:-7%}.fc-icon-left-double-arrow:after{content:"\000AB";font-size:160%;top:-7%}.fc-icon-right-double-arrow:after{content:"\000BB";font-size:160%;top:-7%}.fc-icon-left-triangle:after{content:"\25C4";font-size:125%;top:3%}.fc-icon-right-triangle:after{content:"\25BA";font-size:125%;top:3%}.fc-icon-down-triangle:after{content:"\25BC";font-size:125%;top:2%}.fc-icon-x:after{content:"\000D7";font-size:200%;top:6%}.fc button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;height:2.1em;padding:0 .6em;font-size:1em;white-space:nowrap;cursor:pointer}.fc button::-moz-focus-inner{margin:0;padding:0}.fc-state-default{border:1px solid;background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.fc-state-default.fc-corner-left{border-top-left-radius:4px;border-bottom-left-radius:4px}.fc-state-default.fc-corner-right{border-top-right-radius:4px;border-bottom-right-radius:4px}.fc button .fc-icon{position:relative;top:-.05em;margin:0 .2em;vertical-align:middle}.fc-state-active,.fc-state-disabled,.fc-state-down,.fc-state-hover{color:#333;background-color:#e6e6e6}.fc-state-hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.fc-state-active,.fc-state-down{background-color:#ccc;background-image:none;box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.fc-state-disabled{cursor:default;background-image:none;opacity:.65;box-shadow:none}.fc-event.fc-draggable,.fc-event[href],.fc-popover .fc-header .fc-close,a[data-goto]{cursor:pointer}.fc .fc-button-group>*{float:left;margin:0 0 0 -1px}.fc .fc-button-group>:first-child{margin-left:0}.fc-popover{position:absolute;box-shadow:0 2px 6px rgba(0,0,0,.15)}.fc-popover .fc-header{padding:2px 4px}.fc-popover .fc-header .fc-title{margin:0 2px}.fc-ltr .fc-popover .fc-header .fc-title,.fc-rtl .fc-popover .fc-header .fc-close{float:left}.fc-ltr .fc-popover .fc-header .fc-close,.fc-rtl .fc-popover .fc-header .fc-title{float:right}.fc-unthemed .fc-popover{border-width:1px;border-style:solid}.fc-unthemed .fc-popover .fc-header .fc-close{font-size:.9em;margin-top:2px}.fc-popover>.ui-widget-header+.ui-widget-content{border-top:0}.fc-divider{border-style:solid;border-width:1px}hr.fc-divider{height:0;margin:0;padding:0 0 2px;border-width:1px 0}.fc-bg table,.fc-row .fc-bgevent-skeleton table,.fc-row .fc-highlight-skeleton table{height:100%}.fc-clear{clear:both}.fc-bg,.fc-bgevent-skeleton,.fc-helper-skeleton,.fc-highlight-skeleton{position:absolute;top:0;left:0;right:0}.fc table{width:100%;box-sizing:border-box;table-layout:fixed;border-collapse:collapse;border-spacing:0;font-size:1em}.fc td,.fc th{border-style:solid;border-width:1px;padding:0;vertical-align:top}.fc td.fc-today{border-style:double}a[data-goto]:hover{text-decoration:underline}.fc .fc-row{border-style:solid;border-width:0}.fc-row table{border-left:0 hidden transparent;border-right:0 hidden transparent;border-bottom:0 hidden transparent}.fc-row:first-child table{border-top:0 hidden transparent}.fc-row{position:relative}.fc-row .fc-bg{z-index:1}.fc-row .fc-bgevent-skeleton td,.fc-row .fc-highlight-skeleton td{border-color:transparent}.fc-row .fc-bgevent-skeleton{z-index:2}.fc-row .fc-highlight-skeleton{z-index:3}.fc-row .fc-content-skeleton{position:relative;z-index:4;padding-bottom:2px}.fc-row .fc-helper-skeleton{z-index:5}.fc-row .fc-content-skeleton td,.fc-row .fc-helper-skeleton td{background:0 0;border-color:transparent;border-bottom:0}.fc-row .fc-content-skeleton tbody td,.fc-row .fc-helper-skeleton tbody td{border-top:0}.fc-scroller{-webkit-overflow-scrolling:touch}.fc-row.fc-rigid,.fc-time-grid-event{overflow:hidden}.fc-scroller>.fc-day-grid,.fc-scroller>.fc-time-grid{position:relative;width:100%}.fc-event{position:relative;display:block;font-size:.85em;line-height:1.3;border-radius:3px;border:1px solid #3a87ad;font-weight:400}.fc-event,.fc-event-dot{background-color:#3a87ad}.fc-event,.fc-event:hover,.ui-widget .fc-event{color:#fff;text-decoration:none}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}.fc-event .fc-bg{z-index:1;background:#fff;opacity:.25}.fc-event .fc-content{position:relative;z-index:2}.fc-event .fc-resizer{position:absolute;z-index:4;display:none}.fc-event.fc-allow-mouse-resize .fc-resizer,.fc-event.fc-selected .fc-resizer{display:block}.fc-event.fc-selected .fc-resizer:before{content:"";position:absolute;z-index:9999;top:50%;left:50%;width:40px;height:40px;margin-left:-20px;margin-top:-20px}.fc-event.fc-selected{z-index:9999!important;box-shadow:0 2px 5px rgba(0,0,0,.2)}.fc-event.fc-selected.fc-dragging{box-shadow:0 2px 7px rgba(0,0,0,.3)}.fc-h-event.fc-selected:before{content:"";position:absolute;z-index:3;top:-10px;bottom:-10px;left:0;right:0}.fc-ltr .fc-h-event.fc-not-start,.fc-rtl .fc-h-event.fc-not-end{margin-left:0;border-left-width:0;padding-left:1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-ltr .fc-h-event.fc-not-end,.fc-rtl .fc-h-event.fc-not-start{margin-right:0;border-right-width:0;padding-right:1px;border-top-right-radius:0;border-bottom-right-radius:0}.fc-ltr .fc-h-event .fc-start-resizer,.fc-rtl .fc-h-event .fc-end-resizer{cursor:w-resize;left:-1px}.fc-ltr .fc-h-event .fc-end-resizer,.fc-rtl .fc-h-event .fc-start-resizer{cursor:e-resize;right:-1px}.fc-h-event.fc-allow-mouse-resize .fc-resizer{width:7px;top:-1px;bottom:-1px}.fc-h-event.fc-selected .fc-resizer{border-radius:4px;border-width:1px;width:6px;height:6px;border-style:solid;border-color:inherit;background:#fff;top:50%;margin-top:-4px}.fc-ltr .fc-h-event.fc-selected .fc-start-resizer,.fc-rtl .fc-h-event.fc-selected .fc-end-resizer{margin-left:-4px}.fc-ltr .fc-h-event.fc-selected .fc-end-resizer,.fc-rtl .fc-h-event.fc-selected .fc-start-resizer{margin-right:-4px}.fc-day-grid-event{margin:1px 2px 0;padding:0 1px}tr:first-child>td>.fc-day-grid-event{margin-top:2px}.fc-day-grid-event.fc-selected:after{content:"";position:absolute;z-index:1;top:-1px;right:-1px;bottom:-1px;left:-1px;background:#000;opacity:.25}.fc-day-grid-event .fc-content{white-space:nowrap;overflow:hidden}.fc-day-grid-event .fc-time{font-weight:700}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer{margin-left:-2px}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer{margin-right:-2px}a.fc-more{margin:1px 3px;font-size:.85em;cursor:pointer;text-decoration:none}a.fc-more:hover{text-decoration:underline}.fc-limited{display:none}.fc-day-grid .fc-row{z-index:1}.fc-more-popover{z-index:2;width:220px}.fc-more-popover .fc-event-container{padding:10px}.fc-now-indicator{position:absolute;border:0 solid red}.fc-unselectable{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.fc-toolbar{margin-bottom:1em}.fc-toolbar .fc-left{float:left}.fc-toolbar .fc-right{float:right}.fc-toolbar .fc-center{display:inline-block}.fc .fc-toolbar>*>*{float:left;margin-left:.75em}.fc .fc-toolbar>*>:first-child{margin-left:0}.fc-toolbar h2{margin:0}.fc-toolbar button{position:relative}.fc-toolbar .fc-state-hover,.fc-toolbar .ui-state-hover{z-index:2}.fc-toolbar .fc-state-down{z-index:3}.fc-toolbar .fc-state-active,.fc-toolbar .ui-state-active{z-index:4}.fc-toolbar button:focus{z-index:5}.fc-view-container *,.fc-view-container :after,.fc-view-container :before{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fc-view,.fc-view>table{position:relative;z-index:1}.fc-basicDay-view .fc-content-skeleton,.fc-basicWeek-view .fc-content-skeleton{padding-bottom:1em}.fc-basic-view .fc-body .fc-row{min-height:4em}.fc-row.fc-rigid .fc-content-skeleton{position:absolute;top:0;left:0;right:0}.fc-day-top.fc-other-month{opacity:.3}.fc-basic-view .fc-day-number,.fc-basic-view .fc-week-number{padding:2px}.fc-basic-view th.fc-day-number,.fc-basic-view th.fc-week-number{padding:0 2px}.fc-ltr .fc-basic-view .fc-day-top .fc-day-number{float:right}.fc-rtl .fc-basic-view .fc-day-top .fc-day-number{float:left}.fc-ltr .fc-basic-view .fc-day-top .fc-week-number{float:left;border-radius:0 0 3px}.fc-rtl .fc-basic-view .fc-day-top .fc-week-number{float:right;border-radius:0 0 0 3px}.fc-basic-view .fc-day-top .fc-week-number{min-width:1.5em;text-align:center;background-color:#f2f2f2;color:grey}.fc-basic-view td.fc-week-number>*{display:inline-block;min-width:1.25em}.fc-agenda-view .fc-day-grid{position:relative;z-index:2}.fc-agenda-view .fc-day-grid .fc-row{min-height:3em}.fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton{padding-bottom:1em}.fc .fc-axis{vertical-align:middle;padding:0 4px;white-space:nowrap}.fc-ltr .fc-axis{text-align:right}.fc-rtl .fc-axis{text-align:left}.ui-widget td.fc-axis{font-weight:400}.fc-time-grid,.fc-time-grid-container{position:relative;z-index:1}.fc-time-grid{min-height:100%}.fc-time-grid table{border:0 hidden transparent}.fc-time-grid>.fc-bg{z-index:1}.fc-time-grid .fc-slats,.fc-time-grid>hr{position:relative;z-index:2}.fc-time-grid .fc-content-col{position:relative}.fc-time-grid .fc-content-skeleton{position:absolute;z-index:3;top:0;left:0;right:0}.fc-time-grid .fc-business-container{position:relative;z-index:1}.fc-time-grid .fc-bgevent-container{position:relative;z-index:2}.fc-time-grid .fc-highlight-container{z-index:3;position:relative}.fc-time-grid .fc-event-container{position:relative;z-index:4}.fc-time-grid .fc-now-indicator-line{z-index:5}.fc-time-grid .fc-helper-container{position:relative;z-index:6}.fc-time-grid .fc-slats td{height:1.5em;border-bottom:0}.fc-time-grid .fc-slats .fc-minor td{border-top-style:dotted}.fc-time-grid .fc-slats .ui-widget-content{background:0 0}.fc-time-grid .fc-highlight{position:absolute;left:0;right:0}.fc-ltr .fc-time-grid .fc-event-container{margin:0 2.5% 0 2px}.fc-rtl .fc-time-grid .fc-event-container{margin:0 2px 0 2.5%}.fc-time-grid .fc-bgevent,.fc-time-grid .fc-event{position:absolute;z-index:1}.fc-time-grid .fc-bgevent{left:0;right:0}.fc-v-event.fc-not-start{border-top-width:0;padding-top:1px;border-top-left-radius:0;border-top-right-radius:0}.fc-v-event.fc-not-end{border-bottom-width:0;padding-bottom:1px;border-bottom-left-radius:0;border-bottom-right-radius:0}.fc-time-grid-event.fc-selected{overflow:visible}.fc-time-grid-event.fc-selected .fc-bg{display:none}.fc-time-grid-event .fc-content{overflow:hidden}.fc-time-grid-event .fc-time,.fc-time-grid-event .fc-title{padding:0 1px}.fc-time-grid-event .fc-time{font-size:.85em;white-space:nowrap}.fc-time-grid-event.fc-short .fc-content{white-space:nowrap}.fc-time-grid-event.fc-short .fc-time,.fc-time-grid-event.fc-short .fc-title{display:inline-block;vertical-align:top}.fc-time-grid-event.fc-short .fc-time span{display:none}.fc-time-grid-event.fc-short .fc-time:before{content:attr(data-start)}.fc-time-grid-event.fc-short .fc-time:after{content:"\000A0-\000A0"}.fc-time-grid-event.fc-short .fc-title{font-size:.85em;padding:0}.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer{left:0;right:0;bottom:0;height:8px;overflow:hidden;line-height:8px;font-size:11px;font-family:monospace;text-align:center;cursor:s-resize}.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer:after{content:"="}.fc-time-grid-event.fc-selected .fc-resizer{border-radius:5px;border-width:1px;width:8px;height:8px;border-style:solid;border-color:inherit;background:#fff;left:50%;margin-left:-5px;bottom:-5px}.fc-time-grid .fc-now-indicator-line{border-top-width:1px;left:0;right:0}.fc-time-grid .fc-now-indicator-arrow{margin-top:-5px}.fc-ltr .fc-time-grid .fc-now-indicator-arrow{left:0;border-width:5px 0 5px 6px;border-top-color:transparent;border-bottom-color:transparent}.fc-rtl .fc-time-grid .fc-now-indicator-arrow{right:0;border-width:5px 6px 5px 0;border-top-color:transparent;border-bottom-color:transparent}.fc-event-dot{display:inline-block;width:10px;height:10px;border-radius:5px}.fc-rtl .fc-list-view{direction:rtl}.fc-list-view{border-width:1px;border-style:solid}.fc .fc-list-table{table-layout:auto}.fc-list-table td{border-width:1px 0 0;padding:8px 14px}.fc-list-table tr:first-child td{border-top-width:0}.fc-list-heading{border-bottom-width:1px}.fc-list-heading td{font-weight:700}.fc-ltr .fc-list-heading-main{float:left}.fc-ltr .fc-list-heading-alt,.fc-rtl .fc-list-heading-main{float:right}.fc-rtl .fc-list-heading-alt{float:left}.fc-list-item.fc-has-url{cursor:pointer}.fc-list-item:hover td{background-color:#f5f5f5}.fc-list-item-marker,.fc-list-item-time{white-space:nowrap;width:1px}.fc-ltr .fc-list-item-marker{padding-right:0}.fc-rtl .fc-list-item-marker{padding-left:0}.fc-list-item-title a{text-decoration:none;color:inherit}.fc-list-item-title a[href]:hover{text-decoration:underline}.fc-list-empty-wrap2{position:absolute;top:0;left:0;right:0;bottom:0}.fc-list-empty-wrap1{width:100%;height:100%;display:table}.fc-list-empty{display:table-cell;vertical-align:middle;text-align:center}.fc-unthemed .fc-list-empty{background-color:#eee}
\ No newline at end of file
+ */.fc-icon,body .fc{font-size:1em}.fc-button-group,.fc-icon{display:inline-block}.fc-bg,.fc-row .fc-bgevent-skeleton,.fc-row .fc-highlight-skeleton{bottom:0}.fc-icon,.fc-unselectable{-khtml-user-select:none;-webkit-touch-callout:none}.fc{direction:ltr;text-align:left}.fc-rtl{text-align:right}.fc th,.fc-basic-view td.fc-week-number,.fc-icon,.fc-toolbar{text-align:center}.fc-unthemed .fc-content,.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-list-view,.fc-unthemed .fc-popover,.fc-unthemed .fc-row,.fc-unthemed tbody,.fc-unthemed td,.fc-unthemed th,.fc-unthemed thead{border-color:#ddd}.fc-unthemed .fc-popover{background-color:#fff}.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-popover .fc-header{background:#eee}.fc-unthemed .fc-popover .fc-header .fc-close{color:#666}.fc-unthemed td.fc-today{background:#fcf8e3}.fc-highlight{background:#bce8f1;opacity:.3}.fc-bgevent{background:#8fdf82;opacity:.3}.fc-nonbusiness{background:#d7d7d7}.fc-icon{height:1em;line-height:1em;overflow:hidden;font-family:"Courier New",Courier,monospace;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fc-icon:after{position:relative}.fc-icon-left-single-arrow:after{content:"\02039";font-weight:700;font-size:200%;top:-7%}.fc-icon-right-single-arrow:after{content:"\0203A";font-weight:700;font-size:200%;top:-7%}.fc-icon-left-double-arrow:after{content:"\000AB";font-size:160%;top:-7%}.fc-icon-right-double-arrow:after{content:"\000BB";font-size:160%;top:-7%}.fc-icon-left-triangle:after{content:"\25C4";font-size:125%;top:3%}.fc-icon-right-triangle:after{content:"\25BA";font-size:125%;top:3%}.fc-icon-down-triangle:after{content:"\25BC";font-size:125%;top:2%}.fc-icon-x:after{content:"\000D7";font-size:200%;top:6%}.fc button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;height:2.1em;padding:0 .6em;font-size:1em;white-space:nowrap;cursor:pointer}.fc button::-moz-focus-inner{margin:0;padding:0}.fc-state-default{border:1px solid;background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.fc-state-default.fc-corner-left{border-top-left-radius:4px;border-bottom-left-radius:4px}.fc-state-default.fc-corner-right{border-top-right-radius:4px;border-bottom-right-radius:4px}.fc button .fc-icon{position:relative;top:-.05em;margin:0 .2em;vertical-align:middle}.fc-state-active,.fc-state-disabled,.fc-state-down,.fc-state-hover{color:#333;background-color:#e6e6e6}.fc-state-hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.fc-state-active,.fc-state-down{background-color:#ccc;background-image:none;box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.fc-state-disabled{cursor:default;background-image:none;opacity:.65;box-shadow:none}.fc-event.fc-draggable,.fc-event[href],.fc-popover .fc-header .fc-close,a[data-goto]{cursor:pointer}.fc .fc-button-group>*{float:left;margin:0 0 0 -1px}.fc .fc-button-group>:first-child{margin-left:0}.fc-popover{position:absolute;box-shadow:0 2px 6px rgba(0,0,0,.15)}.fc-popover .fc-header{padding:2px 4px}.fc-popover .fc-header .fc-title{margin:0 2px}.fc-ltr .fc-popover .fc-header .fc-title,.fc-rtl .fc-popover .fc-header .fc-close{float:left}.fc-ltr .fc-popover .fc-header .fc-close,.fc-rtl .fc-popover .fc-header .fc-title{float:right}.fc-unthemed .fc-popover{border-width:1px;border-style:solid}.fc-unthemed .fc-popover .fc-header .fc-close{font-size:.9em;margin-top:2px}.fc-popover>.ui-widget-header+.ui-widget-content{border-top:0}.fc-divider{border-style:solid;border-width:1px}hr.fc-divider{height:0;margin:0;padding:0 0 2px;border-width:1px 0}.fc-bg table,.fc-row .fc-bgevent-skeleton table,.fc-row .fc-highlight-skeleton table{height:100%}.fc-clear{clear:both}.fc-bg,.fc-bgevent-skeleton,.fc-helper-skeleton,.fc-highlight-skeleton{position:absolute;top:0;left:0;right:0}.fc table{width:100%;box-sizing:border-box;table-layout:fixed;border-collapse:collapse;border-spacing:0;font-size:1em}.fc td,.fc th{border-style:solid;border-width:1px;padding:0;vertical-align:top}.fc td.fc-today{border-style:double}a[data-goto]:hover{text-decoration:underline}.fc .fc-row{border-style:solid;border-width:0}.fc-row table{border-left:0 hidden transparent;border-right:0 hidden transparent;border-bottom:0 hidden transparent}.fc-row:first-child table{border-top:0 hidden transparent}.fc-row{position:relative}.fc-row .fc-bg{z-index:1}.fc-row .fc-bgevent-skeleton td,.fc-row .fc-highlight-skeleton td{border-color:transparent}.fc-row .fc-bgevent-skeleton{z-index:2}.fc-row .fc-highlight-skeleton{z-index:3}.fc-row .fc-content-skeleton{position:relative;z-index:4;padding-bottom:2px}.fc-row .fc-helper-skeleton{z-index:5}.fc-row .fc-content-skeleton td,.fc-row .fc-helper-skeleton td{background:0 0;border-color:transparent;border-bottom:0}.fc-row .fc-content-skeleton tbody td,.fc-row .fc-helper-skeleton tbody td{border-top:0}.fc-scroller{-webkit-overflow-scrolling:touch}.fc-row.fc-rigid,.fc-time-grid-event{overflow:hidden}.fc-scroller>.fc-day-grid,.fc-scroller>.fc-time-grid{position:relative;width:100%}.fc-event{position:relative;display:block;font-size:.85em;line-height:1.3;border-radius:3px;border:1px solid #3a87ad;font-weight:400}.fc-event,.fc-event-dot{background-color:#3a87ad}.fc-event,.fc-event:hover,.ui-widget .fc-event{color:#fff;text-decoration:none}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}.fc-event .fc-bg{z-index:1;background:#fff;opacity:.25}.fc-event .fc-content{position:relative;z-index:2}.fc-event .fc-resizer{position:absolute;z-index:4;display:none}.fc-event.fc-allow-mouse-resize .fc-resizer,.fc-event.fc-selected .fc-resizer{display:block}.fc-event.fc-selected .fc-resizer:before{content:"";position:absolute;z-index:9999;top:50%;left:50%;width:40px;height:40px;margin-left:-20px;margin-top:-20px}.fc-event.fc-selected{z-index:9999!important;box-shadow:0 2px 5px rgba(0,0,0,.2)}.fc-event.fc-selected.fc-dragging{box-shadow:0 2px 7px rgba(0,0,0,.3)}.fc-h-event.fc-selected:before{content:"";position:absolute;z-index:3;top:-10px;bottom:-10px;left:0;right:0}.fc-ltr .fc-h-event.fc-not-start,.fc-rtl .fc-h-event.fc-not-end{margin-left:0;border-left-width:0;padding-left:1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-ltr .fc-h-event.fc-not-end,.fc-rtl .fc-h-event.fc-not-start{margin-right:0;border-right-width:0;padding-right:1px;border-top-right-radius:0;border-bottom-right-radius:0}.fc-ltr .fc-h-event .fc-start-resizer,.fc-rtl .fc-h-event .fc-end-resizer{cursor:w-resize;left:-1px}.fc-ltr .fc-h-event .fc-end-resizer,.fc-rtl .fc-h-event .fc-start-resizer{cursor:e-resize;right:-1px}.fc-h-event.fc-allow-mouse-resize .fc-resizer{width:7px;top:-1px;bottom:-1px}.fc-h-event.fc-selected .fc-resizer{border-radius:4px;border-width:1px;width:6px;height:6px;border-style:solid;border-color:inherit;background:#fff;top:50%;margin-top:-4px}.fc-ltr .fc-h-event.fc-selected .fc-start-resizer,.fc-rtl .fc-h-event.fc-selected .fc-end-resizer{margin-left:-4px}.fc-ltr .fc-h-event.fc-selected .fc-end-resizer,.fc-rtl .fc-h-event.fc-selected .fc-start-resizer{margin-right:-4px}.fc-day-grid-event{margin:1px 2px 0;padding:0 1px}tr:first-child>td>.fc-day-grid-event{margin-top:2px}.fc-day-grid-event.fc-selected:after{content:"";position:absolute;z-index:1;top:-1px;right:-1px;bottom:-1px;left:-1px;background:#000;opacity:.25}.fc-day-grid-event .fc-content{white-space:nowrap;overflow:hidden}.fc-day-grid-event .fc-time{font-weight:700}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer{margin-left:-2px}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer{margin-right:-2px}a.fc-more{margin:1px 3px;font-size:.85em;cursor:pointer;text-decoration:none}a.fc-more:hover{text-decoration:underline}.fc-limited{display:none}.fc-day-grid .fc-row{z-index:1}.fc-more-popover{z-index:2;width:220px}.fc-more-popover .fc-event-container{padding:10px}.fc-now-indicator{position:absolute;border:0 solid red}.fc-unselectable{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.fc-toolbar.fc-header-toolbar{margin-bottom:1em}.fc-toolbar.fc-footer-toolbar{margin-top:1em}.fc-toolbar .fc-left{float:left}.fc-toolbar .fc-right{float:right}.fc-toolbar .fc-center{display:inline-block}.fc .fc-toolbar>*>*{float:left;margin-left:.75em}.fc .fc-toolbar>*>:first-child{margin-left:0}.fc-toolbar h2{margin:0}.fc-toolbar button{position:relative}.fc-toolbar .fc-state-hover,.fc-toolbar .ui-state-hover{z-index:2}.fc-toolbar .fc-state-down{z-index:3}.fc-toolbar .fc-state-active,.fc-toolbar .ui-state-active{z-index:4}.fc-toolbar button:focus{z-index:5}.fc-view-container *,.fc-view-container :after,.fc-view-container :before{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fc-view,.fc-view>table{position:relative;z-index:1}.fc-basicDay-view .fc-content-skeleton,.fc-basicWeek-view .fc-content-skeleton{padding-bottom:1em}.fc-basic-view .fc-body .fc-row{min-height:4em}.fc-row.fc-rigid .fc-content-skeleton{position:absolute;top:0;left:0;right:0}.fc-day-top.fc-other-month{opacity:.3}.fc-basic-view .fc-day-number,.fc-basic-view .fc-week-number{padding:2px}.fc-basic-view th.fc-day-number,.fc-basic-view th.fc-week-number{padding:0 2px}.fc-ltr .fc-basic-view .fc-day-top .fc-day-number{float:right}.fc-rtl .fc-basic-view .fc-day-top .fc-day-number{float:left}.fc-ltr .fc-basic-view .fc-day-top .fc-week-number{float:left;border-radius:0 0 3px}.fc-rtl .fc-basic-view .fc-day-top .fc-week-number{float:right;border-radius:0 0 0 3px}.fc-basic-view .fc-day-top .fc-week-number{min-width:1.5em;text-align:center;background-color:#f2f2f2;color:grey}.fc-basic-view td.fc-week-number>*{display:inline-block;min-width:1.25em}.fc-agenda-view .fc-day-grid{position:relative;z-index:2}.fc-agenda-view .fc-day-grid .fc-row{min-height:3em}.fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton{padding-bottom:1em}.fc .fc-axis{vertical-align:middle;padding:0 4px;white-space:nowrap}.fc-ltr .fc-axis{text-align:right}.fc-rtl .fc-axis{text-align:left}.ui-widget td.fc-axis{font-weight:400}.fc-time-grid,.fc-time-grid-container{position:relative;z-index:1}.fc-time-grid{min-height:100%}.fc-time-grid table{border:0 hidden transparent}.fc-time-grid>.fc-bg{z-index:1}.fc-time-grid .fc-slats,.fc-time-grid>hr{position:relative;z-index:2}.fc-time-grid .fc-content-col{position:relative}.fc-time-grid .fc-content-skeleton{position:absolute;z-index:3;top:0;left:0;right:0}.fc-time-grid .fc-business-container{position:relative;z-index:1}.fc-time-grid .fc-bgevent-container{position:relative;z-index:2}.fc-time-grid .fc-highlight-container{z-index:3;position:relative}.fc-time-grid .fc-event-container{position:relative;z-index:4}.fc-time-grid .fc-now-indicator-line{z-index:5}.fc-time-grid .fc-helper-container{position:relative;z-index:6}.fc-time-grid .fc-slats td{height:1.5em;border-bottom:0}.fc-time-grid .fc-slats .fc-minor td{border-top-style:dotted}.fc-time-grid .fc-slats .ui-widget-content{background:0 0}.fc-time-grid .fc-highlight{position:absolute;left:0;right:0}.fc-ltr .fc-time-grid .fc-event-container{margin:0 2.5% 0 2px}.fc-rtl .fc-time-grid .fc-event-container{margin:0 2px 0 2.5%}.fc-time-grid .fc-bgevent,.fc-time-grid .fc-event{position:absolute;z-index:1}.fc-time-grid .fc-bgevent{left:0;right:0}.fc-v-event.fc-not-start{border-top-width:0;padding-top:1px;border-top-left-radius:0;border-top-right-radius:0}.fc-v-event.fc-not-end{border-bottom-width:0;padding-bottom:1px;border-bottom-left-radius:0;border-bottom-right-radius:0}.fc-time-grid-event.fc-selected{overflow:visible}.fc-time-grid-event.fc-selected .fc-bg{display:none}.fc-time-grid-event .fc-content{overflow:hidden}.fc-time-grid-event .fc-time,.fc-time-grid-event .fc-title{padding:0 1px}.fc-time-grid-event .fc-time{font-size:.85em;white-space:nowrap}.fc-time-grid-event.fc-short .fc-content{white-space:nowrap}.fc-time-grid-event.fc-short .fc-time,.fc-time-grid-event.fc-short .fc-title{display:inline-block;vertical-align:top}.fc-time-grid-event.fc-short .fc-time span{display:none}.fc-time-grid-event.fc-short .fc-time:before{content:attr(data-start)}.fc-time-grid-event.fc-short .fc-time:after{content:"\000A0-\000A0"}.fc-time-grid-event.fc-short .fc-title{font-size:.85em;padding:0}.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer{left:0;right:0;bottom:0;height:8px;overflow:hidden;line-height:8px;font-size:11px;font-family:monospace;text-align:center;cursor:s-resize}.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer:after{content:"="}.fc-time-grid-event.fc-selected .fc-resizer{border-radius:5px;border-width:1px;width:8px;height:8px;border-style:solid;border-color:inherit;background:#fff;left:50%;margin-left:-5px;bottom:-5px}.fc-time-grid .fc-now-indicator-line{border-top-width:1px;left:0;right:0}.fc-time-grid .fc-now-indicator-arrow{margin-top:-5px}.fc-ltr .fc-time-grid .fc-now-indicator-arrow{left:0;border-width:5px 0 5px 6px;border-top-color:transparent;border-bottom-color:transparent}.fc-rtl .fc-time-grid .fc-now-indicator-arrow{right:0;border-width:5px 6px 5px 0;border-top-color:transparent;border-bottom-color:transparent}.fc-event-dot{display:inline-block;width:10px;height:10px;border-radius:5px}.fc-rtl .fc-list-view{direction:rtl}.fc-list-view{border-width:1px;border-style:solid}.fc .fc-list-table{table-layout:auto}.fc-list-table td{border-width:1px 0 0;padding:8px 14px}.fc-list-table tr:first-child td{border-top-width:0}.fc-list-heading{border-bottom-width:1px}.fc-list-heading td{font-weight:700}.fc-ltr .fc-list-heading-main{float:left}.fc-ltr .fc-list-heading-alt,.fc-rtl .fc-list-heading-main{float:right}.fc-rtl .fc-list-heading-alt{float:left}.fc-list-item.fc-has-url{cursor:pointer}.fc-list-item:hover td{background-color:#f5f5f5}.fc-list-item-marker,.fc-list-item-time{white-space:nowrap;width:1px}.fc-ltr .fc-list-item-marker{padding-right:0}.fc-rtl .fc-list-item-marker{padding-left:0}.fc-list-item-title a{text-decoration:none;color:inherit}.fc-list-item-title a[href]:hover{text-decoration:underline}.fc-list-empty-wrap2{position:absolute;top:0;left:0;right:0;bottom:0}.fc-list-empty-wrap1{width:100%;height:100%;display:table}.fc-list-empty{display:table-cell;vertical-align:middle;text-align:center}.fc-unthemed .fc-list-empty{background-color:#eee}
\ No newline at end of file
diff --git a/library/fullcalendar/fullcalendar.min.js b/library/fullcalendar/fullcalendar.min.js
index bc8b51c8a..8484b7c45 100644
--- a/library/fullcalendar/fullcalendar.min.js
+++ b/library/fullcalendar/fullcalendar.min.js
@@ -1,9 +1,10 @@
/*!
- * FullCalendar v3.0.1
+ * FullCalendar v3.1.0
* Docs & License: http://fullcalendar.io/
* (c) 2016 Adam Shaw
*/
-!function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):"object"==typeof exports?module.exports=t(require("jquery"),require("moment")):t(jQuery,moment)}(function(t,e){function n(t){return q(t,qt)}function i(t,e){e.left&&t.css({"border-left-width":1,"margin-left":e.left-1}),e.right&&t.css({"border-right-width":1,"margin-right":e.right-1})}function r(t){t.css({"margin-left":"","margin-right":"","border-left-width":"","border-right-width":""})}function s(){t("body").addClass("fc-not-allowed")}function o(){t("body").removeClass("fc-not-allowed")}function l(e,n,i){var r=Math.floor(n/e.length),s=Math.floor(n-r*(e.length-1)),o=[],l=[],u=[],d=0;a(e),e.each(function(n,i){var a=n===e.length-1?s:r,c=t(i).outerHeight(!0);c *").each(function(e,i){var r=t(i).outerWidth();r>n&&(n=r)}),n++,e.width(n),n}function d(t,e){var n,i=t.add(e);return i.css({position:"relative",left:-1}),n=t.outerHeight()-e.outerHeight(),i.css({position:"",left:""}),n}function c(e){var n=e.css("position"),i=e.parents().filter(function(){var e=t(this);return/(auto|scroll)/.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==n&&i.length?i:t(e[0].ownerDocument||document)}function h(t,e){var n=t.offset(),i=n.left-(e?e.left:0),r=n.top-(e?e.top:0);return{left:i,right:i+t.outerWidth(),top:r,bottom:r+t.outerHeight()}}function f(t,e){var n=t.offset(),i=p(t),r=n.left+y(t,"border-left-width")+i.left-(e?e.left:0),s=n.top+y(t,"border-top-width")+i.top-(e?e.top:0);return{left:r,right:r+t[0].clientWidth,top:s,bottom:s+t[0].clientHeight}}function g(t,e){var n=t.offset(),i=n.left+y(t,"border-left-width")+y(t,"padding-left")-(e?e.left:0),r=n.top+y(t,"border-top-width")+y(t,"padding-top")-(e?e.top:0);return{left:i,right:i+t.width(),top:r,bottom:r+t.height()}}function p(t){var e=t.innerWidth()-t[0].clientWidth,n={left:0,right:0,top:0,bottom:t.innerHeight()-t[0].clientHeight};return v()&&"rtl"==t.css("direction")?n.left=e:n.right=e,n}function v(){return null===Zt&&(Zt=m()),Zt}function m(){var e=t("").css({position:"absolute",top:-1e3,left:0,border:0,padding:0,overflow:"scroll",direction:"rtl"}).appendTo("body"),n=e.children(),i=n.offset().left>e.offset().left;return e.remove(),i}function y(t,e){return parseFloat(t.css(e))||0}function S(t){return 1==t.which&&!t.ctrlKey}function w(t){if(void 0!==t.pageX)return t.pageX;var e=t.originalEvent.touches;return e?e[0].pageX:void 0}function E(t){if(void 0!==t.pageY)return t.pageY;var e=t.originalEvent.touches;return e?e[0].pageY:void 0}function D(t){return/^touch/.test(t.type)}function b(t){t.addClass("fc-unselectable").on("selectstart",C)}function C(t){t.preventDefault()}function H(t){return!!window.addEventListener&&(window.addEventListener("scroll",t,!0),!0)}function T(t){return!!window.removeEventListener&&(window.removeEventListener("scroll",t,!0),!0)}function x(t,e){var n={left:Math.max(t.left,e.left),right:Math.min(t.right,e.right),top:Math.max(t.top,e.top),bottom:Math.min(t.bottom,e.bottom)};return n.lefta&&o=a?(n=o.clone(),r=!0):(n=a.clone(),r=!1),l<=u?(i=l.clone(),s=!0):(i=u.clone(),s=!1),{start:n,end:i,isStart:r,isEnd:s}}function N(t,n){return e.duration({days:t.clone().stripTime().diff(n.clone().stripTime(),"days"),ms:t.time()-n.time()})}function G(t,n){return e.duration({days:t.clone().stripTime().diff(n.clone().stripTime(),"days")})}function A(t,n,i){return e.duration(Math.round(t.diff(n,i,!0)),i)}function O(t,e){var n,i,r;for(n=0;n=1&&ot(r)));n++);return i}function V(t,n,i){return null!=i?i.diff(n,t,!0):e.isDuration(n)?n.as(t):n.end.diff(n.start,t,!0)}function P(t,e,n){var i;return W(n)?(e-t)/n:(i=n.asMonths(),Math.abs(i)>=1&&ot(i)?e.diff(t,"months",!0)/i:e.diff(t,"days",!0)/n.asDays())}function _(t,e){var n,i;return W(t)||W(e)?t/e:(n=t.asMonths(),i=e.asMonths(),Math.abs(n)>=1&&ot(n)&&Math.abs(i)>=1&&ot(i)?n/i:t.asDays()/e.asDays())}function Y(t,n){var i;return W(t)?e.duration(t*n):(i=t.asMonths(),Math.abs(i)>=1&&ot(i)?e.duration({months:i*n}):e.duration({days:t.asDays()*n}))}function W(t){return Boolean(t.hours()||t.minutes()||t.seconds()||t.milliseconds())}function j(t){return"[object Date]"===Object.prototype.toString.call(t)||t instanceof Date}function U(t){return/^\d+\:\d+(?:\:\d+\.?(?:\d{3})?)?$/.test(t)}function q(t,e){var n,i,r,s,o,l,a={};if(e)for(n=0;n=0;s--)if(o=t[s][i],"object"==typeof o)r.unshift(o);else if(void 0!==o){a[i]=o;break}r.length&&(a[i]=q(r))}for(n=t.length-1;n>=0;n--){l=t[n];for(i in l)i in a||(a[i]=l[i])}return a}function Z(t){var e=function(){};return e.prototype=t,new e}function $(t,e){for(var n in t)X(t,n)&&(e[n]=t[n])}function X(t,e){return Kt.call(t,e)}function K(e){return/undefined|null|boolean|number|string/.test(t.type(e))}function Q(e,n,i){if(t.isFunction(e)&&(e=[e]),e){var r,s;for(r=0;r /g,">").replace(/'/g,"'").replace(/"/g,""").replace(/\n/g," ")}function et(t){return t.replace(/&.*?;/g,"")}function nt(e){var n=[];return t.each(e,function(t,e){null!=e&&n.push(t+":"+e)}),n.join(";")}function it(e){var n=[];return t.each(e,function(t,e){null!=e&&n.push(t+'="'+tt(e)+'"')}),n.join(" ")}function rt(t){return t.charAt(0).toUpperCase()+t.slice(1)}function st(t,e){return t-e}function ot(t){return t%1===0}function lt(t,e){var n=t[e];return function(){return n.apply(t,arguments)}}function at(t,e,n){var i,r,s,o,l,a=function(){var u=+new Date-o;uo&&(s=mt(t,e,u,d,n[l]),s!==!1);l--)h=s+h;for(a=o;a<=l;a++)f+=gt(t,n[a]),g+=gt(e,n[a]);return(f||g)&&(p=r?g+i+f:f+i+g),c+p+h}function mt(t,e,n,i,r){var s,o;return"string"==typeof r?r:!!((s=r.token)&&(o=re[s.charAt(0)],o&&n.isSame(i,o)))&&ct(t,s)}function yt(t){return t in se?se[t]:se[t]=St(t)}function St(t){for(var e,n=[],i=/\[([^\]]*)\]|\(([^\)]*)\)|(LTS|LT|(\w)\4*o?)|([^\w\[\(]+)/g;e=i.exec(t);)e[1]?n.push(e[1]):e[2]?n.push({maybe:St(e[2])}):e[3]?n.push({token:e[3]}):e[5]&&n.push(e[5]);return n}function wt(){}function Et(t,e){var n;return X(e,"constructor")&&(n=e.constructor),"function"!=typeof n&&(n=e.constructor=function(){t.apply(this,arguments)}),n.prototype=Z(t.prototype),$(e,n.prototype),$(t,n),n}function Dt(t,e){$(e,t.prototype)}function bt(t,e){return!t&&!e||!(!t||!e)&&(t.component===e.component&&Ct(t,e)&&Ct(e,t))}function Ct(t,e){for(var n in t)if(!/^(component|left|right|top|bottom)$/.test(n)&&t[n]!==e[n])return!1;return!0}function Ht(t){return{start:t.start.clone(),end:t.end?t.end.clone():null,allDay:t.allDay}}function Tt(t){var e=Rt(t);return"background"===e||"inverse-background"===e}function xt(t){return"inverse-background"===Rt(t)}function Rt(t){return J((t.source||{}).rendering,t.rendering)}function It(t){var e,n,i={};for(e=0;e=t.leftCol)return!0;return!1}function Bt(t,e){return t.leftCol-e.leftCol}function zt(t){var e,n,i,r=[];for(e=0;ee.top&&t.top ").prependTo(n),q=j.header=new _t(j),l(),d(j.options.defaultView),j.options.handleWindowResize&&(J=at(m,j.options.windowResizeDelay),t(window).resize(J))}function l(){q.render(),q.el&&n.prepend(q.el)}function a(){K&&K.removeElement(),q.removeElement(),$.remove(),n.removeClass("fc fc-ltr fc-rtl fc-unthemed ui-widget"),n.off(".fc"),J&&t(window).unbind("resize",J)}function u(){return n.is(":visible")}function d(e,n){lt++,K&&e&&K.type!==e&&(A(),c()),!K&&e&&(K=j.view=ot[e]||(ot[e]=j.instantiateView(e)),K.setElement(t("
").appendTo($)),q.activateButton(e)),K&&(tt=K.massageCurrentDate(tt),K.displaying&&tt>=K.intervalStart&&tt=K.intervalStart&&t "),h.append(r("left")).append(r("right")).append(r("center")).append('
')):i()}function i(){h&&(h.remove(),h=c.el=null)}function r(n){var i=t('
'),r=e.options,s=r.header[n];return s&&t.each(s.split(" "),function(n){var s,o=t(),l=!0;t.each(this.split(","),function(n,i){var s,a,u,d,c,h,p,v,m,y;"title"==i?(o=o.add(t(" ")),l=!1):((s=(r.customButtons||{})[i])?(u=function(t){s.click&&s.click.call(y[0],t)},d="",c=s.text):(a=e.getViewSpec(i))?(u=function(){e.changeView(i)},g.push(i),d=a.buttonTextOverride,c=a.buttonTextDefault):e[i]&&(u=function(){e[i]()},d=(e.overrides.buttonText||{})[i],c=r.buttonText[i]),u&&(h=s?s.themeIcon:r.themeButtonIcons[i],p=s?s.icon:r.buttonIcons[i],v=d?tt(d):h&&r.theme?" ":p&&!r.theme?" ":tt(c),m=["fc-"+i+"-button",f+"-button",f+"-state-default"],y=t(''+v+" ").click(function(t){y.hasClass(f+"-state-disabled")||(u(t),(y.hasClass(f+"-state-active")||y.hasClass(f+"-state-disabled"))&&y.removeClass(f+"-state-hover"))}).mousedown(function(){y.not("."+f+"-state-active").not("."+f+"-state-disabled").addClass(f+"-state-down")}).mouseup(function(){y.removeClass(f+"-state-down")}).hover(function(){y.not("."+f+"-state-active").not("."+f+"-state-disabled").addClass(f+"-state-hover")},function(){y.removeClass(f+"-state-hover").removeClass(f+"-state-down")}),o=o.add(y)))}),l&&o.first().addClass(f+"-corner-left").end().last().addClass(f+"-corner-right").end(),o.length>1?(s=t("
"),l&&s.addClass("fc-button-group"),s.append(o),i.append(s)):i.append(o)}),i}function s(t){h&&h.find("h2").text(t)}function o(t){h&&h.find(".fc-"+t+"-button").addClass(f+"-state-active")}function l(t){h&&h.find(".fc-"+t+"-button").removeClass(f+"-state-active")}function a(t){h&&h.find(".fc-"+t+"-button").prop("disabled",!0).addClass(f+"-state-disabled")}function u(t){h&&h.find(".fc-"+t+"-button").prop("disabled",!1).removeClass(f+"-state-disabled")}function d(){return g}var c=this;c.render=n,c.removeElement=i,c.updateTitle=s,c.activateButton=o,c.deactivateButton=l,c.disableButton=a,c.enableButton=u,c.getViewsWithButtons=d,c.el=null;var h,f,g=[]}function Yt(){function n(t,e){return!O||tV}function i(t,e){O=t,V=e,r(Y,"reset")}function r(t,e){var n,i;for("reset"===e?j=[]:"add"!==e&&(j=w(j,t)),n=0;nr.value)&&(r=i));return r?r.unit:null},jt.Class=wt,wt.extend=function(){var t,e,n=arguments.length;for(t=0;t ').addClass(n.className||"").css({top:0,left:0}).append(n.content).appendTo(n.parentEl),this.el.on("click",".fc-close",function(){e.hide()}),n.autoHide&&this.listenTo(t(document),"mousedown",this.documentMousedown)},documentMousedown:function(e){this.el&&!t(e.target).closest(this.el).length&&this.hide()},removeElement:function(){this.hide(),this.el&&(this.el.remove(),this.el=null),this.stopListeningTo(t(document),"mousedown")},position:function(){var e,n,i,r,s,o=this.options,l=this.el.offsetParent().offset(),a=this.el.outerWidth(),u=this.el.outerHeight(),d=t(window),h=c(this.el);r=o.top||0,s=void 0!==o.left?o.left:void 0!==o.right?o.right-a:0,h.is(window)||h.is(document)?(h=d,e=0,n=0):(i=h.offset(),e=i.top,n=i.left),e+=d.scrollTop(),n+=d.scrollLeft(),o.viewportConstrain!==!1&&(r=Math.min(r,e+h.outerHeight()-u-this.margin),r=Math.max(r,e+this.margin),s=Math.min(s,n+h.outerWidth()-a-this.margin),s=Math.max(s,n+this.margin)),this.el.css({top:r-l.top,left:s-l.left})},trigger:function(t){this.options[t]&&this.options[t].apply(this,Array.prototype.slice.call(arguments,1))}}),ce=jt.CoordCache=wt.extend({els:null,forcedOffsetParentEl:null,origin:null,boundingRect:null,isHorizontal:!1,isVertical:!1,lefts:null,rights:null,tops:null,bottoms:null,constructor:function(e){this.els=t(e.els),this.isHorizontal=e.isHorizontal,this.isVertical=e.isVertical,this.forcedOffsetParentEl=e.offsetParent?t(e.offsetParent):null},build:function(){var t=this.forcedOffsetParentEl||this.els.eq(0).offsetParent();this.origin=t.offset(),this.boundingRect=this.queryBoundingRect(),this.isHorizontal&&this.buildElHorizontals(),this.isVertical&&this.buildElVerticals()},clear:function(){this.origin=null,this.boundingRect=null,this.lefts=null,this.rights=null,this.tops=null,this.bottoms=null},ensureBuilt:function(){this.origin||this.build()},buildElHorizontals:function(){var e=[],n=[];this.els.each(function(i,r){var s=t(r),o=s.offset().left,l=s.outerWidth();e.push(o),n.push(o+l)}),this.lefts=e,this.rights=n},buildElVerticals:function(){var e=[],n=[];this.els.each(function(i,r){var s=t(r),o=s.offset().top,l=s.outerHeight();e.push(o),n.push(o+l)}),this.tops=e,this.bottoms=n},getHorizontalIndex:function(t){this.ensureBuilt();var e,n=this.lefts,i=this.rights,r=n.length;for(e=0;e=n[e]&&t=n[e]&&t=this.boundingRect.left&&t=this.boundingRect.top&&t=r*r&&this.handleDistanceSurpassed(t)),this.isDragging&&this.handleDrag(n,i,t)},handleDrag:function(t,e,n){this.trigger("drag",t,e,n),this.updateAutoScroll(n)},endDrag:function(t){this.isDragging&&(this.isDragging=!1,this.handleDragEnd(t))},handleDragEnd:function(t){this.trigger("dragEnd",t)},startDelay:function(t){var e=this;this.delay?this.delayTimeoutId=setTimeout(function(){e.handleDelayEnd(t)},this.delay):this.handleDelayEnd(t)},handleDelayEnd:function(t){this.isDelayEnded=!0,this.isDistanceSurpassed&&this.startDrag(t)},handleDistanceSurpassed:function(t){this.isDistanceSurpassed=!0,this.isDelayEnded&&this.startDrag(t)},handleTouchMove:function(t){this.isDragging&&t.preventDefault(),this.handleMove(t)},handleMouseMove:function(t){this.handleMove(t)},handleTouchScroll:function(t){this.isDragging||this.endInteraction(t,!0)},trigger:function(t){this.options[t]&&this.options[t].apply(this,Array.prototype.slice.call(arguments,1)),this["_"+t]&&this["_"+t].apply(this,Array.prototype.slice.call(arguments,1))}});he.mixin({isAutoScroll:!1,scrollBounds:null,scrollTopVel:null,scrollLeftVel:null,scrollIntervalId:null,scrollSensitivity:30,scrollSpeed:200,scrollIntervalMs:50,initAutoScroll:function(){var t=this.scrollEl;this.isAutoScroll=this.options.scroll&&t&&!t.is(window)&&!t.is(document),this.isAutoScroll&&this.listenTo(t,"scroll",at(this.handleDebouncedScroll,100))},destroyAutoScroll:function(){this.endAutoScroll(),this.isAutoScroll&&this.stopListeningTo(this.scrollEl,"scroll")},computeScrollBounds:function(){this.isAutoScroll&&(this.scrollBounds=h(this.scrollEl))},updateAutoScroll:function(t){var e,n,i,r,s=this.scrollSensitivity,o=this.scrollBounds,l=0,a=0;o&&(e=(s-(E(t)-o.top))/s,n=(s-(o.bottom-E(t)))/s,i=(s-(w(t)-o.left))/s,r=(s-(o.right-w(t)))/s,e>=0&&e<=1?l=e*this.scrollSpeed*-1:n>=0&&n<=1&&(l=n*this.scrollSpeed),i>=0&&i<=1?a=i*this.scrollSpeed*-1:r>=0&&r<=1&&(a=r*this.scrollSpeed)),this.setScrollVel(l,a)},setScrollVel:function(t,e){this.scrollTopVel=t,this.scrollLeftVel=e,this.constrainScrollVel(),!this.scrollTopVel&&!this.scrollLeftVel||this.scrollIntervalId||(this.scrollIntervalId=setInterval(lt(this,"scrollIntervalFunc"),this.scrollIntervalMs))},constrainScrollVel:function(){var t=this.scrollEl;this.scrollTopVel<0?t.scrollTop()<=0&&(this.scrollTopVel=0):this.scrollTopVel>0&&t.scrollTop()+t[0].clientHeight>=t[0].scrollHeight&&(this.scrollTopVel=0),this.scrollLeftVel<0?t.scrollLeft()<=0&&(this.scrollLeftVel=0):this.scrollLeftVel>0&&t.scrollLeft()+t[0].clientWidth>=t[0].scrollWidth&&(this.scrollLeftVel=0)},scrollIntervalFunc:function(){var t=this.scrollEl,e=this.scrollIntervalMs/1e3;this.scrollTopVel&&t.scrollTop(t.scrollTop()+this.scrollTopVel*e),this.scrollLeftVel&&t.scrollLeft(t.scrollLeft()+this.scrollLeftVel*e),this.constrainScrollVel(),this.scrollTopVel||this.scrollLeftVel||this.endAutoScroll()},endAutoScroll:function(){this.scrollIntervalId&&(clearInterval(this.scrollIntervalId),this.scrollIntervalId=null,this.handleScrollEnd())},handleDebouncedScroll:function(){this.scrollIntervalId||this.handleScrollEnd()},handleScrollEnd:function(){}});var fe=he.extend({component:null,origHit:null,hit:null,coordAdjust:null,constructor:function(t,e){he.call(this,e),this.component=t},handleInteractionStart:function(t){var e,n,i,r=this.subjectEl;this.computeCoords(),t?(n={left:w(t),top:E(t)},i=n,r&&(e=h(r),i=R(i,e)),this.origHit=this.queryHit(i.left,i.top),r&&this.options.subjectCenter&&(this.origHit&&(e=x(this.origHit,e)||e),i=I(e)),this.coordAdjust=k(i,n)):(this.origHit=null,this.coordAdjust=null),he.prototype.handleInteractionStart.apply(this,arguments)},computeCoords:function(){this.component.prepareHits(),this.computeScrollBounds()},handleDragStart:function(t){var e;he.prototype.handleDragStart.apply(this,arguments),e=this.queryHit(w(t),E(t)),e&&this.handleHitOver(e)},handleDrag:function(t,e,n){var i;he.prototype.handleDrag.apply(this,arguments),i=this.queryHit(w(n),E(n)),bt(i,this.hit)||(this.hit&&this.handleHitOut(),i&&this.handleHitOver(i))},handleDragEnd:function(){this.handleHitDone(),he.prototype.handleDragEnd.apply(this,arguments)},handleHitOver:function(t){var e=bt(t,this.origHit);this.hit=t,this.trigger("hitOver",this.hit,e,this.origHit)},handleHitOut:function(){this.hit&&(this.trigger("hitOut",this.hit),this.handleHitDone(),this.hit=null)},handleHitDone:function(){this.hit&&this.trigger("hitDone",this.hit)},handleInteractionEnd:function(){he.prototype.handleInteractionEnd.apply(this,arguments),this.origHit=null,this.hit=null,this.component.releaseHits()},handleScrollEnd:function(){he.prototype.handleScrollEnd.apply(this,arguments),this.computeCoords()},queryHit:function(t,e){return this.coordAdjust&&(t+=this.coordAdjust.left,e+=this.coordAdjust.top),this.component.queryHit(t,e)}}),ge=wt.extend(ae,{options:null,sourceEl:null,el:null,parentEl:null,top0:null,left0:null,y0:null,x0:null,topDelta:null,leftDelta:null,isFollowing:!1,isHidden:!1,isAnimating:!1,constructor:function(e,n){this.options=n=n||{},this.sourceEl=e,this.parentEl=n.parentEl?t(n.parentEl):e.parent()},start:function(e){this.isFollowing||(this.isFollowing=!0,this.y0=E(e),this.x0=w(e),this.topDelta=0,this.leftDelta=0,this.isHidden||this.updatePosition(),D(e)?this.listenTo(t(document),"touchmove",this.handleMove):this.listenTo(t(document),"mousemove",this.handleMove))},stop:function(e,n){function i(){r.isAnimating=!1,r.removeElement(),r.top0=r.left0=null,n&&n()}var r=this,s=this.options.revertDuration;this.isFollowing&&!this.isAnimating&&(this.isFollowing=!1,this.stopListeningTo(t(document)),e&&s&&!this.isHidden?(this.isAnimating=!0,this.el.animate({top:this.top0,left:this.left0},{duration:s,complete:i})):i())},getEl:function(){var t=this.el;return t||(t=this.el=this.sourceEl.clone().addClass(this.options.additionalClass||"").css({position:"absolute",visibility:"",display:this.isHidden?"none":"",margin:0,right:"auto",bottom:"auto",width:this.sourceEl.width(),height:this.sourceEl.height(),opacity:this.options.opacity||"",zIndex:this.options.zIndex}),t.addClass("fc-unselectable"),t.appendTo(this.parentEl)),t},removeElement:function(){this.el&&(this.el.remove(),this.el=null)},updatePosition:function(){var t,e;this.getEl(),null===this.top0&&(t=this.sourceEl.offset(),e=this.el.offsetParent().offset(),this.top0=t.top-e.top,this.left0=t.left-e.left),this.el.css({top:this.top0+this.topDelta,left:this.left0+this.leftDelta})},handleMove:function(t){this.topDelta=E(t)-this.y0,this.leftDelta=w(t)-this.x0,this.isHidden||this.updatePosition()},hide:function(){this.isHidden||(this.isHidden=!0,this.el&&this.el.hide())},show:function(){this.isHidden&&(this.isHidden=!1,this.updatePosition(),this.getEl().show())}}),pe=jt.Grid=wt.extend(ae,ue,{hasDayInteractions:!0,view:null,isRTL:null,start:null,end:null,el:null,elsByFill:null,eventTimeFormat:null,displayEventTime:null,displayEventEnd:null,minResizeDuration:null,largeUnit:null,dayDragListener:null,segDragListener:null,segResizeListener:null,externalDragListener:null,constructor:function(t){this.view=t,this.isRTL=t.opt("isRTL"),this.elsByFill={},this.dayDragListener=this.buildDayDragListener(),this.initMouseIgnoring()},computeEventTimeFormat:function(){return this.view.opt("smallTimeFormat")},computeDisplayEventTime:function(){return!0},computeDisplayEventEnd:function(){return!0},setRange:function(t){this.start=t.start.clone(),this.end=t.end.clone(),this.rangeUpdated(),this.processRangeOptions()},rangeUpdated:function(){},processRangeOptions:function(){var t,e,n=this.view;this.eventTimeFormat=n.opt("eventTimeFormat")||n.opt("timeFormat")||this.computeEventTimeFormat(),t=n.opt("displayEventTime"),null==t&&(t=this.computeDisplayEventTime()),e=n.opt("displayEventEnd"),null==e&&(e=this.computeDisplayEventEnd()),this.displayEventTime=t,this.displayEventEnd=e},spanToSegs:function(t){},diffDates:function(t,e){return this.largeUnit?A(t,e,this.largeUnit):N(t,e)},prepareHits:function(){},releaseHits:function(){},queryHit:function(t,e){},getHitSpan:function(t){},getHitEl:function(t){},setElement:function(t){this.el=t,this.hasDayInteractions&&(b(t),this.bindDayHandler("touchstart",this.dayTouchStart),this.bindDayHandler("mousedown",this.dayMousedown)),this.bindSegHandlers(),this.bindGlobalHandlers()},bindDayHandler:function(e,n){var i=this;this.el.on(e,function(e){if(!t(e.target).is(i.segSelector+","+i.segSelector+" *,.fc-more,a[data-goto]"))return n.call(i,e)})},removeElement:function(){this.unbindGlobalHandlers(),this.clearDragListeners(),this.el.remove()},renderSkeleton:function(){},renderDates:function(){},unrenderDates:function(){},bindGlobalHandlers:function(){this.listenTo(t(document),{dragstart:this.externalDragStart,sortstart:this.externalDragStart})},unbindGlobalHandlers:function(){this.stopListeningTo(t(document))},dayMousedown:function(t){this.isIgnoringMouse||this.dayDragListener.startInteraction(t,{})},dayTouchStart:function(t){var e=this.view;(e.isSelected||e.selectedEvent)&&this.tempIgnoreMouse(),this.dayDragListener.startInteraction(t,{delay:this.view.opt("longPressDelay")})},buildDayDragListener:function(){var t,e,n=this,i=this.view,r=i.opt("selectable"),l=new fe(this,{scroll:i.opt("dragScroll"),interactionStart:function(){t=l.origHit,e=null},dragStart:function(){i.unselect()},hitOver:function(i,o,l){l&&(o||(t=null),r&&(e=n.computeSelection(n.getHitSpan(l),n.getHitSpan(i)),e?n.renderSelection(e):e===!1&&s()))},hitOut:function(){t=null,e=null,n.unrenderSelection()},hitDone:function(){o()},interactionEnd:function(r,s){s||(t&&!n.isIgnoringMouse&&i.triggerDayClick(n.getHitSpan(t),n.getHitEl(t),r),e&&i.reportSelection(e,r))}});return l},clearDragListeners:function(){this.dayDragListener.endInteraction(),this.segDragListener&&this.segDragListener.endInteraction(),this.segResizeListener&&this.segResizeListener.endInteraction(),this.externalDragListener&&this.externalDragListener.endInteraction()},renderEventLocationHelper:function(t,e){var n=this.fabricateHelperEvent(t,e);return this.renderHelper(n,e)},fabricateHelperEvent:function(t,e){var n=e?Z(e.event):{};return n.start=t.start.clone(),n.end=t.end?t.end.clone():null,n.allDay=null,this.view.calendar.normalizeEventDates(n),n.className=(n.className||[]).concat("fc-helper"),e||(n.editable=!1),n},renderHelper:function(t,e){},unrenderHelper:function(){},renderSelection:function(t){this.renderHighlight(t)},unrenderSelection:function(){this.unrenderHighlight()},computeSelection:function(t,e){var n=this.computeSelectionSpan(t,e);return!(n&&!this.view.calendar.isSelectionSpanAllowed(n))&&n},computeSelectionSpan:function(t,e){var n=[t.start,t.end,e.start,e.end];return n.sort(st),{start:n[0].clone(),end:n[3].clone()}},renderHighlight:function(t){this.renderFill("highlight",this.spanToSegs(t))},unrenderHighlight:function(){this.unrenderFill("highlight")},highlightSegClasses:function(){return["fc-highlight"]},renderBusinessHours:function(){},unrenderBusinessHours:function(){},getNowIndicatorUnit:function(){},renderNowIndicator:function(t){},unrenderNowIndicator:function(){},renderFill:function(t,e){},unrenderFill:function(t){var e=this.elsByFill[t];e&&(e.remove(),delete this.elsByFill[t])},renderFillSegEls:function(e,n){var i,r=this,s=this[e+"SegEl"],o="",l=[];if(n.length){for(i=0;i "},getDayClasses:function(t){var e=this.view,n=e.calendar.getNow(),i=["fc-"+$t[t.day()]];return 1==e.intervalDuration.as("months")&&t.month()!=e.intervalStart.month()&&i.push("fc-other-month"),t.isSame(n,"day")?i.push("fc-today",e.highlightStateClass):t *",mousedOverSeg:null,isDraggingSeg:!1,isResizingSeg:!1,isDraggingExternal:!1,segs:null,renderEvents:function(t){var e,n=[],i=[];for(e=0;el&&o.push({start:l,end:n.start}),l=n.end;return l=e.length?e[e.length-1]+1:e[n]},computeColHeadFormat:function(){return this.rowCnt>1||this.colCnt>10?"ddd":this.colCnt>1?this.view.opt("dayOfMonthFormat"):"dddd"},sliceRangeByRow:function(t){var e,n,i,r,s,o=this.daysPerRow,l=this.view.computeDayRange(t),a=this.getDateDayIndex(l.start),u=this.getDateDayIndex(l.end.clone().subtract(1,"days")),d=[];for(e=0;e "},updateSegVerticals:function(t){this.computeSegVerticals(t),this.assignSegVerticals(t)},computeSegVerticals:function(t){var e,n;for(e=0;e1?"ll":"LL"},formatRange:function(t,e,n){var i=t.end;return i.hasTime()||(i=i.clone().subtract(1)),pt(t.start,i,e,n,this.opt("isRTL"))},getAllDayHtml:function(){return this.opt("allDayHtml")||tt(this.opt("allDayText"))},buildGotoAnchorHtml:function(e,n,i){var r,s,o,l;return t.isPlainObject(e)?(r=e.date,s=e.type,o=e.forceOff):r=e,r=jt.moment(r),l={date:r.format("YYYY-MM-DD"),type:s||"day"},"string"==typeof n&&(i=n,n=null),n=n?" "+it(n):"",i=i||"",!o&&this.opt("navLinks")?"'+i+" ":""+i+" "},setElement:function(t){this.el=t,this.bindGlobalHandlers()},removeElement:function(){this.clear(),this.isSkeletonRendered&&(this.unrenderSkeleton(),this.isSkeletonRendered=!1),this.unbindGlobalHandlers(),this.el.remove()},display:function(t,e){var n=this,i=null;return null!=e&&this.displaying&&(i=this.queryScroll()),this.calendar.freezeContentHeight(),ut(this.clear(),function(){return n.displaying=ut(n.displayView(t),function(){null!=e?n.setScroll(e):n.forceScroll(n.computeInitialScroll(i)),n.calendar.unfreezeContentHeight(),n.triggerRender()})})},clear:function(){var e=this,n=this.displaying;return n?ut(n,function(){return e.displaying=null,e.clearEvents(),e.clearView()}):t.when()},displayView:function(t){this.isSkeletonRendered||(this.renderSkeleton(),this.isSkeletonRendered=!0),t&&this.setDate(t),this.render&&this.render(),this.renderDates(),this.updateSize(),this.renderBusinessHours(),this.startNowIndicator()},clearView:function(){this.unselect(),this.stopNowIndicator(),this.triggerUnrender(),this.unrenderBusinessHours(),this.unrenderDates(),this.destroy&&this.destroy()},renderSkeleton:function(){},unrenderSkeleton:function(){},renderDates:function(){},unrenderDates:function(){},triggerRender:function(){this.trigger("viewRender",this,this,this.el)},triggerUnrender:function(){this.trigger("viewDestroy",this,this,this.el)},bindGlobalHandlers:function(){this.listenTo(t(document),"mousedown",this.handleDocumentMousedown),this.listenTo(t(document),"touchstart",this.processUnselect)},unbindGlobalHandlers:function(){this.stopListeningTo(t(document))},initThemingProps:function(){var t=this.opt("theme")?"ui":"fc";this.widgetHeaderClass=t+"-widget-header",this.widgetContentClass=t+"-widget-content",this.highlightStateClass=t+"-state-highlight"},renderBusinessHours:function(){},unrenderBusinessHours:function(){},startNowIndicator:function(){var t,n,i,r=this;this.opt("nowIndicator")&&(t=this.getNowIndicatorUnit(),t&&(n=lt(this,"updateNowIndicator"),this.initialNowDate=this.calendar.getNow(),this.initialNowQueriedMs=+new Date,this.renderNowIndicator(this.initialNowDate),this.isNowIndicatorRendered=!0,i=this.initialNowDate.clone().startOf(t).add(1,t)-this.initialNowDate,this.nowIndicatorTimeoutID=setTimeout(function(){r.nowIndicatorTimeoutID=null,n(),i=+e.duration(1,t),i=Math.max(100,i),r.nowIndicatorIntervalID=setInterval(n,i)},i)))},updateNowIndicator:function(){this.isNowIndicatorRendered&&(this.unrenderNowIndicator(),this.renderNowIndicator(this.initialNowDate.clone().add(new Date-this.initialNowQueriedMs)))},stopNowIndicator:function(){this.isNowIndicatorRendered&&(this.nowIndicatorTimeoutID&&(clearTimeout(this.nowIndicatorTimeoutID),this.nowIndicatorTimeoutID=null),this.nowIndicatorIntervalID&&(clearTimeout(this.nowIndicatorIntervalID),this.nowIndicatorIntervalID=null),this.unrenderNowIndicator(),this.isNowIndicatorRendered=!1)},getNowIndicatorUnit:function(){},renderNowIndicator:function(t){},unrenderNowIndicator:function(){},updateSize:function(t){var e;t&&(e=this.queryScroll()),this.updateHeight(t),this.updateWidth(t),this.updateNowIndicator(),t&&this.setScroll(e)},updateWidth:function(t){},updateHeight:function(t){var e=this.calendar;this.setHeight(e.getSuggestedViewHeight(),e.isHeightAuto())},setHeight:function(t,e){},computeInitialScroll:function(t){return 0},queryScroll:function(){},setScroll:function(t){},forceScroll:function(t){var e=this;this.setScroll(t),setTimeout(function(){e.setScroll(t)},0)},displayEvents:function(t){var e=this.queryScroll();this.clearEvents(),this.renderEvents(t),this.isEventsRendered=!0,this.setScroll(e),this.triggerEventRender()},clearEvents:function(){var t;this.isEventsRendered&&(t=this.queryScroll(),this.triggerEventUnrender(),this.destroyEvents&&this.destroyEvents(),this.unrenderEvents(),this.setScroll(t),this.isEventsRendered=!1)},renderEvents:function(t){},unrenderEvents:function(){},triggerEventRender:function(){this.renderedEventSegEach(function(t){this.trigger("eventAfterRender",t.event,t.event,t.el)}),this.trigger("eventAfterAllRender")},triggerEventUnrender:function(){this.renderedEventSegEach(function(t){this.trigger("eventDestroy",t.event,t.event,t.el)})},resolveEventEl:function(e,n){var i=this.trigger("eventRender",e,e,n);return i===!1?n=null:i&&i!==!0&&(n=t(i)),n},showEvent:function(t){this.renderedEventSegEach(function(t){t.el.css("visibility","")},t)},hideEvent:function(t){this.renderedEventSegEach(function(t){t.el.css("visibility","hidden")},t)},renderedEventSegEach:function(t,e){var n,i=this.getEventSegs();for(n=0;n=this.nextDayThreshold&&r.add(1,"days")),(!i||r<=n)&&(r=n.clone().add(1,"days")),{start:n,end:r}},isMultiDayEvent:function(t){var e=this.computeDayRange(t);return e.end.diff(e.start,"days")>1}}),we=jt.Scroller=wt.extend({el:null,scrollEl:null,overflowX:null,overflowY:null,constructor:function(t){t=t||{},this.overflowX=t.overflowX||t.overflow||"auto",this.overflowY=t.overflowY||t.overflow||"auto"},render:function(){this.el=this.renderEl(),this.applyOverflow()},renderEl:function(){return this.scrollEl=t('
')},clear:function(){this.setHeight("auto"),this.applyOverflow()},destroy:function(){this.el.remove()},applyOverflow:function(){this.scrollEl.css({"overflow-x":this.overflowX,"overflow-y":this.overflowY})},lockOverflow:function(t){var e=this.overflowX,n=this.overflowY;t=t||this.getScrollbarWidths(),"auto"===e&&(e=t.top||t.bottom||this.scrollEl[0].scrollWidth-1>this.scrollEl[0].clientWidth?"scroll":"hidden"),"auto"===n&&(n=t.left||t.right||this.scrollEl[0].scrollHeight-1>this.scrollEl[0].clientHeight?"scroll":"hidden"),this.scrollEl.css({"overflow-x":e,"overflow-y":n})},setHeight:function(t){this.scrollEl.height(t)},getScrollTop:function(){return this.scrollEl.scrollTop()},setScrollTop:function(t){this.scrollEl.scrollTop(t)},getClientWidth:function(){return this.scrollEl[0].clientWidth},getClientHeight:function(){return this.scrollEl[0].clientHeight},getScrollbarWidths:function(){return p(this.scrollEl)}}),Ee=jt.Calendar=wt.extend({dirDefaults:null,localeDefaults:null,overrides:null,dynamicOverrides:null,options:null,viewSpecCache:null,view:null,header:null,loadingLevel:0,constructor:Ot,initialize:function(){},populateOptionsHash:function(){var t,e,i,r;t=J(this.dynamicOverrides.locale,this.overrides.locale),e=De[t],e||(t=Ee.defaults.locale,e=De[t]||{}),i=J(this.dynamicOverrides.isRTL,this.overrides.isRTL,e.isRTL,Ee.defaults.isRTL),r=i?Ee.rtlDefaults:{},this.dirDefaults=r,this.localeDefaults=e,this.options=n([Ee.defaults,r,e,this.overrides,this.dynamicOverrides]),Vt(this.options)},getViewSpec:function(t){var e=this.viewSpecCache;return e[t]||(e[t]=this.buildViewSpec(t))},getUnitViewSpec:function(e){var n,i,r;if(t.inArray(e,Xt)!=-1)for(n=this.header.getViewsWithButtons(),t.each(jt.views,function(t){n.push(t)}),i=0;i=n&&e.end<=i},Ee.prototype.getPeerEvents=function(t,e){var n,i,r=this.getEventCache(),s=[];for(n=0;nn};var Re={id:"_fcBusinessHours",start:"09:00",end:"17:00",dow:[1,2,3,4,5],rendering:"inverse-background"};Ee.prototype.getCurrentBusinessHourEvents=function(t){return this.computeBusinessHourEvents(t,this.options.businessHours)},Ee.prototype.computeBusinessHourEvents=function(e,n){return n===!0?this.expandBusinessHourEvents(e,[{}]):t.isPlainObject(n)?this.expandBusinessHourEvents(e,[n]):t.isArray(n)?this.expandBusinessHourEvents(e,n,!0):[]},Ee.prototype.expandBusinessHourEvents=function(e,n,i){var r,s,o=this.getView(),l=[];for(r=0;r1,this.opt("weekNumbers")&&(this.opt("weekNumbersWithinDays")?(this.cellWeekNumbersVisible=!0,this.colWeekNumbersVisible=!1):(this.cellWeekNumbersVisible=!1,this.colWeekNumbersVisible=!0)),this.dayGrid.numbersVisible=this.dayNumbersVisible||this.cellWeekNumbersVisible||this.colWeekNumbersVisible,this.el.addClass("fc-basic-view").html(this.renderSkeletonHtml()),this.renderHead(),this.scroller.render();var e=this.scroller.el.addClass("fc-day-grid-container"),n=t('
').appendTo(e);this.el.find(".fc-body > tr > td").append(e),this.dayGrid.setElement(n),this.dayGrid.renderDates(this.hasRigidRows())},renderHead:function(){this.headContainerEl=this.el.find(".fc-head-container").html(this.dayGrid.renderHeadHtml()),this.headRowEl=this.headContainerEl.find(".fc-row")},unrenderDates:function(){this.dayGrid.unrenderDates(),this.dayGrid.removeElement(),this.scroller.destroy()},renderBusinessHours:function(){this.dayGrid.renderBusinessHours()},unrenderBusinessHours:function(){this.dayGrid.unrenderBusinessHours()},renderSkeletonHtml:function(){return''},weekNumberStyleAttr:function(){return null!==this.weekNumberWidth?'style="width:'+this.weekNumberWidth+'px"':""},hasRigidRows:function(){var t=this.opt("eventLimit");return t&&"number"!=typeof t},updateWidth:function(){this.colWeekNumbersVisible&&(this.weekNumberWidth=u(this.el.find(".fc-week-number")))},setHeight:function(t,e){var n,s,o=this.opt("eventLimit");this.scroller.clear(),r(this.headRowEl),this.dayGrid.removeSegPopover(),o&&"number"==typeof o&&this.dayGrid.limitRows(o),n=this.computeScrollerHeight(t),this.setGridHeight(n,e),o&&"number"!=typeof o&&this.dayGrid.limitRows(o),e||(this.scroller.setHeight(n),s=this.scroller.getScrollbarWidths(),(s.left||s.right)&&(i(this.headRowEl,s),n=this.computeScrollerHeight(t),this.scroller.setHeight(n)),this.scroller.lockOverflow(s))},computeScrollerHeight:function(t){return t-d(this.el,this.scroller.el)},setGridHeight:function(t,e){e?a(this.dayGrid.rowEls):l(this.dayGrid.rowEls,t,!0)},queryScroll:function(){return this.scroller.getScrollTop()},setScroll:function(t){this.scroller.setScrollTop(t)},prepareHits:function(){this.dayGrid.prepareHits()},releaseHits:function(){this.dayGrid.releaseHits()},queryHit:function(t,e){return this.dayGrid.queryHit(t,e)},getHitSpan:function(t){return this.dayGrid.getHitSpan(t)},getHitEl:function(t){return this.dayGrid.getHitEl(t)},renderEvents:function(t){this.dayGrid.renderEvents(t),this.updateHeight()},getEventSegs:function(){return this.dayGrid.getEventSegs()},unrenderEvents:function(){this.dayGrid.unrenderEvents()},renderDrag:function(t,e){return this.dayGrid.renderDrag(t,e)},unrenderDrag:function(){this.dayGrid.unrenderDrag()},renderSelection:function(t){this.dayGrid.renderSelection(t)},unrenderSelection:function(){this.dayGrid.unrenderSelection()}}),ke={renderHeadIntroHtml:function(){var t=this.view;return t.colWeekNumbersVisible?'":""},renderNumberIntroHtml:function(t){var e=this.view,n=this.getCellDate(t,0);return e.colWeekNumbersVisible?'"+e.buildGotoAnchorHtml({date:n,type:"week",forceOff:1===this.colCnt},n.format("w"))+" ":""},renderBgIntroHtml:function(){var t=this.view;return t.colWeekNumbersVisible?' ":""},renderIntroHtml:function(){var t=this.view;return t.colWeekNumbersVisible?' ":""}},Me=jt.MonthView=Ie.extend({computeRange:function(t){var e,n=Ie.prototype.computeRange.call(this,t);return this.isFixedWeeks()&&(e=Math.ceil(n.end.diff(n.start,"weeks",!0)),n.end.add(6-e,"weeks")),n},setGridHeight:function(t,e){e&&(t*=this.rowCnt/6),l(this.dayGrid.rowEls,t,!e)},isFixedWeeks:function(){return this.opt("fixedWeekCount")}});Ut.basic={class:Ie},Ut.basicDay={type:"basic",duration:{days:1}},Ut.basicWeek={type:"basic",duration:{weeks:1}},Ut.month={class:Me,duration:{months:1},defaults:{fixedWeekCount:!0}};var Le=jt.AgendaView=Se.extend({scroller:null,timeGridClass:ye,timeGrid:null,dayGridClass:me,dayGrid:null,axisWidth:null,headContainerEl:null,noScrollRowEls:null,bottomRuleEl:null,initialize:function(){this.timeGrid=this.instantiateTimeGrid(),this.opt("allDaySlot")&&(this.dayGrid=this.instantiateDayGrid()),this.scroller=new we({overflowX:"hidden",overflowY:"auto"})},instantiateTimeGrid:function(){var t=this.timeGridClass.extend(Be);return new t(this)},instantiateDayGrid:function(){var t=this.dayGridClass.extend(ze);return new t(this)},setRange:function(t){Se.prototype.setRange.call(this,t),this.timeGrid.setRange(t),this.dayGrid&&this.dayGrid.setRange(t)},renderDates:function(){this.el.addClass("fc-agenda-view").html(this.renderSkeletonHtml()),this.renderHead(),this.scroller.render();var e=this.scroller.el.addClass("fc-time-grid-container"),n=t('
').appendTo(e);this.el.find(".fc-body > tr > td").append(e),this.timeGrid.setElement(n),this.timeGrid.renderDates(),this.bottomRuleEl=t('').appendTo(this.timeGrid.el),this.dayGrid&&(this.dayGrid.setElement(this.el.find(".fc-day-grid")),this.dayGrid.renderDates(),this.dayGrid.bottomCoordPadding=this.dayGrid.el.next("hr").outerHeight()),this.noScrollRowEls=this.el.find(".fc-row:not(.fc-scroller *)")},renderHead:function(){this.headContainerEl=this.el.find(".fc-head-container").html(this.timeGrid.renderHeadHtml())},unrenderDates:function(){this.timeGrid.unrenderDates(),this.timeGrid.removeElement(),this.dayGrid&&(this.dayGrid.unrenderDates(),this.dayGrid.removeElement()),this.scroller.destroy()},renderSkeletonHtml:function(){return'"},axisStyleAttr:function(){return null!==this.axisWidth?'style="width:'+this.axisWidth+'px"':""},renderBusinessHours:function(){this.timeGrid.renderBusinessHours(),this.dayGrid&&this.dayGrid.renderBusinessHours()},unrenderBusinessHours:function(){this.timeGrid.unrenderBusinessHours(),this.dayGrid&&this.dayGrid.unrenderBusinessHours()},getNowIndicatorUnit:function(){return this.timeGrid.getNowIndicatorUnit()},renderNowIndicator:function(t){this.timeGrid.renderNowIndicator(t)},unrenderNowIndicator:function(){this.timeGrid.unrenderNowIndicator()},updateSize:function(t){this.timeGrid.updateSize(t),Se.prototype.updateSize.call(this,t)},updateWidth:function(){this.axisWidth=u(this.el.find(".fc-axis"))},setHeight:function(t,e){var n,s,o;this.bottomRuleEl.hide(),this.scroller.clear(),r(this.noScrollRowEls),this.dayGrid&&(this.dayGrid.removeSegPopover(),n=this.opt("eventLimit"),n&&"number"!=typeof n&&(n=Fe),n&&this.dayGrid.limitRows(n)),e||(s=this.computeScrollerHeight(t),this.scroller.setHeight(s),o=this.scroller.getScrollbarWidths(),(o.left||o.right)&&(i(this.noScrollRowEls,o),s=this.computeScrollerHeight(t),this.scroller.setHeight(s)),this.scroller.lockOverflow(o),this.timeGrid.getTotalSlatHeight()