Editorial Calendar - Version 0.1

Version Description

  • This version is just for beta testers

<?php code(); // goes in backticks ?>

Download this release

Release Info

Developer MaryVogt
Plugin Icon wp plugin Editorial Calendar
Version 0.1
Comparing to
See all releases

Version 0.1

LICENSE.txt ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
202
+
203
+
204
+ /*
205
+ * One-JAR� (http://www.simontuffs.com/one-jar). Copyright (c) 2004-2007,
206
+ * P. Simon Tuffs (simon@simontuffs.com). All rights reserved.
207
+ *
208
+ * Redistribution and use in source and binary forms, with or without
209
+ * modification, are permitted provided that the following conditions are met:
210
+ *
211
+ * Redistributions of source code must retain the above copyright notice, this
212
+ * list of conditions and the following disclaimer.
213
+ *
214
+ * Redistributions in binary form must reproduce the above copyright notice,
215
+ * this list of conditions and the following disclaimer in the documentation
216
+ * and/or other materials provided with the distribution.
217
+ *
218
+ * Neither the name of P. Simon Tuffs, nor the names of any contributors,
219
+ * nor the name One-JAR may be used to endorse or promote products derived
220
+ * from this software without specific prior written permission.
221
+ *
222
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
223
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
224
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
225
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
226
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
227
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
228
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
229
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
230
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
231
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
232
+ * POSSIBILITY OF SUCH DAMAGE.
233
+ *
234
+ * Including this file inside the built One-JAR file conforms with these terms.
235
+ */
236
+
edcal.css ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #edcal_scrollable {
2
+ width: 98%;
3
+ /* required settings */
4
+ position:relative;
5
+ overflow:hidden;
6
+
7
+ /* This height will be reset with JavaScript */
8
+ height: 100%;
9
+ }
10
+
11
+ #cal {
12
+ position:absolute;
13
+
14
+ /* this time we have very large space for the height */
15
+ height:100%;
16
+ }
17
+
18
+ .day {
19
+ /*background-color: #F2F2F2;*/
20
+ background-color: #e0e0e0;;
21
+ }
22
+
23
+ .scrollHover {
24
+ background: lightblue;
25
+ }
26
+
27
+ .row {
28
+ height: 100%;
29
+ }
30
+
31
+ .jan, .mar, .may, .jul, .sep, .nov {
32
+ background-color: #F1F1F1;
33
+ }
34
+
35
+ .today {
36
+ background-color: wheat;
37
+ }
38
+
39
+ .today .daylabel {
40
+ font-weight: bold;
41
+ }
42
+
43
+ .dayobj {
44
+ position: relative;
45
+ width: 100%;
46
+ height: 100%;
47
+ }
48
+
49
+ .daylabel {
50
+ position: absolute;
51
+ top: 0px;
52
+ right: 5px;
53
+ }
54
+
55
+ .saturday .daylabel {
56
+ right: 20px;
57
+ }
58
+
59
+ .post {
60
+ display: block;
61
+ list-style-type: none;
62
+ background: #FFFFE0;
63
+ z-index: 10;
64
+ cursor: move;
65
+ }
66
+
67
+ .day-active {
68
+ background-color: lightyellow;
69
+ }
70
+
71
+ .dayhead {
72
+ font-weight: bold;
73
+ text-align: center;
74
+ }
75
+
76
+ #rowhead {
77
+ height: 1.5em;
78
+ top: 0px;
79
+ width: 98%
80
+ }
81
+
82
+ #topbar {
83
+ margin-bottom: 1em;
84
+ width: 98%;
85
+ margin-top: 1em;
86
+ position: relative;
87
+ height: 2em;
88
+ }
89
+
90
+ #topleft {
91
+ position: absolute;
92
+ left: 75px;
93
+ top: 5px;
94
+ }
95
+
96
+ #topright {
97
+ position: absolute;
98
+ left: 15%;
99
+ top: 0px;
100
+ width: 85%;
101
+ text-align: right;
102
+ }
103
+
104
+ #moveToToday {
105
+ margin-left: 2em;
106
+ }
107
+
108
+ .future {
109
+ background-color: #FFFFE0;
110
+ }
111
+
112
+ .draft {
113
+ background-color: #cccccc;
114
+
115
+ }
116
+
117
+ .publish {
118
+ background-color: #dde4fb;
119
+ }
120
+
121
+ #loading {
122
+ background-image: url('images/loading.gif');
123
+ background-repeat: no-repeat;
124
+ width: 43px;
125
+ height: 11px;
126
+ display: block;
127
+ position: absolute;
128
+ top: 5px;
129
+ right: 5px;
130
+ }
131
+
132
+ .loadingclass {
133
+ background-image: url('images/loading_post.gif');
134
+ background-position: top right;
135
+ background-repeat: no-repeat;
136
+ cursor: auto;
137
+ }
138
+
139
+ .postlist {
140
+ position: relative;
141
+ top: 1.5em;
142
+ height: 85%;
143
+ overflow: auto;
144
+ }
145
+
146
+ .page-numbers {
147
+ text-decoration: none;
148
+ border: none;
149
+ border-bottom-style: none;
150
+ }
151
+
152
+ #tooltip p {
153
+ margin-left: 1.5em;
154
+ }
155
+
156
+ #tooltip h3 {
157
+ max-width: 15em;
158
+ margin-bottom: -10px;
159
+ }
160
+
161
+ #tooltip {
162
+ background: white;
163
+ opacity: 1;
164
+ padding: 1em;
165
+ padding-bottom: 0em;
166
+ border: thin solid lightgray;
167
+ -moz-border-radius:11px;
168
+ }
169
+
170
+ .ui-draggable-dragging {
171
+ width: 8em;
172
+ }
edcal.js ADDED
@@ -0,0 +1,911 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*******************************************************************************
2
+ *
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ *
15
+ ******************************************************************************/
16
+
17
+ /*
18
+ This is the WordPress editorial calendar. It is a continuous
19
+ calendar in both directions. That means instead of showing only
20
+ one month at a time it shows the months running together. Users
21
+ can scroll from one month to the next using the up and down
22
+ arrow keys, the page up and page down keys, the next and previous
23
+ month buttons, and their mouse wheel.
24
+
25
+ The calendar shows five weeks visible at a time and maintains 11
26
+ weeks of rendered HTML. Only the middle weeks are visible.
27
+
28
+ Week 1
29
+ Week 2
30
+ Week 3
31
+ - Week 4 -
32
+ | Week 5 |
33
+ | Week 6 |
34
+ | Week 7 |
35
+ - Week 8 -
36
+ Week 9
37
+ Week 10
38
+ Week 11
39
+
40
+ When the user scrolls down one week the new week is added at the
41
+ end of the calendar and the first week is removed. In this way
42
+ the calendar will only ever have 11 weeks total and won't use up
43
+ excessive memory.
44
+
45
+ This calendar uses AJAX to call into the functions defined in
46
+ edcal.php. These functions get posts and change post dates.
47
+
48
+ The HTML structure of the calendar is:
49
+
50
+ <div id="cal">
51
+ <div id="row08Nov2009">
52
+ <div id="row08Nov2009row">
53
+ <div class="day sunday nov" id="08Nov2009">
54
+ <div class="dayobj">
55
+ <div class="daylabel">8</div>
56
+ <ul class="postlist">
57
+ </ul>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ */
64
+ var edcal = {
65
+ /*
66
+ * True if the calendar is in the process of moving
67
+ */
68
+ isMoving: false,
69
+
70
+ /*
71
+ This is the base URL we use to make AJAX calls back
72
+ to the server. This values is set in code generated
73
+ from edcal.php.
74
+ */
75
+ ajax_url: '',
76
+
77
+ /*
78
+ * The cache of dates we have already loaded posts for.
79
+ */
80
+ cacheDates : new Array(),
81
+
82
+ /*
83
+ * The ID of the timer we use to batch new post requests
84
+ */
85
+ tID: null,
86
+
87
+ /*
88
+ * The number of steps moving for this timer.
89
+ */
90
+ steps: 0,
91
+
92
+ /*
93
+ * The constant for the concurrency error.
94
+ */
95
+ CONCURRENCY_ERROR: 4,
96
+
97
+ /*
98
+ * The constant for the user permission error
99
+ */
100
+ PERMISSION_ERROR: 5,
101
+
102
+ /*
103
+ The direction the calendar last moved.
104
+ true = down = to the future
105
+ false = up = to the past
106
+
107
+ */
108
+ currentDirection: true,
109
+
110
+ /*
111
+ This date is our index. When the calendar moves we
112
+ update this date to indicate the next rows we need
113
+ to add.
114
+ */
115
+ _wDate: Date.today(),
116
+
117
+ /*
118
+ * The date since the previous move
119
+ */
120
+ moveDate: null,
121
+
122
+ /*
123
+ A cache of all the posts we have loaded so far. The
124
+ data structure is:
125
+
126
+ posts [date - ddMMMyyyy][posts array - post object from JSON data]
127
+ */
128
+ posts: new Array(50),
129
+
130
+ /*
131
+ IE will sometimes fire the resize event twice for the same resize
132
+ action. We save it so we only resize the calendar once and avoid
133
+ any flickering.
134
+ */
135
+ windowHeight: 0,
136
+
137
+ /*
138
+ This function aligns the grid in two directions. There
139
+ is a vertical grid with a row of each week and a horizontal
140
+ grid for each week with a list of days.
141
+ */
142
+ alignGrid: function(/*string*/ gridid, /*int*/ cols, /*int*/ cellWidth, /*int*/ cellHeight, /*int*/ padding) {
143
+ var x = 0;
144
+ var y = 0;
145
+ var count = 1;
146
+
147
+ jQuery(gridid).each(function(){
148
+ jQuery(this).css("position", "relative");
149
+
150
+ jQuery(this).children("div").each(function(){
151
+ jQuery(this).css({
152
+ width: cellWidth + "%",
153
+ height: cellHeight + "%",
154
+ position: "absolute",
155
+ left: x + "%",
156
+ top: y + "%"
157
+ });
158
+
159
+ if ((count % cols) === 0){
160
+ x = 0;
161
+ y += cellHeight + padding;
162
+ }else{
163
+ x += cellWidth + padding;
164
+ }
165
+
166
+ count++;
167
+ });
168
+ });
169
+ },
170
+
171
+ /*
172
+ This is a helper function to align the calendar so we don't
173
+ have to change the cell sizes in multiple places.
174
+ */
175
+ alignCal: function() {
176
+ edcal.alignGrid("#cal", 1, 100, 20, 0.25);
177
+ },
178
+
179
+
180
+ /*
181
+ This function creates the days header at the top of the
182
+ calendar.
183
+
184
+ TODO: We should localize these values
185
+ */
186
+ createDaysHeader: function() {
187
+ var html = '<div class="dayhead">Sunday</div>';
188
+ html += '<div class="dayhead">Monday</div>';
189
+ html += '<div class="dayhead">Tuesday</div>';
190
+ html += '<div class="dayhead">Wednesday</div>';
191
+ html += '<div class="dayhead">Thursday</div>';
192
+ html += '<div class="dayhead">Friday</div>';
193
+ html += '<div class="dayhead">Saturday</div>';
194
+
195
+ jQuery("#rowhead").append(html);
196
+
197
+ edcal.alignGrid("#rowhead", 7, 14.2, 100, 0.25);
198
+ },
199
+
200
+ /*
201
+ Creates a row of the calendar and adds all of the CSS classes
202
+ and listeners for each calendar day.
203
+ */
204
+ createRow: function(/*jQuery*/ parent, /*bool*/ append) {
205
+ var _date = edcal._wDate.clone();
206
+
207
+ var newrow = '<div class="rowcont" id="' + 'row' + edcal._wDate.toString("ddMMMyyyy") + '">' +
208
+ '<div id="' + 'row' + edcal._wDate.toString("ddMMMyyyy") + 'row" class="row">';
209
+ for (var i = 0; i < 7; i++) {
210
+ newrow +='<div id="' + _date.toString("ddMMMyyyy") + '" class="day ' + _date.toString("dddd").toLowerCase() + ' ' +
211
+ _date.toString("MMM").toLowerCase() + '">';
212
+
213
+ newrow += '<div class="dayobj">';
214
+
215
+ newrow += '<div class="daylabel">';
216
+ if (_date.toString("dd") == "01") {
217
+ newrow += _date.toString("MMM d");
218
+ } else {
219
+ newrow += _date.toString("d");
220
+ }
221
+ newrow += '</div>';
222
+
223
+ newrow += '<ul class="postlist">';
224
+
225
+ newrow += edcal.getPostItems(_date.toString("ddMMMyyyy"));
226
+
227
+ newrow += '</ul>';
228
+
229
+ newrow += '</div>';
230
+ newrow += '</div>';
231
+ _date.add(1).days();
232
+ }
233
+
234
+ newrow += '</div></div';
235
+
236
+ if (append) {
237
+ parent.append(newrow);
238
+
239
+ } else {
240
+ parent.prepend(newrow);
241
+ }
242
+
243
+ edcal.alignGrid("#row" + edcal._wDate.toString("ddMMMyyyy") + "row", 7, 14.2, 100, 0.25);
244
+
245
+ jQuery('#row' + edcal._wDate.toString("ddMMMyyyy") + ' .day').each( function() {
246
+ edcal.addTooltip(jQuery(this).attr("id"));
247
+ });
248
+
249
+ edcal.draggablePost('#row' + edcal._wDate.toString("ddMMMyyyy") + ' .post');
250
+
251
+ jQuery('#row' + edcal._wDate.toString("ddMMMyyyy") + ' .day').droppable({
252
+ hoverClass: 'day-active',
253
+ accept: '.post',
254
+ greedy: true,
255
+ tolerance: 'pointer',
256
+ drop: function(event, ui) {
257
+ //output('dropped ui.draggable.attr("id"): ' + ui.draggable.attr("id"));
258
+ //output('dropped on jQuery(this).attr("id"): ' + jQuery(this).attr("id"));
259
+ //output('ui.draggable.html(): ' + ui.draggable.html());
260
+
261
+ var dayId = ui.draggable.parent().parent().parent().attr("id");
262
+
263
+ // Step 0. Get the post object from the map
264
+ var post = edcal.findPostForId(ui.draggable.parent().parent().parent().attr("id"), ui.draggable.attr("id"));
265
+ output("post: " + post);
266
+
267
+ // Step 1. Remove the post from the posts map
268
+ edcal.removePostFromMap(ui.draggable.parent().parent().attr("id"),
269
+ ui.draggable.attr("id"));
270
+
271
+ // Step 2. Remove the old element from the old parent.
272
+ jQuery('#' + ui.draggable.attr("id")).remove();
273
+
274
+ // Step 3. Add the item to the new DOM parent
275
+ jQuery('#' + jQuery(this).attr("id") + ' .postlist').append(edcal.createPostItem(post,
276
+ jQuery(this).attr("id")));
277
+
278
+ // Step 4. Don't forget to make the new item draggable
279
+ //jQuery('#' + jQuery(this).attr("id") + ' .post').draggable({ revert: 'invalid'});
280
+
281
+ // Step 5. And add the tooltip
282
+ edcal.addTooltip(jQuery(this).attr("id"));
283
+
284
+ if (dayId == jQuery(this).attr("id")) {
285
+ /*
286
+ * If they dropped back on to the day they started with we
287
+ * don't want to go back to the server.
288
+ */
289
+ edcal.draggablePost('#' + jQuery(this).attr("id") + ' .post');
290
+ } else {
291
+ // Step6. Update the date on the server
292
+ edcal.changeDate(jQuery(this).attr("id"), post);
293
+ }
294
+ }
295
+ });
296
+
297
+ return jQuery('row' + edcal._wDate.toString("ddMMMyyyy"));
298
+ },
299
+
300
+ draggablePost: function(/*post selector*/ post) {
301
+ jQuery(post).draggable({
302
+ revert: 'invalid',
303
+ appendTo: 'body',
304
+ helper: "clone",
305
+ addClasses: false
306
+ });
307
+ },
308
+
309
+ /*
310
+ This is a utility method to find a post and remove it
311
+ from the cache map.
312
+ */
313
+ removePostFromMap: function(/*string*/ dayobjId, /*string*/ postId) {
314
+ if (edcal.posts[dayobjId]) {
315
+ for (var i = 0; i < edcal.posts[dayobjId].length; i++) {
316
+ if (edcal.posts[dayobjId][i] &&
317
+ "post-" + edcal.posts[dayobjId][i].POST_ID === postId) {
318
+ edcal.posts[dayobjId][i] = null;
319
+ return true;
320
+ }
321
+ }
322
+ }
323
+
324
+ return false;
325
+ },
326
+
327
+ /*
328
+ * Adds a post to al already existing calendar day.
329
+ */
330
+ addPostItem: function(/*post*/ post, /*string*/ dayobjId) {
331
+ jQuery('#' + dayobjId + ' .postlist').append(edcal.createPostItem(post, dayobjId));
332
+ },
333
+
334
+ addPostItemDragAndToolltip: function(/*string*/ dayobjId) {
335
+ edcal.draggablePost('#' + dayobjId + ' .post');
336
+ edcal.addTooltip(dayobjId);
337
+ },
338
+
339
+ confirmDelete: function(/*string*/ posttitle) {
340
+ if (confirm('You are about to delete this post ' + posttitle + '.\n\n Press cancel to stop, OK to delete.')) {
341
+ return true;
342
+ } else {
343
+ return false;
344
+ }
345
+ },
346
+
347
+ /*
348
+ * Adds a tooltip to every post in the specified day.
349
+ */
350
+ addTooltip: function(/*string*/ dayobjId) {
351
+ jQuery('#' + dayobjId + ' .post').tooltip({
352
+ delay: 1500,
353
+ bodyHandler: function() {
354
+ var post = edcal.findPostForId(dayobjId, jQuery(this).attr("id"));
355
+ edcal.findPostForId(jQuery(this).parent().parent().attr("id"),
356
+ jQuery(this).attr("id"));
357
+ var tooltip = '<div class="tooltip">' +
358
+ '<h3>' + post.title + '</h3>' +
359
+ '<p>' +
360
+ '<i>by</i> ' + post.author + ' <i>on</i> ' +
361
+ edcal.getDayFromDayId(post.date).toString("MMMM d, yyyy") +
362
+ '</p>' +
363
+ '<p>' +
364
+ 'Status<b>: ' + post.status + '</b>' +
365
+ '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
366
+ if (post.editlink) {
367
+ /*
368
+ * If the user doesn't have permission to edit a post then
369
+ * then server won't send the edit link URL and we shouldn't
370
+ * show the edit link
371
+ */
372
+ tooltip += '<a href="' + post.editlink + '" title="Edit ' + post.title +
373
+ '">Edit</a>&nbsp; | &nbsp;';
374
+ }
375
+
376
+ if (post.dellink) {
377
+ tooltip += '<a class="submitdelete" href="' + post.dellink + '" ' +
378
+ 'onclick="return edcal.confirmDelete(\'' + post.title + '\');"' +
379
+ 'title="Delete ' + post.title + '">Delete</a> &nbsp; | &nbsp;';
380
+ }
381
+
382
+ tooltip += '<a href="' + post.permalink + '" title="View ' + post.title + '">View</a>' +
383
+ '</p>' +
384
+ '</div>';
385
+
386
+ return tooltip;
387
+ }
388
+ });
389
+ },
390
+
391
+ /*
392
+ Creates the HTML for a post item and adds the data for
393
+ the post to the posts cache.
394
+ */
395
+ createPostItem: function(/*post*/ post, /*string*/ dayobjId) {
396
+ var postHtml = edcal.getPostItemString(post);
397
+ if (!edcal.posts[dayobjId]) {
398
+ edcal.posts[dayobjId] = new Array(0);
399
+ }
400
+
401
+ edcal.posts[dayobjId][edcal.posts[dayobjId].length] = post;
402
+
403
+ return postHtml;
404
+ },
405
+
406
+ /*
407
+ Finds the post object for the specified post ID in the
408
+ specified day.
409
+ */
410
+ findPostForId: function(/*string*/ dayobjId, /*string*/ postId) {
411
+ if (edcal.posts[dayobjId]) {
412
+ for (var i = 0; i < edcal.posts[dayobjId].length; i++) {
413
+ if (edcal.posts[dayobjId][i] &&
414
+ "post-" + edcal.posts[dayobjId][i].id === postId) {
415
+ return edcal.posts[dayobjId][i];
416
+ }
417
+ }
418
+ }
419
+ },
420
+
421
+ /*
422
+ * Removes a post from the HTML and the posts cache.
423
+ */
424
+ removePostItem: function(/*string*/ dayobjId, /*string*/ postId) {
425
+ if (edcal.findPostForId(dayobjId, postId)) {
426
+ for (var i = 0; i < edcal.posts[dayobjId].length; i++) {
427
+ if (edcal.posts[dayobjId][i] &&
428
+ "post-" + edcal.posts[dayobjId][i].id === postId) {
429
+ edcal.posts[dayobjId][i] = null;
430
+ jQuery("#" + postId).remove();
431
+ }
432
+ }
433
+
434
+ }
435
+ },
436
+
437
+ /*
438
+ Gets all the post items for the specified day from
439
+ the post cache.
440
+
441
+ TODO - This function adds all of the posts to a
442
+ specific day in the calendar. If there are too
443
+ many posts they'll overrun the calendar day. We
444
+ need a better UI solution.
445
+ */
446
+ getPostItems: function(/*string*/ dayobjId) {
447
+ var postsString = "";
448
+
449
+ if (edcal.posts[dayobjId]) {
450
+ for (var i = 0; i < edcal.posts[dayobjId].length; i++) {
451
+ if (edcal.posts[dayobjId][i]) {
452
+ postsString += edcal.getPostItemString(edcal.posts[dayobjId][i]);
453
+ }
454
+ }
455
+ }
456
+
457
+ return postsString;
458
+ },
459
+
460
+ /*
461
+ * Gets the HTML string for a post.
462
+ */
463
+ getPostItemString: function(/*post*/ post) {
464
+ return '<li id="post-' + post.id + '" class="post ' + post.status + '">' + post.title + '</li>';
465
+ },
466
+
467
+ /*
468
+ Finds the calendar cell for the current day and adds the
469
+ class "today" to that cell.
470
+ */
471
+ setClassforToday: function() {
472
+ /*
473
+ We want to set a class for the cell that represents the current day so we ca
474
+ give it a background color.
475
+ */
476
+ jQuery('#' + Date.today().toString("ddMMMyyyy")).addClass("today");
477
+ },
478
+
479
+ /*
480
+ Most browsers need us to set a calendar height in pixels instead
481
+ of percent. This function get the correct pixel height for the
482
+ calendar based on the window height.
483
+ */
484
+ getCalHeight: function() {
485
+ var myHeight = 0;
486
+ if ( typeof( window.innerWidth ) == 'number' ) {
487
+ //Non-IE
488
+ myHeight = window.innerHeight;
489
+ } else if ( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
490
+ //IE 6+ in 'standards compliant mode'
491
+ myHeight = document.documentElement.clientHeight;
492
+ } else if ( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
493
+ //IE 4 compatible
494
+ myHeight = document.body.clientHeight;
495
+ }
496
+ return myHeight - 150;
497
+ },
498
+
499
+ /*
500
+ Moves the calendar a certain number of steps in the specified direction.
501
+ True moves the calendar down into the future and false moves the calendar
502
+ up into the past.
503
+ */
504
+ move: function(steps, direction) {
505
+ /*
506
+ The working date is a marker for the last calendar row we created.
507
+ If we are moving forward that will be the last row, if we are moving
508
+ backward it will be the first row. If we switch direction we need
509
+ to bump up our date by 11 rows times 7 days a week or 77 days.
510
+ */
511
+ if (edcal.currentDirection != direction) {
512
+ if (direction) { // into the future
513
+ edcal._wDate = edcal._wDate.add(77).days();
514
+ } else { // into the past
515
+ edcal._wDate = edcal._wDate.add(-77).days();
516
+ }
517
+
518
+ edcal.steps = 0;
519
+ edcal.moveDate = edcal._wDate;
520
+ }
521
+
522
+ edcal.currentDirection = direction;
523
+
524
+
525
+ if (direction) {
526
+ for (var i = 0; i < steps; i++) {
527
+ jQuery("#cal > div:first").remove();
528
+ edcal.createRow(jQuery("#cal"), true);
529
+ edcal._wDate.add(7).days();
530
+ }
531
+ edcal.alignCal();
532
+ } else {
533
+ for (var i = 0; i < steps; i++) {
534
+ jQuery("#cal > div:last").remove();
535
+ edcal.createRow(jQuery("#cal"), false);
536
+ edcal._wDate.add(-7).days();
537
+ }
538
+ edcal.alignCal();
539
+ }
540
+
541
+ edcal.setClassforToday();
542
+ edcal.setDateLabel();
543
+
544
+ /*
545
+ * If the user clicks quickly or uses the mouse wheel they can
546
+ * get a lot of move events very quickly and we need to batch
547
+ * them up together. We set a timeout and clear it if there is
548
+ * another move before the timeout happens.
549
+ */
550
+ edcal.steps += steps;
551
+ if (edcal.tID) {
552
+ clearTimeout(edcal.tID);
553
+ } else {
554
+ edcal.moveDate = edcal._wDate;
555
+ }
556
+
557
+ edcal.tID = setTimeout(function() {
558
+ /*
559
+ * Now that we are done moving the calendar we need to get the posts for the
560
+ * new dates. We want to load the posts between the place the calendar was
561
+ * at when the user started moving it and the place the calendar is at now.
562
+ */
563
+ if (!direction) {
564
+ edcal.getPosts(edcal._wDate.clone(),
565
+ edcal._wDate.clone().add(7 * (edcal.steps + 2)).days());
566
+ } else {
567
+ edcal.getPosts(edcal._wDate.clone().add(-7 * (edcal.steps + 2)).days(),
568
+ edcal._wDate.clone());
569
+ }
570
+
571
+ edcal.steps = 0;
572
+ edcal.tID = null;
573
+ edcal.moveDate = edcal._wDate;
574
+ }, 1000);
575
+ },
576
+
577
+ /*
578
+ We use the date as the ID for day elements, but the Date
579
+ library can't parse the date without spaces and using
580
+ spaces in IDs can cause problems. We work around the
581
+ issue by adding the spaces back before we parse.
582
+ */
583
+ getDayFromDayId: function(/*dayId*/ day) {
584
+ return Date.parse(day.substring(0, 2) + ' ' + day.substring(2, 5) + ' ' + day.substring(5));
585
+ },
586
+
587
+ /*
588
+ This is a helper method to set the date label on the top of
589
+ the calendar. It looks like November 2009-December2009
590
+ */
591
+ setDateLabel: function(year) {
592
+ var api = jQuery(".edcal_scrollable").scrollable();
593
+ var items = api.getVisibleItems();
594
+
595
+ /*
596
+ We need to get the first day in the first week and the
597
+ last day in the last week. We call children twice to
598
+ work around a small JQuery issue.
599
+ */
600
+ var firstDate = edcal.getDayFromDayId(items.eq(0).children(".row").children(".day:first").attr("id"));
601
+ var lastDate = edcal.getDayFromDayId(items.eq(items.length - 1).children(".row").children(".day:last").attr("id"));
602
+
603
+ jQuery("#currentRange").text(firstDate.toString("MMMM yyyy") + " - " + lastDate.toString("MMMM yyyy"));
604
+ },
605
+
606
+ /*
607
+ * Moves the calendar to the specified date.
608
+ */
609
+ moveTo : function(/*Date*/ date) {
610
+ edcal.isMoving = true;
611
+ jQuery("#cal").empty();
612
+
613
+ /*
614
+ When we first start up our working date is 4 weeks before
615
+ the next Sunday.
616
+ */
617
+ edcal._wDate = date.next().sunday().add(-28).days();
618
+
619
+ /*
620
+ After we remove and readd all the rows we are back to
621
+ moving in a going down direction.
622
+ */
623
+ edcal.currentDirection = true;
624
+
625
+ for (var i = 0; i < 10; i++) {
626
+ edcal.createRow(jQuery("#cal"), true);
627
+ edcal._wDate.add(7).days();
628
+ }
629
+
630
+ edcal.alignCal();
631
+
632
+ var api = jQuery(".edcal_scrollable").scrollable();
633
+ api.move(2);
634
+
635
+ edcal.setDateLabel();
636
+ edcal.setClassforToday();
637
+ edcal.isMoving = false;
638
+
639
+ },
640
+
641
+ /*
642
+ * Initializes the calendar
643
+ */
644
+ init : function() {
645
+ if (jQuery("#edcal_scrollable").length === 0) {
646
+ /*
647
+ * This means we are on a page without the editorial
648
+ * calendar
649
+ */
650
+ return;
651
+ }
652
+
653
+ jQuery("#loading").hide();
654
+
655
+ jQuery("#edcal_scrollable").css("height", edcal.getCalHeight() + "px");
656
+ edcal.windowHeight = jQuery(window).height();
657
+
658
+ /*
659
+ * Add the days of the week
660
+ */
661
+ edcal.createDaysHeader();
662
+
663
+ // initialize scrollable
664
+ jQuery(".edcal_scrollable").scrollable({
665
+ vertical:true,
666
+ size: 6,
667
+ keyboardSteps: 1,
668
+ speed: 100
669
+ // use mousewheel plugin
670
+ }).mousewheel();
671
+
672
+ var api = jQuery(".edcal_scrollable").scrollable();
673
+
674
+ edcal.moveTo(Date.today());
675
+
676
+ var steps = 1;
677
+
678
+ /*
679
+ * The scrollable handles some basic binding. This gets us
680
+ * up arrow, down arrow and the mouse wheel.
681
+ */
682
+ api.onBeforeSeek(function(evt, direction) {
683
+ // inside callbacks the "this" variable is a reference to the API
684
+ if (!edcal.isMoving) {
685
+ edcal.move(1, direction);
686
+ }
687
+
688
+ return false;
689
+ });
690
+
691
+ /*
692
+ * We also want to listen for a few other key events
693
+ */
694
+ jQuery(document).bind("keydown", function(evt) {
695
+ //if (evt.altKey || evt.ctrlKey) { return; }
696
+ //output("evt.altKey: " + evt.altKey);
697
+ //output("evt.keyCode: " + evt.keyCode);
698
+ //output("evt.ctrlKey: " + evt.ctrlKey);
699
+
700
+ if ((evt.keyCode === 34 && !(evt.altKey || evt.ctrlKey)) || //page down
701
+ evt.keyCode === 40 && evt.ctrlKey){ // Ctrl+down down arrow
702
+ edcal.move(4, true);
703
+ return false;
704
+ } else if ((evt.keyCode === 33 && !(evt.altKey || evt.ctrlKey)) || //page up
705
+ evt.keyCode === 38 && evt.ctrlKey){ // Ctrl+up up arrow
706
+ edcal.move(4, false);
707
+ return false;
708
+ }
709
+ });
710
+
711
+ edcal.getPosts(Date.today().next().sunday().add(-61).days(),
712
+ Date.today().next().sunday().add(61).days());
713
+
714
+ /*
715
+ Now we bind the listeners for all of our links and the window
716
+ resize.
717
+ */
718
+ jQuery("#moveToToday").click(function() {
719
+ edcal.moveTo(Date.today());
720
+ return false;
721
+ });
722
+
723
+ jQuery("#prevmonth").click(function() {
724
+ edcal.move(4, false);
725
+ return false;
726
+ });
727
+
728
+ jQuery("#nextmonth").click(function() {
729
+ edcal.move(4, true);
730
+ return false;
731
+ });
732
+
733
+ function resizeWindow(e) {
734
+ if (edcal.windowHeight != jQuery(window).height()) {
735
+ jQuery("#edcal_scrollable").css("height", edcal.getCalHeight() + "px");
736
+ edcal.windowHeight = jQuery(window).height();
737
+ }
738
+ }
739
+ jQuery(window).bind("resize", resizeWindow);
740
+ },
741
+
742
+ /*
743
+ This function makes an AJAX call and changes the date of
744
+ the specified post on the server.
745
+ */
746
+ changeDate: function(/*string*/ newdate, /*Post*/ post) {
747
+ output("changeDate(" + newdate + ", " + post + ")");
748
+
749
+ newdate = edcal.getDayFromDayId(newdate).toString("yyyy-MM-dd");
750
+
751
+ var postStatus = "";
752
+
753
+ if (post.status === "draft") {
754
+ /*
755
+ * If the status of the post was a draft we leave it as a draft
756
+ */
757
+ postStatus = "draft";
758
+ } else {
759
+ /*
760
+ If the post status was published or future we need to adjust
761
+ it. If you take a post that is published a move it after
762
+ the current day we change the status to future. If the post
763
+ was scheduled to get published in the future and they drag
764
+ it into the past we change the status to publish.
765
+ */
766
+ var compare = Date.parse(newdate).compareTo(Date.today());
767
+ if (compare === -1) {
768
+ if (post.status === "publish") {
769
+ postStatus = "publish";
770
+ } else {
771
+ postStatus = "draft";
772
+ }
773
+
774
+ } else {
775
+ postStatus = "future";
776
+ }
777
+ }
778
+
779
+ var url = edcal.ajax_url + "?action=edcal_changedate&postid=" + post.id +
780
+ "&postStatus=" + postStatus +
781
+ "&newdate=" + newdate + "&olddate=" + edcal.getDayFromDayId(post.date).toString("yyyy-MM-dd");
782
+
783
+ jQuery("#post-" + post.id).addClass("loadingclass");
784
+
785
+ jQuery.ajax( {
786
+ url: url,
787
+ type: "POST",
788
+ processData: false,
789
+ timeout: 10000,
790
+ dataType: "json",
791
+ success: function(res) {
792
+ edcal.removePostItem(res.post.date, "post-" + res.post.id);
793
+ edcal.addPostItem(res.post, res.post.date);
794
+ edcal.addPostItemDragAndToolltip(res.post.date);
795
+
796
+ if (res.error) {
797
+ if (res.error === edcal.CONCURRENCY_ERROR) {
798
+ edcal.showError("Someone else already moved " + res.post.title);
799
+ } else if (res.error === edcal.PERMISSION_ERROR) {
800
+ edcal.showError("You don't have permission to edit posts");
801
+ }
802
+ }
803
+ },
804
+ error: function(xhr) {
805
+ showError("There was an error contacting your blog.")
806
+ if (xhr.responseText) {
807
+ output("xhr.responseText: " + xhr.responseText);
808
+ }
809
+ }
810
+ });
811
+
812
+ },
813
+
814
+ /*
815
+ * Shows an error message
816
+ */
817
+ showError: function(/*string*/ msg) {
818
+ humanMsg.displayMsg(msg);
819
+ },
820
+
821
+ /*
822
+ Makes an AJAX call to get the posts from the server within the
823
+ specified dates.
824
+ */
825
+ getPosts: function(/*Date*/ from, /*Date*/ to) {
826
+ output("getPosts(" + from.toString("dd-MMM-yyyy") + ", " + to.toString("dd-MMM-yyyy") + ")");
827
+
828
+ var shouldGet = edcal.cacheDates[from];
829
+
830
+ if (shouldGet) {
831
+ /*
832
+ * TODO: We don't want to make extra AJAX calls for dates
833
+ * that we have already coverred. This is cutting down on
834
+ * it somewhat, but we could get much better about this.
835
+ */
836
+ output("We already have the results.");
837
+ return;
838
+ }
839
+
840
+ edcal.cacheDates[from] = true;
841
+
842
+
843
+ var url = edcal.ajax_url + "?action=edcal_posts&from=" + from.toString("yyyy-MM-dd") + "&to=" + to.toString("yyyy-MM-dd");
844
+ output("url: " + url);
845
+
846
+ jQuery("#loading").show();
847
+
848
+ jQuery.ajax( {
849
+ url: url,
850
+ type: "GET",
851
+ processData: false,
852
+ timeout: 10000,
853
+ dataType: "text",
854
+ success: function(res) {
855
+ jQuery("#loading").hide();
856
+
857
+ /*
858
+ * These result here can get pretty large on a busy blog and
859
+ * the JSON parser from JSON.org works faster than the native
860
+ * one used by JQuery.
861
+ */
862
+ var parsedRes = JSON.parseIt(res);
863
+ var postDates = new Array();
864
+ jQuery.each(parsedRes, function(i, post) {
865
+ if (post) {
866
+ edcal.removePostItem(post.date, "post-" + post.id);
867
+ edcal.addPostItem(post, post.date);
868
+ postDates[postDates.length] = post.date;
869
+ }
870
+ });
871
+
872
+ /*
873
+ * If the blog has a very larger number of posts then adding
874
+ * them all can make the UI a little slow. Particularly IE
875
+ * pops up a warning giving the user a chance to abort the
876
+ * script. Adding tooltips and making the items draggable is
877
+ * a lot of what makes things slow. Delaying those two operations
878
+ * makes the UI show up much faster and the user has to wait
879
+ * three seconds before they can drag. It also makes IE
880
+ * stop complaining.
881
+ */
882
+ setTimeout(function() {
883
+ output("adding tooltips and draggables to " + postDates.length + " items.");
884
+ jQuery.each(postDates, function(i, postDate) {
885
+ edcal.addPostItemDragAndToolltip(postDate);
886
+ });
887
+ }, 300);
888
+ },
889
+ error: function(xhr) {
890
+ showError("There was an error contacting your blog.")
891
+ if (xhr.responseText) {
892
+ output("xhr.responseText: " + xhr.responseText);
893
+ }
894
+ }
895
+ });
896
+ }
897
+ };
898
+
899
+ jQuery(document).ready(function(){
900
+ edcal.init();
901
+ });
902
+
903
+ /**
904
+ * This is a helper function to output messages on the HTML page. It
905
+ * write them out to a text area.
906
+ *
907
+ * @param msg the message to write
908
+ */
909
+ function output(msg) {
910
+ //console.info(msg);
911
+ }
edcal.php ADDED
@@ -0,0 +1,374 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*******************************************************************************
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ ******************************************************************************/
17
+
18
+ /*
19
+ Plugin Name: WordPress Editorial Calendar
20
+ Description: An editorial calendar for setting the dates of your WordPress posts
21
+ Author: EdCal Project
22
+ Author URI: TBD
23
+ */
24
+
25
+ add_action('wp_ajax_edcal_changedate', 'edcal_changedate' );
26
+ add_action('admin_menu', 'edcal_list_add_management_page');
27
+ add_action('wp_ajax_edcal_posts', 'edcal_posts' );
28
+ add_action("admin_print_scripts", 'edcal_scripts');
29
+
30
+ /*
31
+ * This function adds our calendar page to the admin UI
32
+ */
33
+ function edcal_list_add_management_page( ) {
34
+ if ( function_exists('add_management_page') ) {
35
+ $page = add_posts_page( 'Calendar', 'Calendar', 'manage_categories', 'posts_list', 'edcal_list_admin' );
36
+ }
37
+ }
38
+
39
+ /*
40
+ * This is a utility function to open a file add it to our
41
+ * output stream. We use this to embed JavaScript and CSS
42
+ * files and cut down on the number of HTTP requests.
43
+ */
44
+ function echoEdCalFile($myFile) {
45
+ $fh = fopen($myFile, 'r');
46
+ $theData = fread($fh, filesize($myFile));
47
+ fclose($fh);
48
+ echo $theData;
49
+ }
50
+
51
+ /*
52
+ * This is the function that generates our admin page. It adds the CSS files and
53
+ * generates the divs that we need for the JavaScript to work.
54
+ */
55
+ function edcal_list_admin() {
56
+ include_once('edcal.php');
57
+
58
+ /*
59
+ * This section of code embeds certain CSS and
60
+ * JavaScript files into the HTML. This has the
61
+ * advantage of fewer HTTP requests, but the
62
+ * disadvantage that the browser can't cache the
63
+ * results. We only do this for files that will
64
+ * be used on this page and nowhere else.
65
+ */
66
+
67
+ echo '<!-- This is the styles from jquery.tooltip.css -->';
68
+ echo '<style type="text/css">';
69
+ echoEdCalFile(dirname( __FILE__ ) . "/lib/jquery.tooltip.css");
70
+ echo '</style>';
71
+
72
+ echo '<!-- This is the styles from humanmsg.css -->';
73
+ echo '<style type="text/css">';
74
+ echoEdCalFile(dirname( __FILE__ ) . "/lib/humanmsg.css");
75
+ echo '</style>';
76
+
77
+ echo '<!-- This is the styles from edcal.css -->';
78
+ echo '<style type="text/css">';
79
+ echoEdCalFile(dirname( __FILE__ ) . "/edcal.css");
80
+ echo '</style>';
81
+
82
+ ?>
83
+ <!-- This is just a little script so we can pass the AJAX URL -->
84
+ <script type="text/javascript">
85
+ jQuery(document).ready(function(){
86
+ edcal.ajax_url = '<?php echo admin_url("admin-ajax.php"); ?>';
87
+ });
88
+ </script>
89
+
90
+ <style type="text/css">
91
+ .loadingclass {
92
+ background-image: url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/images/loading_post.gif")); ?>');
93
+ }
94
+
95
+ #loading {
96
+ background-image: url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/images/loading.gif")); ?>');
97
+ }
98
+ </style>
99
+ <?php
100
+
101
+ echo '<!-- This is the code from edcal.js -->';
102
+ echo '<script type="text/javascript">';
103
+ echoEdCalFile(dirname( __FILE__ ) . "/edcal.js");
104
+ echo '</script>';
105
+
106
+ ?>
107
+ <div class="wrap">
108
+ <div class="icon32" id="icon-edit"><br/></div>
109
+ <h2>Posts Calendar</h2>
110
+
111
+ <div id="topbar">
112
+ <div id="topleft">
113
+ <div id="loading"> </div>
114
+ </div>
115
+ <div id="topright">
116
+ <a href="#" class="prev page-numbers" id="prevmonth" style="border: none";>&laquo;</a><span id="currentRange"></span><a href="#" class="next page-numbers" id="nextmonth" style="border: none";>&raquo;</a> <a href="#" id="moveToToday">Jump to Today</a>
117
+ </div>
118
+ </div>
119
+
120
+ <div id="rowhead"></div>
121
+ <div id="edcal_scrollable" class="edcal_scrollable vertical">
122
+ <div id="cal"></div>
123
+ </div>
124
+ </div>
125
+ <?php
126
+ }
127
+
128
+ /*
129
+ * We use these variables to hold the post dates for the filter when
130
+ * we do our post query.
131
+ */
132
+ $edcal_startDate;
133
+ $edcal_endDate;
134
+
135
+ /*
136
+ * When we get a set of posts to populate the calendar we don't want
137
+ * to get all of the posts. This filter allows us to specify the dates
138
+ * we want.
139
+ */
140
+ function edcal_filter_where($where = '') {
141
+ global $edcal_startDate, $edcal_endDate;
142
+ //posts in the last 30 days
143
+ //$where .= " AND post_date > '" . date('Y-m-d', strtotime('-30 days')) . "'";
144
+ //posts 30 to 60 days old
145
+ //$where .= " AND post_date >= '" . date('Y-m-d', strtotime('-60 days')) . "'" . " AND post_date <= '" . date('Y-m-d', strtotime('-30 days')) . "'";
146
+ //posts for March 1 to March 15, 2009
147
+ $where .= " AND post_date >= '" . $edcal_startDate . "' AND post_date < '" . $edcal_endDate . "'";
148
+ return $where;
149
+ }
150
+
151
+ /*
152
+ * This function adds all of the JavaScript files we need.
153
+ *
154
+ * TODO: This list is way too long. We need to minimize and
155
+ * combine most of these files. I'm still on the fence if we
156
+ * should include these files inline.
157
+ */
158
+ function edcal_scripts( ) {
159
+ wp_enqueue_script( "ui-core", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/ui.core.js"), array( 'jquery' ) );
160
+ wp_enqueue_script( "ui-draggable", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/ui.draggable.js"), array( 'jquery' ) );
161
+ wp_enqueue_script( "ui-droppable", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/ui.droppable.js"), array( 'jquery' ) );
162
+
163
+
164
+ wp_enqueue_script( "bgiframe", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/jquery.bgiframe.js"), array( 'jquery' ) );
165
+ wp_enqueue_script( "tooltip", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/jquery.tooltip.js"), array( 'jquery' ) );
166
+ wp_enqueue_script( "humanMsg", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/humanmsg.js"), array( 'jquery' ) );
167
+
168
+
169
+ wp_enqueue_script( "date", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/date.js"), array( 'jquery' ) );
170
+ wp_enqueue_script( "scrollable", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/tools.scrollable-1.1.2.js"), array( 'jquery' ) );
171
+ wp_enqueue_script( "mouse-wheel", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/tools.scrollable.mousewheel-1.0.1.js"), array( 'jquery' ) );
172
+
173
+ wp_enqueue_script( "json-parse2", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/json2.js"), array( 'jquery' ) );
174
+ }
175
+
176
+ /*
177
+ * This is an AJAX call that gets the posts between the from date
178
+ * and the to date.
179
+ */
180
+ function edcal_posts( ) {
181
+ header("Content-Type: application/json");
182
+ global $edcal_startDate, $edcal_endDate;
183
+ $edcal_startDate = isset($_GET['from'])?$_GET['from']:null;
184
+ $edcal_endDate = isset($_GET['to'])?$_GET['to']:null;
185
+ global $post;
186
+ $args = array(
187
+ 'posts_per_page' => -1,
188
+ 'post_status' => "publish&future&draft",
189
+ 'post_parent' => null, // any parent
190
+ 'post_type' => 'post',
191
+ );
192
+
193
+ add_filter('posts_where', 'edcal_filter_where');
194
+ $myposts = query_posts($args);
195
+ remove_filter('posts_where', 'edcal_filter_where');
196
+
197
+ ?>[
198
+ <?php
199
+ foreach($myposts as $post) {
200
+ edcal_postJSON($post);
201
+ }
202
+
203
+ ?> ]
204
+ <?php
205
+ die;
206
+ }
207
+
208
+ /*
209
+ * This function sets up the post data and prints out the values we
210
+ * care about in a JSON data structure. This prints out just the
211
+ * value part.
212
+ */
213
+ function edcal_postJSON($post) {
214
+ setup_postdata($post);
215
+ ?>
216
+ {
217
+ "date" : "<?php the_time('d') ?><?php the_time('M') ?><?php the_time('Y') ?>",
218
+ "url" : "<?php the_permalink(); ?>",
219
+ "status" : "<?php echo(get_post_status()); ?>",
220
+ "title" : "<?php the_title(); ?>",
221
+ "author" : "<?php the_author(); ?>",
222
+ <?php if ( current_user_can('edit_post', $post->ID) ) {?>
223
+ "editlink" : "<?php echo(get_edit_post_link($id)); ?>",
224
+ <?php } ?>
225
+
226
+ <?php if ( current_user_can('delete_post', $post->ID) ) {?>
227
+ "dellink" : "<?php echo(wp_nonce_url("post.php?action=delete&amp;post=$post->ID", 'delete-post_' . $post->ID)); ?>",
228
+ <?php } ?>
229
+
230
+ "permalink" : "<?php echo(get_permalink($id)); ?>",
231
+ "id" : "<?php the_ID(); ?>"
232
+ },
233
+ <?php
234
+ }
235
+
236
+ /*
237
+ * This function changes the date on a post. It does optimistic
238
+ * concurrency checking by comparing the original post date from
239
+ * the browser with the one from the database. If they don't match
240
+ * then it returns an error code and the updated post data.
241
+ *
242
+ * If the call is successful then it returns the updated post data.
243
+ */
244
+ function edcal_changedate() {
245
+ if (!current_user_can('edit_others_posts')) {
246
+ /*
247
+ * This is just a sanity check to make sure that the current
248
+ * user has permission to edit posts. Most of the time this
249
+ * will never be run because you can't see the calendar unless
250
+ * you are at least an editor
251
+ */
252
+ ?>
253
+ {
254
+ "error": 5,
255
+ <?php
256
+
257
+
258
+ global $post;
259
+ $args = array(
260
+ 'posts_id' => $edcal_postid,
261
+ );
262
+
263
+ $post = get_post($edcal_postid);
264
+ ?>
265
+ "post" :
266
+ <?php
267
+ edcal_postJSON($post);
268
+
269
+
270
+
271
+ ?> }
272
+ <?php
273
+ }
274
+
275
+ header("Content-Type: application/json");
276
+ global $edcal_startDate, $edcal_endDate;
277
+ $edcal_postid = isset($_GET['postid'])?$_GET['postid']:null;
278
+ $edcal_newDate = isset($_GET['newdate'])?$_GET['newdate']:null;
279
+ $edcal_oldDate = isset($_GET['olddate'])?$_GET['olddate']:null;
280
+ $edcal_postStatus = isset($_GET['postStatus'])?$_GET['postStatus']:null;
281
+
282
+ $post = get_post($edcal_postid, ARRAY_A);
283
+ setup_postdata($post);
284
+
285
+ $matches = strpos($post['post_date'], $edcal_oldDate) === 0;
286
+
287
+ /*
288
+ * We are doing optimistic concurrency checking on the dates. If
289
+ * the user tries to move a post we want to make sure nobody else
290
+ * has moved that post since the page was last updated. If the
291
+ * old date in the database doesn't match the old date from the
292
+ * browser then we return an error to the browser along with the
293
+ * updated post data.
294
+ */
295
+ if ($matches != 1) {
296
+ ?>
297
+ {
298
+ "error": 4,
299
+ <?php
300
+
301
+
302
+ global $post;
303
+ $args = array(
304
+ 'posts_id' => $edcal_postid,
305
+ );
306
+
307
+ $post = get_post($edcal_postid);
308
+ ?>
309
+ "post" :
310
+ <?php
311
+ edcal_postJSON($post);
312
+
313
+ ?> }
314
+ <?php
315
+ //return new WP_Error('broke', __("no match error"));
316
+ die();
317
+ }
318
+
319
+ /*
320
+ * Posts in WordPress have more than one date. There is the GMT date,
321
+ * the date in the local time zone, the modified date in GMT and the
322
+ * modified date in the local time zone. We update all of them.
323
+ */
324
+ $updated_post = array();
325
+ $updated_post['ID'] = $edcal_postid;
326
+ $updated_post['post_date'] = $edcal_newDate . substr($post['post_date'], strlen($edcal_newDate));
327
+ $updated_post['post_date_gmt'] = $edcal_newDate . substr($post['post_date_gmt'], strlen($edcal_newDate));
328
+ $updated_post['post_modified'] = $edcal_newDate . substr($post['post_modified'], strlen($edcal_newDate));
329
+ $updated_post['post_modified_gmt'] = $edcal_newDate . substr($post['post_modified_gmt'], strlen($edcal_newDate));
330
+
331
+ if ( $edcal_postStatus != $post['post_status'] ) {
332
+ /*
333
+ * We only want to update the post status if it has changed.
334
+ * If the post status has changed that takes a few more steps
335
+ */
336
+ wp_transition_post_status($edcal_postStatus, $post['post_status'], $post);
337
+ $updated_post['post_status'] = $edcal_postStatus;
338
+
339
+ // Update counts for the post's terms.
340
+ foreach ( (array) get_object_taxonomies('post') as $taxonomy ) {
341
+ $tt_ids = wp_get_object_terms($post_id, $taxonomy, 'fields=tt_ids');
342
+ wp_update_term_count($tt_ids, $taxonomy);
343
+ }
344
+
345
+ do_action('edit_post', $edcal_postid, $post);
346
+ do_action('save_post', $edcal_postid, $post);
347
+ do_action('wp_insert_post', $edcal_postid, $post);
348
+ }
349
+
350
+ /*
351
+ * Now we finally update the post into the database
352
+ */
353
+ wp_update_post( $updated_post );
354
+
355
+ /*
356
+ * We finish by returning the latest data for the post in the JSON
357
+ */
358
+ global $post;
359
+ $args = array(
360
+ 'posts_id' => $edcal_postid,
361
+ );
362
+
363
+ $post = get_post($edcal_postid);
364
+ ?>{
365
+ "post" :
366
+ <?php
367
+ edcal_postJSON($post);
368
+ ?>
369
+ }
370
+ <?php
371
+
372
+
373
+ die;
374
+ }
images/loading.gif ADDED
Binary file
images/loading_post.gif ADDED
Binary file
lib/date.js ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Version: 1.0 Alpha-1
3
+ * Build Date: 13-Nov-2007
4
+ * Copyright (c) 2006-2007, Coolite Inc. (http://www.coolite.com/). All rights reserved.
5
+ * License: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/.
6
+ * Website: http://www.datejs.com/ or http://www.coolite.com/datejs/
7
+ */
8
+ Date.CultureInfo={name:"en-US",englishName:"English (United States)",nativeName:"English (United States)",dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],abbreviatedDayNames:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],shortestDayNames:["Su","Mo","Tu","We","Th","Fr","Sa"],firstLetterDayNames:["S","M","T","W","T","F","S"],monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],abbreviatedMonthNames:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],amDesignator:"AM",pmDesignator:"PM",firstDayOfWeek:0,twoDigitYearMax:2029,dateElementOrder:"mdy",formatPatterns:{shortDate:"M/d/yyyy",longDate:"dddd, MMMM dd, yyyy",shortTime:"h:mm tt",longTime:"h:mm:ss tt",fullDateTime:"dddd, MMMM dd, yyyy h:mm:ss tt",sortableDateTime:"yyyy-MM-ddTHH:mm:ss",universalSortableDateTime:"yyyy-MM-dd HH:mm:ssZ",rfc1123:"ddd, dd MMM yyyy HH:mm:ss GMT",monthDay:"MMMM dd",yearMonth:"MMMM, yyyy"},regexPatterns:{jan:/^jan(uary)?/i,feb:/^feb(ruary)?/i,mar:/^mar(ch)?/i,apr:/^apr(il)?/i,may:/^may/i,jun:/^jun(e)?/i,jul:/^jul(y)?/i,aug:/^aug(ust)?/i,sep:/^sep(t(ember)?)?/i,oct:/^oct(ober)?/i,nov:/^nov(ember)?/i,dec:/^dec(ember)?/i,sun:/^su(n(day)?)?/i,mon:/^mo(n(day)?)?/i,tue:/^tu(e(s(day)?)?)?/i,wed:/^we(d(nesday)?)?/i,thu:/^th(u(r(s(day)?)?)?)?/i,fri:/^fr(i(day)?)?/i,sat:/^sa(t(urday)?)?/i,future:/^next/i,past:/^last|past|prev(ious)?/i,add:/^(\+|after|from)/i,subtract:/^(\-|before|ago)/i,yesterday:/^yesterday/i,today:/^t(oday)?/i,tomorrow:/^tomorrow/i,now:/^n(ow)?/i,millisecond:/^ms|milli(second)?s?/i,second:/^sec(ond)?s?/i,minute:/^min(ute)?s?/i,hour:/^h(ou)?rs?/i,week:/^w(ee)?k/i,month:/^m(o(nth)?s?)?/i,day:/^d(ays?)?/i,year:/^y((ea)?rs?)?/i,shortMeridian:/^(a|p)/i,longMeridian:/^(a\.?m?\.?|p\.?m?\.?)/i,timezone:/^((e(s|d)t|c(s|d)t|m(s|d)t|p(s|d)t)|((gmt)?\s*(\+|\-)\s*\d\d\d\d?)|gmt)/i,ordinalSuffix:/^\s*(st|nd|rd|th)/i,timeContext:/^\s*(\:|a|p)/i},abbreviatedTimeZoneStandard:{GMT:"-000",EST:"-0400",CST:"-0500",MST:"-0600",PST:"-0700"},abbreviatedTimeZoneDST:{GMT:"-000",EDT:"-0500",CDT:"-0600",MDT:"-0700",PDT:"-0800"}};
9
+ Date.getMonthNumberFromName=function(name){var n=Date.CultureInfo.monthNames,m=Date.CultureInfo.abbreviatedMonthNames,s=name.toLowerCase();for(var i=0;i<n.length;i++){if(n[i].toLowerCase()==s||m[i].toLowerCase()==s){return i;}}
10
+ return-1;};Date.getDayNumberFromName=function(name){var n=Date.CultureInfo.dayNames,m=Date.CultureInfo.abbreviatedDayNames,o=Date.CultureInfo.shortestDayNames,s=name.toLowerCase();for(var i=0;i<n.length;i++){if(n[i].toLowerCase()==s||m[i].toLowerCase()==s){return i;}}
11
+ return-1;};Date.isLeapYear=function(year){return(((year%4===0)&&(year%100!==0))||(year%400===0));};Date.getDaysInMonth=function(year,month){return[31,(Date.isLeapYear(year)?29:28),31,30,31,30,31,31,30,31,30,31][month];};Date.getTimezoneOffset=function(s,dst){return(dst||false)?Date.CultureInfo.abbreviatedTimeZoneDST[s.toUpperCase()]:Date.CultureInfo.abbreviatedTimeZoneStandard[s.toUpperCase()];};Date.getTimezoneAbbreviation=function(offset,dst){var n=(dst||false)?Date.CultureInfo.abbreviatedTimeZoneDST:Date.CultureInfo.abbreviatedTimeZoneStandard,p;for(p in n){if(n[p]===offset){return p;}}
12
+ return null;};Date.prototype.clone=function(){return new Date(this.getTime());};Date.prototype.compareTo=function(date){if(isNaN(this)){throw new Error(this);}
13
+ if(date instanceof Date&&!isNaN(date)){return(this>date)?1:(this<date)?-1:0;}else{throw new TypeError(date);}};Date.prototype.equals=function(date){return(this.compareTo(date)===0);};Date.prototype.between=function(start,end){var t=this.getTime();return t>=start.getTime()&&t<=end.getTime();};Date.prototype.addMilliseconds=function(value){this.setMilliseconds(this.getMilliseconds()+value);return this;};Date.prototype.addSeconds=function(value){return this.addMilliseconds(value*1000);};Date.prototype.addMinutes=function(value){return this.addMilliseconds(value*60000);};Date.prototype.addHours=function(value){return this.addMilliseconds(value*3600000);};Date.prototype.addDays=function(value){return this.addMilliseconds(value*86400000);};Date.prototype.addWeeks=function(value){return this.addMilliseconds(value*604800000);};Date.prototype.addMonths=function(value){var n=this.getDate();this.setDate(1);this.setMonth(this.getMonth()+value);this.setDate(Math.min(n,this.getDaysInMonth()));return this;};Date.prototype.addYears=function(value){return this.addMonths(value*12);};Date.prototype.add=function(config){if(typeof config=="number"){this._orient=config;return this;}
14
+ var x=config;if(x.millisecond||x.milliseconds){this.addMilliseconds(x.millisecond||x.milliseconds);}
15
+ if(x.second||x.seconds){this.addSeconds(x.second||x.seconds);}
16
+ if(x.minute||x.minutes){this.addMinutes(x.minute||x.minutes);}
17
+ if(x.hour||x.hours){this.addHours(x.hour||x.hours);}
18
+ if(x.month||x.months){this.addMonths(x.month||x.months);}
19
+ if(x.year||x.years){this.addYears(x.year||x.years);}
20
+ if(x.day||x.days){this.addDays(x.day||x.days);}
21
+ return this;};Date._validate=function(value,min,max,name){if(typeof value!="number"){throw new TypeError(value+" is not a Number.");}else if(value<min||value>max){throw new RangeError(value+" is not a valid value for "+name+".");}
22
+ return true;};Date.validateMillisecond=function(n){return Date._validate(n,0,999,"milliseconds");};Date.validateSecond=function(n){return Date._validate(n,0,59,"seconds");};Date.validateMinute=function(n){return Date._validate(n,0,59,"minutes");};Date.validateHour=function(n){return Date._validate(n,0,23,"hours");};Date.validateDay=function(n,year,month){return Date._validate(n,1,Date.getDaysInMonth(year,month),"days");};Date.validateMonth=function(n){return Date._validate(n,0,11,"months");};Date.validateYear=function(n){return Date._validate(n,1,9999,"seconds");};Date.prototype.set=function(config){var x=config;if(!x.millisecond&&x.millisecond!==0){x.millisecond=-1;}
23
+ if(!x.second&&x.second!==0){x.second=-1;}
24
+ if(!x.minute&&x.minute!==0){x.minute=-1;}
25
+ if(!x.hour&&x.hour!==0){x.hour=-1;}
26
+ if(!x.day&&x.day!==0){x.day=-1;}
27
+ if(!x.month&&x.month!==0){x.month=-1;}
28
+ if(!x.year&&x.year!==0){x.year=-1;}
29
+ if(x.millisecond!=-1&&Date.validateMillisecond(x.millisecond)){this.addMilliseconds(x.millisecond-this.getMilliseconds());}
30
+ if(x.second!=-1&&Date.validateSecond(x.second)){this.addSeconds(x.second-this.getSeconds());}
31
+ if(x.minute!=-1&&Date.validateMinute(x.minute)){this.addMinutes(x.minute-this.getMinutes());}
32
+ if(x.hour!=-1&&Date.validateHour(x.hour)){this.addHours(x.hour-this.getHours());}
33
+ if(x.month!==-1&&Date.validateMonth(x.month)){this.addMonths(x.month-this.getMonth());}
34
+ if(x.year!=-1&&Date.validateYear(x.year)){this.addYears(x.year-this.getFullYear());}
35
+ if(x.day!=-1&&Date.validateDay(x.day,this.getFullYear(),this.getMonth())){this.addDays(x.day-this.getDate());}
36
+ if(x.timezone){this.setTimezone(x.timezone);}
37
+ if(x.timezoneOffset){this.setTimezoneOffset(x.timezoneOffset);}
38
+ return this;};Date.prototype.clearTime=function(){this.setHours(0);this.setMinutes(0);this.setSeconds(0);this.setMilliseconds(0);return this;};Date.prototype.isLeapYear=function(){var y=this.getFullYear();return(((y%4===0)&&(y%100!==0))||(y%400===0));};Date.prototype.isWeekday=function(){return!(this.is().sat()||this.is().sun());};Date.prototype.getDaysInMonth=function(){return Date.getDaysInMonth(this.getFullYear(),this.getMonth());};Date.prototype.moveToFirstDayOfMonth=function(){return this.set({day:1});};Date.prototype.moveToLastDayOfMonth=function(){return this.set({day:this.getDaysInMonth()});};Date.prototype.moveToDayOfWeek=function(day,orient){var diff=(day-this.getDay()+7*(orient||+1))%7;return this.addDays((diff===0)?diff+=7*(orient||+1):diff);};Date.prototype.moveToMonth=function(month,orient){var diff=(month-this.getMonth()+12*(orient||+1))%12;return this.addMonths((diff===0)?diff+=12*(orient||+1):diff);};Date.prototype.getDayOfYear=function(){return Math.floor((this-new Date(this.getFullYear(),0,1))/86400000);};Date.prototype.getWeekOfYear=function(firstDayOfWeek){var y=this.getFullYear(),m=this.getMonth(),d=this.getDate();var dow=firstDayOfWeek||Date.CultureInfo.firstDayOfWeek;var offset=7+1-new Date(y,0,1).getDay();if(offset==8){offset=1;}
39
+ var daynum=((Date.UTC(y,m,d,0,0,0)-Date.UTC(y,0,1,0,0,0))/86400000)+1;var w=Math.floor((daynum-offset+7)/7);if(w===dow){y--;var prevOffset=7+1-new Date(y,0,1).getDay();if(prevOffset==2||prevOffset==8){w=53;}else{w=52;}}
40
+ return w;};Date.prototype.isDST=function(){console.log('isDST');return this.toString().match(/(E|C|M|P)(S|D)T/)[2]=="D";};Date.prototype.getTimezone=function(){return Date.getTimezoneAbbreviation(this.getUTCOffset,this.isDST());};Date.prototype.setTimezoneOffset=function(s){var here=this.getTimezoneOffset(),there=Number(s)*-6/10;this.addMinutes(there-here);return this;};Date.prototype.setTimezone=function(s){return this.setTimezoneOffset(Date.getTimezoneOffset(s));};Date.prototype.getUTCOffset=function(){var n=this.getTimezoneOffset()*-10/6,r;if(n<0){r=(n-10000).toString();return r[0]+r.substr(2);}else{r=(n+10000).toString();return"+"+r.substr(1);}};Date.prototype.getDayName=function(abbrev){return abbrev?Date.CultureInfo.abbreviatedDayNames[this.getDay()]:Date.CultureInfo.dayNames[this.getDay()];};Date.prototype.getMonthName=function(abbrev){return abbrev?Date.CultureInfo.abbreviatedMonthNames[this.getMonth()]:Date.CultureInfo.monthNames[this.getMonth()];};Date.prototype._toString=Date.prototype.toString;Date.prototype.toString=function(format){var self=this;var p=function p(s){return(s.toString().length==1)?"0"+s:s;};return format?format.replace(/dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|zz?z?/g,function(format){switch(format){case"hh":return p(self.getHours()<13?self.getHours():(self.getHours()-12));case"h":return self.getHours()<13?self.getHours():(self.getHours()-12);case"HH":return p(self.getHours());case"H":return self.getHours();case"mm":return p(self.getMinutes());case"m":return self.getMinutes();case"ss":return p(self.getSeconds());case"s":return self.getSeconds();case"yyyy":return self.getFullYear();case"yy":return self.getFullYear().toString().substring(2,4);case"dddd":return self.getDayName();case"ddd":return self.getDayName(true);case"dd":return p(self.getDate());case"d":return self.getDate().toString();case"MMMM":return self.getMonthName();case"MMM":return self.getMonthName(true);case"MM":return p((self.getMonth()+1));case"M":return self.getMonth()+1;case"t":return self.getHours()<12?Date.CultureInfo.amDesignator.substring(0,1):Date.CultureInfo.pmDesignator.substring(0,1);case"tt":return self.getHours()<12?Date.CultureInfo.amDesignator:Date.CultureInfo.pmDesignator;case"zzz":case"zz":case"z":return"";}}):this._toString();};
41
+ Date.now=function(){return new Date();};Date.today=function(){return Date.now().clearTime();};Date.prototype._orient=+1;Date.prototype.next=function(){this._orient=+1;return this;};Date.prototype.last=Date.prototype.prev=Date.prototype.previous=function(){this._orient=-1;return this;};Date.prototype._is=false;Date.prototype.is=function(){this._is=true;return this;};Number.prototype._dateElement="day";Number.prototype.fromNow=function(){var c={};c[this._dateElement]=this;return Date.now().add(c);};Number.prototype.ago=function(){var c={};c[this._dateElement]=this*-1;return Date.now().add(c);};(function(){var $D=Date.prototype,$N=Number.prototype;var dx=("sunday monday tuesday wednesday thursday friday saturday").split(/\s/),mx=("january february march april may june july august september october november december").split(/\s/),px=("Millisecond Second Minute Hour Day Week Month Year").split(/\s/),de;var df=function(n){return function(){if(this._is){this._is=false;return this.getDay()==n;}
42
+ return this.moveToDayOfWeek(n,this._orient);};};for(var i=0;i<dx.length;i++){$D[dx[i]]=$D[dx[i].substring(0,3)]=df(i);}
43
+ var mf=function(n){return function(){if(this._is){this._is=false;return this.getMonth()===n;}
44
+ return this.moveToMonth(n,this._orient);};};for(var j=0;j<mx.length;j++){$D[mx[j]]=$D[mx[j].substring(0,3)]=mf(j);}
45
+ var ef=function(j){return function(){if(j.substring(j.length-1)!="s"){j+="s";}
46
+ return this["add"+j](this._orient);};};var nf=function(n){return function(){this._dateElement=n;return this;};};for(var k=0;k<px.length;k++){de=px[k].toLowerCase();$D[de]=$D[de+"s"]=ef(px[k]);$N[de]=$N[de+"s"]=nf(de);}}());Date.prototype.toJSONString=function(){return this.toString("yyyy-MM-ddThh:mm:ssZ");};Date.prototype.toShortDateString=function(){return this.toString(Date.CultureInfo.formatPatterns.shortDatePattern);};Date.prototype.toLongDateString=function(){return this.toString(Date.CultureInfo.formatPatterns.longDatePattern);};Date.prototype.toShortTimeString=function(){return this.toString(Date.CultureInfo.formatPatterns.shortTimePattern);};Date.prototype.toLongTimeString=function(){return this.toString(Date.CultureInfo.formatPatterns.longTimePattern);};Date.prototype.getOrdinal=function(){switch(this.getDate()){case 1:case 21:case 31:return"st";case 2:case 22:return"nd";case 3:case 23:return"rd";default:return"th";}};
47
+ (function(){Date.Parsing={Exception:function(s){this.message="Parse error at '"+s.substring(0,10)+" ...'";}};var $P=Date.Parsing;var _=$P.Operators={rtoken:function(r){return function(s){var mx=s.match(r);if(mx){return([mx[0],s.substring(mx[0].length)]);}else{throw new $P.Exception(s);}};},token:function(s){return function(s){return _.rtoken(new RegExp("^\s*"+s+"\s*"))(s);};},stoken:function(s){return _.rtoken(new RegExp("^"+s));},until:function(p){return function(s){var qx=[],rx=null;while(s.length){try{rx=p.call(this,s);}catch(e){qx.push(rx[0]);s=rx[1];continue;}
48
+ break;}
49
+ return[qx,s];};},many:function(p){return function(s){var rx=[],r=null;while(s.length){try{r=p.call(this,s);}catch(e){return[rx,s];}
50
+ rx.push(r[0]);s=r[1];}
51
+ return[rx,s];};},optional:function(p){return function(s){var r=null;try{r=p.call(this,s);}catch(e){return[null,s];}
52
+ return[r[0],r[1]];};},not:function(p){return function(s){try{p.call(this,s);}catch(e){return[null,s];}
53
+ throw new $P.Exception(s);};},ignore:function(p){return p?function(s){var r=null;r=p.call(this,s);return[null,r[1]];}:null;},product:function(){var px=arguments[0],qx=Array.prototype.slice.call(arguments,1),rx=[];for(var i=0;i<px.length;i++){rx.push(_.each(px[i],qx));}
54
+ return rx;},cache:function(rule){var cache={},r=null;return function(s){try{r=cache[s]=(cache[s]||rule.call(this,s));}catch(e){r=cache[s]=e;}
55
+ if(r instanceof $P.Exception){throw r;}else{return r;}};},any:function(){var px=arguments;return function(s){var r=null;for(var i=0;i<px.length;i++){if(px[i]==null){continue;}
56
+ try{r=(px[i].call(this,s));}catch(e){r=null;}
57
+ if(r){return r;}}
58
+ throw new $P.Exception(s);};},each:function(){var px=arguments;return function(s){var rx=[],r=null;for(var i=0;i<px.length;i++){if(px[i]==null){continue;}
59
+ try{r=(px[i].call(this,s));}catch(e){throw new $P.Exception(s);}
60
+ rx.push(r[0]);s=r[1];}
61
+ return[rx,s];};},all:function(){var px=arguments,_=_;return _.each(_.optional(px));},sequence:function(px,d,c){d=d||_.rtoken(/^\s*/);c=c||null;if(px.length==1){return px[0];}
62
+ return function(s){var r=null,q=null;var rx=[];for(var i=0;i<px.length;i++){try{r=px[i].call(this,s);}catch(e){break;}
63
+ rx.push(r[0]);try{q=d.call(this,r[1]);}catch(ex){q=null;break;}
64
+ s=q[1];}
65
+ if(!r){throw new $P.Exception(s);}
66
+ if(q){throw new $P.Exception(q[1]);}
67
+ if(c){try{r=c.call(this,r[1]);}catch(ey){throw new $P.Exception(r[1]);}}
68
+ return[rx,(r?r[1]:s)];};},between:function(d1,p,d2){d2=d2||d1;var _fn=_.each(_.ignore(d1),p,_.ignore(d2));return function(s){var rx=_fn.call(this,s);return[[rx[0][0],r[0][2]],rx[1]];};},list:function(p,d,c){d=d||_.rtoken(/^\s*/);c=c||null;return(p instanceof Array?_.each(_.product(p.slice(0,-1),_.ignore(d)),p.slice(-1),_.ignore(c)):_.each(_.many(_.each(p,_.ignore(d))),px,_.ignore(c)));},set:function(px,d,c){d=d||_.rtoken(/^\s*/);c=c||null;return function(s){var r=null,p=null,q=null,rx=null,best=[[],s],last=false;for(var i=0;i<px.length;i++){q=null;p=null;r=null;last=(px.length==1);try{r=px[i].call(this,s);}catch(e){continue;}
69
+ rx=[[r[0]],r[1]];if(r[1].length>0&&!last){try{q=d.call(this,r[1]);}catch(ex){last=true;}}else{last=true;}
70
+ if(!last&&q[1].length===0){last=true;}
71
+ if(!last){var qx=[];for(var j=0;j<px.length;j++){if(i!=j){qx.push(px[j]);}}
72
+ p=_.set(qx,d).call(this,q[1]);if(p[0].length>0){rx[0]=rx[0].concat(p[0]);rx[1]=p[1];}}
73
+ if(rx[1].length<best[1].length){best=rx;}
74
+ if(best[1].length===0){break;}}
75
+ if(best[0].length===0){return best;}
76
+ if(c){try{q=c.call(this,best[1]);}catch(ey){throw new $P.Exception(best[1]);}
77
+ best[1]=q[1];}
78
+ return best;};},forward:function(gr,fname){return function(s){return gr[fname].call(this,s);};},replace:function(rule,repl){return function(s){var r=rule.call(this,s);return[repl,r[1]];};},process:function(rule,fn){return function(s){var r=rule.call(this,s);return[fn.call(this,r[0]),r[1]];};},min:function(min,rule){return function(s){var rx=rule.call(this,s);if(rx[0].length<min){throw new $P.Exception(s);}
79
+ return rx;};}};var _generator=function(op){return function(){var args=null,rx=[];if(arguments.length>1){args=Array.prototype.slice.call(arguments);}else if(arguments[0]instanceof Array){args=arguments[0];}
80
+ if(args){for(var i=0,px=args.shift();i<px.length;i++){args.unshift(px[i]);rx.push(op.apply(null,args));args.shift();return rx;}}else{return op.apply(null,arguments);}};};var gx="optional not ignore cache".split(/\s/);for(var i=0;i<gx.length;i++){_[gx[i]]=_generator(_[gx[i]]);}
81
+ var _vector=function(op){return function(){if(arguments[0]instanceof Array){return op.apply(null,arguments[0]);}else{return op.apply(null,arguments);}};};var vx="each any all".split(/\s/);for(var j=0;j<vx.length;j++){_[vx[j]]=_vector(_[vx[j]]);}}());(function(){var flattenAndCompact=function(ax){var rx=[];for(var i=0;i<ax.length;i++){if(ax[i]instanceof Array){rx=rx.concat(flattenAndCompact(ax[i]));}else{if(ax[i]){rx.push(ax[i]);}}}
82
+ return rx;};Date.Grammar={};Date.Translator={hour:function(s){return function(){this.hour=Number(s);};},minute:function(s){return function(){this.minute=Number(s);};},second:function(s){return function(){this.second=Number(s);};},meridian:function(s){return function(){this.meridian=s.slice(0,1).toLowerCase();};},timezone:function(s){return function(){var n=s.replace(/[^\d\+\-]/g,"");if(n.length){this.timezoneOffset=Number(n);}else{this.timezone=s.toLowerCase();}};},day:function(x){var s=x[0];return function(){this.day=Number(s.match(/\d+/)[0]);};},month:function(s){return function(){this.month=((s.length==3)?Date.getMonthNumberFromName(s):(Number(s)-1));};},year:function(s){return function(){var n=Number(s);this.year=((s.length>2)?n:(n+(((n+2000)<Date.CultureInfo.twoDigitYearMax)?2000:1900)));};},rday:function(s){return function(){switch(s){case"yesterday":this.days=-1;break;case"tomorrow":this.days=1;break;case"today":this.days=0;break;case"now":this.days=0;this.now=true;break;}};},finishExact:function(x){x=(x instanceof Array)?x:[x];var now=new Date();this.year=now.getFullYear();this.month=now.getMonth();this.day=1;this.hour=0;this.minute=0;this.second=0;for(var i=0;i<x.length;i++){if(x[i]){x[i].call(this);}}
83
+ this.hour=(this.meridian=="p"&&this.hour<13)?this.hour+12:this.hour;if(this.day>Date.getDaysInMonth(this.year,this.month)){throw new RangeError(this.day+" is not a valid value for days.");}
84
+ var r=new Date(this.year,this.month,this.day,this.hour,this.minute,this.second);if(this.timezone){r.set({timezone:this.timezone});}else if(this.timezoneOffset){r.set({timezoneOffset:this.timezoneOffset});}
85
+ return r;},finish:function(x){x=(x instanceof Array)?flattenAndCompact(x):[x];if(x.length===0){return null;}
86
+ for(var i=0;i<x.length;i++){if(typeof x[i]=="function"){x[i].call(this);}}
87
+ if(this.now){return new Date();}
88
+ var today=Date.today();var method=null;var expression=!!(this.days!=null||this.orient||this.operator);if(expression){var gap,mod,orient;orient=((this.orient=="past"||this.operator=="subtract")?-1:1);if(this.weekday){this.unit="day";gap=(Date.getDayNumberFromName(this.weekday)-today.getDay());mod=7;this.days=gap?((gap+(orient*mod))%mod):(orient*mod);}
89
+ if(this.month){this.unit="month";gap=(this.month-today.getMonth());mod=12;this.months=gap?((gap+(orient*mod))%mod):(orient*mod);this.month=null;}
90
+ if(!this.unit){this.unit="day";}
91
+ if(this[this.unit+"s"]==null||this.operator!=null){if(!this.value){this.value=1;}
92
+ if(this.unit=="week"){this.unit="day";this.value=this.value*7;}
93
+ this[this.unit+"s"]=this.value*orient;}
94
+ return today.add(this);}else{if(this.meridian&&this.hour){this.hour=(this.hour<13&&this.meridian=="p")?this.hour+12:this.hour;}
95
+ if(this.weekday&&!this.day){this.day=(today.addDays((Date.getDayNumberFromName(this.weekday)-today.getDay()))).getDate();}
96
+ if(this.month&&!this.day){this.day=1;}
97
+ return today.set(this);}}};var _=Date.Parsing.Operators,g=Date.Grammar,t=Date.Translator,_fn;g.datePartDelimiter=_.rtoken(/^([\s\-\.\,\/\x27]+)/);g.timePartDelimiter=_.stoken(":");g.whiteSpace=_.rtoken(/^\s*/);g.generalDelimiter=_.rtoken(/^(([\s\,]|at|on)+)/);var _C={};g.ctoken=function(keys){var fn=_C[keys];if(!fn){var c=Date.CultureInfo.regexPatterns;var kx=keys.split(/\s+/),px=[];for(var i=0;i<kx.length;i++){px.push(_.replace(_.rtoken(c[kx[i]]),kx[i]));}
98
+ fn=_C[keys]=_.any.apply(null,px);}
99
+ return fn;};g.ctoken2=function(key){return _.rtoken(Date.CultureInfo.regexPatterns[key]);};g.h=_.cache(_.process(_.rtoken(/^(0[0-9]|1[0-2]|[1-9])/),t.hour));g.hh=_.cache(_.process(_.rtoken(/^(0[0-9]|1[0-2])/),t.hour));g.H=_.cache(_.process(_.rtoken(/^([0-1][0-9]|2[0-3]|[0-9])/),t.hour));g.HH=_.cache(_.process(_.rtoken(/^([0-1][0-9]|2[0-3])/),t.hour));g.m=_.cache(_.process(_.rtoken(/^([0-5][0-9]|[0-9])/),t.minute));g.mm=_.cache(_.process(_.rtoken(/^[0-5][0-9]/),t.minute));g.s=_.cache(_.process(_.rtoken(/^([0-5][0-9]|[0-9])/),t.second));g.ss=_.cache(_.process(_.rtoken(/^[0-5][0-9]/),t.second));g.hms=_.cache(_.sequence([g.H,g.mm,g.ss],g.timePartDelimiter));g.t=_.cache(_.process(g.ctoken2("shortMeridian"),t.meridian));g.tt=_.cache(_.process(g.ctoken2("longMeridian"),t.meridian));g.z=_.cache(_.process(_.rtoken(/^(\+|\-)?\s*\d\d\d\d?/),t.timezone));g.zz=_.cache(_.process(_.rtoken(/^(\+|\-)\s*\d\d\d\d/),t.timezone));g.zzz=_.cache(_.process(g.ctoken2("timezone"),t.timezone));g.timeSuffix=_.each(_.ignore(g.whiteSpace),_.set([g.tt,g.zzz]));g.time=_.each(_.optional(_.ignore(_.stoken("T"))),g.hms,g.timeSuffix);g.d=_.cache(_.process(_.each(_.rtoken(/^([0-2]\d|3[0-1]|\d)/),_.optional(g.ctoken2("ordinalSuffix"))),t.day));g.dd=_.cache(_.process(_.each(_.rtoken(/^([0-2]\d|3[0-1])/),_.optional(g.ctoken2("ordinalSuffix"))),t.day));g.ddd=g.dddd=_.cache(_.process(g.ctoken("sun mon tue wed thu fri sat"),function(s){return function(){this.weekday=s;};}));g.M=_.cache(_.process(_.rtoken(/^(1[0-2]|0\d|\d)/),t.month));g.MM=_.cache(_.process(_.rtoken(/^(1[0-2]|0\d)/),t.month));g.MMM=g.MMMM=_.cache(_.process(g.ctoken("jan feb mar apr may jun jul aug sep oct nov dec"),t.month));g.y=_.cache(_.process(_.rtoken(/^(\d\d?)/),t.year));g.yy=_.cache(_.process(_.rtoken(/^(\d\d)/),t.year));g.yyy=_.cache(_.process(_.rtoken(/^(\d\d?\d?\d?)/),t.year));g.yyyy=_.cache(_.process(_.rtoken(/^(\d\d\d\d)/),t.year));_fn=function(){return _.each(_.any.apply(null,arguments),_.not(g.ctoken2("timeContext")));};g.day=_fn(g.d,g.dd);g.month=_fn(g.M,g.MMM);g.year=_fn(g.yyyy,g.yy);g.orientation=_.process(g.ctoken("past future"),function(s){return function(){this.orient=s;};});g.operator=_.process(g.ctoken("add subtract"),function(s){return function(){this.operator=s;};});g.rday=_.process(g.ctoken("yesterday tomorrow today now"),t.rday);g.unit=_.process(g.ctoken("minute hour day week month year"),function(s){return function(){this.unit=s;};});g.value=_.process(_.rtoken(/^\d\d?(st|nd|rd|th)?/),function(s){return function(){this.value=s.replace(/\D/g,"");};});g.expression=_.set([g.rday,g.operator,g.value,g.unit,g.orientation,g.ddd,g.MMM]);_fn=function(){return _.set(arguments,g.datePartDelimiter);};g.mdy=_fn(g.ddd,g.month,g.day,g.year);g.ymd=_fn(g.ddd,g.year,g.month,g.day);g.dmy=_fn(g.ddd,g.day,g.month,g.year);g.date=function(s){return((g[Date.CultureInfo.dateElementOrder]||g.mdy).call(this,s));};g.format=_.process(_.many(_.any(_.process(_.rtoken(/^(dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|zz?z?)/),function(fmt){if(g[fmt]){return g[fmt];}else{throw Date.Parsing.Exception(fmt);}}),_.process(_.rtoken(/^[^dMyhHmstz]+/),function(s){return _.ignore(_.stoken(s));}))),function(rules){return _.process(_.each.apply(null,rules),t.finishExact);});var _F={};var _get=function(f){return _F[f]=(_F[f]||g.format(f)[0]);};g.formats=function(fx){if(fx instanceof Array){var rx=[];for(var i=0;i<fx.length;i++){rx.push(_get(fx[i]));}
100
+ return _.any.apply(null,rx);}else{return _get(fx);}};g._formats=g.formats(["yyyy-MM-ddTHH:mm:ss","ddd, MMM dd, yyyy H:mm:ss tt","ddd MMM d yyyy HH:mm:ss zzz","d"]);g._start=_.process(_.set([g.date,g.time,g.expression],g.generalDelimiter,g.whiteSpace),t.finish);g.start=function(s){try{var r=g._formats.call({},s);if(r[1].length===0){return r;}}catch(e){}
101
+ return g._start.call({},s);};}());Date._parse=Date.parse;Date.parse=function(s){var r=null;if(!s){return null;}
102
+ try{r=Date.Grammar.start.call({},s);}catch(e){return null;}
103
+ return((r[1].length===0)?r[0]:null);};Date.getParseFunction=function(fx){var fn=Date.Grammar.formats(fx);return function(s){var r=null;try{r=fn.call({},s);}catch(e){return null;}
104
+ return((r[1].length===0)?r[0]:null);};};Date.parseExact=function(s,fx){return Date.getParseFunction(fx)(s);};
lib/humanmsg.css ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ HUMANIZED MESSAGES 1.0
3
+ idea - http://www.humanized.com/weblog/2006/09/11/monolog_boxes_and_transparent_messages
4
+ home - http://humanmsg.googlecode.com
5
+ */
6
+
7
+ .humanMsg {
8
+ font: normal 2em Helvetica, Arial, Sans-Serif;
9
+ letter-spacing: -1px;
10
+ position: fixed;
11
+ top: 130px;
12
+ left: 25%;
13
+ width: 50%;
14
+ color: white;
15
+ background-color: red;
16
+ text-align: center;
17
+ display: none;
18
+ opacity: 0;
19
+ z-index: 100000;
20
+ }
21
+
22
+ .humanMsg .round {
23
+ border-left: solid 2px white;
24
+ border-right: solid 2px white;
25
+ font-size: 1px; height: 2px;
26
+ }
27
+
28
+ .humanMsg p {
29
+ padding: .3em;
30
+ display: inline;
31
+ font-size: 1.5em;
32
+ }
33
+
34
+ .humanMsg a {
35
+ display: none;
36
+ }
lib/humanmsg.js ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ HUMANIZED MESSAGES 1.0
3
+ idea - http://www.humanized.com/weblog/2006/09/11/monolog_boxes_and_transparent_messages
4
+ home - http://humanmsg.googlecode.com
5
+ */
6
+
7
+ var humanMsg = {
8
+ setup: function(appendTo, msgOpacity) {
9
+ humanMsg.msgID = 'humanMsg';
10
+ humanMsg.logID = 'humanMsgLog';
11
+
12
+ // appendTo is the element the msg is appended to
13
+ if (appendTo == undefined)
14
+ appendTo = 'body';
15
+
16
+ // Opacity of the message
17
+ humanMsg.msgOpacity = .8;
18
+
19
+ if (msgOpacity != undefined)
20
+ humanMsg.msgOpacity = parseFloat(msgOpacity);
21
+
22
+ // Inject the message structure
23
+ jQuery(appendTo).append('<div id="'+humanMsg.msgID+'" class="humanMsg"><div class="round"></div><p></p><div class="round"></div></div>');
24
+
25
+ jQuery('#'+humanMsg.logID+' p').click(
26
+ function() { jQuery(this).siblings('ul').slideToggle() }
27
+ )
28
+ },
29
+
30
+ displayMsg: function(msg) {
31
+ if (msg == '')
32
+ return;
33
+
34
+ clearTimeout(humanMsg.t2);
35
+
36
+ // Inject message
37
+ jQuery('#'+humanMsg.msgID+' p').html(msg)
38
+
39
+ // Show message
40
+ jQuery('#'+humanMsg.msgID+'').show().animate({ opacity: humanMsg.msgOpacity}, 200, function() {
41
+ jQuery('#'+humanMsg.logID)
42
+ .show().children('ul').prepend('<li>'+msg+'</li>') // Prepend message to log
43
+ .children('li:first').slideDown(200) // Slide it down
44
+
45
+ if ( jQuery('#'+humanMsg.logID+' ul').css('display') == 'none') {
46
+ jQuery('#'+humanMsg.logID+' p').animate({ bottom: 40 }, 200, 'linear', function() {
47
+ jQuery(this).animate({ bottom: 0 }, 300, 'easeOutBounce', function() { jQuery(this).css({ bottom: 0 }) })
48
+ })
49
+ }
50
+
51
+ })
52
+
53
+ // Watch for mouse & keyboard in .5s
54
+ humanMsg.t1 = setTimeout("humanMsg.bindEvents()", 700)
55
+ // Remove message after 5s
56
+ humanMsg.t2 = setTimeout("humanMsg.removeMsg()", 5000)
57
+ },
58
+
59
+ bindEvents: function() {
60
+ // Remove message if mouse is moved or key is pressed
61
+ jQuery(window)
62
+ .mousemove(humanMsg.removeMsg)
63
+ .click(humanMsg.removeMsg)
64
+ .keypress(humanMsg.removeMsg)
65
+ },
66
+
67
+ removeMsg: function() {
68
+ // Unbind mouse & keyboard
69
+ jQuery(window)
70
+ .unbind('mousemove', humanMsg.removeMsg)
71
+ .unbind('click', humanMsg.removeMsg)
72
+ .unbind('keypress', humanMsg.removeMsg)
73
+
74
+ // If message is fully transparent, fade it out
75
+ if (jQuery('#'+humanMsg.msgID).css('opacity') == humanMsg.msgOpacity)
76
+ jQuery('#'+humanMsg.msgID).animate({ opacity: 0 }, 500, function() { jQuery(this).hide() })
77
+ }
78
+ };
79
+
80
+ jQuery(document).ready(function(){
81
+ humanMsg.setup();
82
+ })
lib/jquery.bgiframe.js ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Copyright (c) 2006 Brandon Aaron (http://brandonaaron.net)
2
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
3
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
4
+ *
5
+ * jQueryLastChangedDate: 2007-06-20 03:23:36 +0200 (Mi, 20 Jun 2007) jQuery
6
+ * jQueryRev: 2110 jQuery
7
+ *
8
+ * Version 2.1
9
+ */
10
+
11
+ (function(jQuery){
12
+
13
+ /**
14
+ * The bgiframe is chainable and applies the iframe hack to get
15
+ * around zIndex issues in IE6. It will only apply itself in IE
16
+ * and adds a class to the iframe called 'bgiframe'. The iframe
17
+ * is appeneded as the first child of the matched element(s)
18
+ * with a tabIndex and zIndex of -1.
19
+ *
20
+ * By default the plugin will take borders, sized with pixel units,
21
+ * into account. If a different unit is used for the border's width,
22
+ * then you will need to use the top and left settings as explained below.
23
+ *
24
+ * NOTICE: This plugin has been reported to cause perfromance problems
25
+ * when used on elements that change properties (like width, height and
26
+ * opacity) a lot in IE6. Most of these problems have been caused by
27
+ * the expressions used to calculate the elements width, height and
28
+ * borders. Some have reported it is due to the opacity filter. All
29
+ * these settings can be changed if needed as explained below.
30
+ *
31
+ * @example jQuery('div').bgiframe();
32
+ * @before <div><p>Paragraph</p></div>
33
+ * @result <div><iframe class="bgiframe".../><p>Paragraph</p></div>
34
+ *
35
+ * @param Map settings Optional settings to configure the iframe.
36
+ * @option String|Number top The iframe must be offset to the top
37
+ * by the width of the top border. This should be a negative
38
+ * number representing the border-top-width. If a number is
39
+ * is used here, pixels will be assumed. Otherwise, be sure
40
+ * to specify a unit. An expression could also be used.
41
+ * By default the value is "auto" which will use an expression
42
+ * to get the border-top-width if it is in pixels.
43
+ * @option String|Number left The iframe must be offset to the left
44
+ * by the width of the left border. This should be a negative
45
+ * number representing the border-left-width. If a number is
46
+ * is used here, pixels will be assumed. Otherwise, be sure
47
+ * to specify a unit. An expression could also be used.
48
+ * By default the value is "auto" which will use an expression
49
+ * to get the border-left-width if it is in pixels.
50
+ * @option String|Number width This is the width of the iframe. If
51
+ * a number is used here, pixels will be assume. Otherwise, be sure
52
+ * to specify a unit. An experssion could also be used.
53
+ * By default the value is "auto" which will use an experssion
54
+ * to get the offsetWidth.
55
+ * @option String|Number height This is the height of the iframe. If
56
+ * a number is used here, pixels will be assume. Otherwise, be sure
57
+ * to specify a unit. An experssion could also be used.
58
+ * By default the value is "auto" which will use an experssion
59
+ * to get the offsetHeight.
60
+ * @option Boolean opacity This is a boolean representing whether or not
61
+ * to use opacity. If set to true, the opacity of 0 is applied. If
62
+ * set to false, the opacity filter is not applied. Default: true.
63
+ * @option String src This setting is provided so that one could change
64
+ * the src of the iframe to whatever they need.
65
+ * Default: "javascript:false;"
66
+ *
67
+ * @name bgiframe
68
+ * @type jQuery
69
+ * @cat Plugins/bgiframe
70
+ * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
71
+ */
72
+ jQuery.fn.bgIframe = jQuery.fn.bgiframe = function(s) {
73
+ // This is only for IE6
74
+ if ( jQuery.browser.msie && parseInt(jQuery.browser.version) <= 6 ) {
75
+ s = jQuery.extend({
76
+ top : 'auto', // auto == .currentStyle.borderTopWidth
77
+ left : 'auto', // auto == .currentStyle.borderLeftWidth
78
+ width : 'auto', // auto == offsetWidth
79
+ height : 'auto', // auto == offsetHeight
80
+ opacity : true,
81
+ src : 'javascript:false;'
82
+ }, s || {});
83
+ var prop = function(n){return n&&n.constructor==Number?n+'px':n;},
84
+ html = '<iframe class="bgiframe"frameborder="0"tabindex="-1"src="'+s.src+'"'+
85
+ 'style="display:block;position:absolute;z-index:-1;'+
86
+ (s.opacity !== false?'filter:Alpha(Opacity=\'0\');':'')+
87
+ 'top:'+(s.top=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+\'px\')':prop(s.top))+';'+
88
+ 'left:'+(s.left=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+\'px\')':prop(s.left))+';'+
89
+ 'width:'+(s.width=='auto'?'expression(this.parentNode.offsetWidth+\'px\')':prop(s.width))+';'+
90
+ 'height:'+(s.height=='auto'?'expression(this.parentNode.offsetHeight+\'px\')':prop(s.height))+';'+
91
+ '"/>';
92
+ return this.each(function() {
93
+ if ( jQuery('> iframe.bgiframe', this).length == 0 )
94
+ this.insertBefore( document.createElement(html), this.firstChild );
95
+ });
96
+ }
97
+ return this;
98
+ };
99
+
100
+ // Add browser.version if it doesn't exist
101
+ if (!jQuery.browser.version)
102
+ jQuery.browser.version = navigator.userAgent.toLowerCase().match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)[1];
103
+
104
+ })(jQuery);
lib/jquery.delegate.js ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * jQuery delegate plug-in v1.0
3
+ *
4
+ * Copyright (c) 2007 Jörn Zaefferer
5
+ *
6
+ * jQueryId: jquery.delegate.js 4786 2008-02-19 20:02:34Z joern.zaefferer jQuery
7
+ *
8
+ * Dual licensed under the MIT and GPL licenses:
9
+ * http://www.opensource.org/licenses/mit-license.php
10
+ * http://www.gnu.org/licenses/gpl.html
11
+ */
12
+
13
+ // provides cross-browser focusin and focusout events
14
+ // IE has native support, in other browsers, use event caputuring (neither bubbles)
15
+
16
+ // provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
17
+ // handler is only called when jQuery(event.target).is(delegate), in the scope of the jQuery-object for event.target
18
+
19
+ // provides triggerEvent(type: String, target: Element) to trigger delegated events
20
+ ;(function(jQuery) {
21
+ jQuery.each({
22
+ focus: 'focusin',
23
+ blur: 'focusout'
24
+ }, function( original, fix ){
25
+ jQuery.event.special[fix] = {
26
+ setup:function() {
27
+ if ( jQuery.browser.msie ) return false;
28
+ this.addEventListener( original, jQuery.event.special[fix].handler, true );
29
+ },
30
+ teardown:function() {
31
+ if ( jQuery.browser.msie ) return false;
32
+ this.removeEventListener( original,
33
+ jQuery.event.special[fix].handler, true );
34
+ },
35
+ handler: function(e) {
36
+ arguments[0] = jQuery.event.fix(e);
37
+ arguments[0].type = fix;
38
+ return jQuery.event.handle.apply(this, arguments);
39
+ }
40
+ };
41
+ });
42
+
43
+ jQuery.extend(jQuery.fn, {
44
+ delegate: function(type, delegate, handler) {
45
+ return this.bind(type, function(event) {
46
+ var target = jQuery(event.target);
47
+ if (target.is(delegate)) {
48
+ return handler.apply(target, arguments);
49
+ }
50
+ });
51
+ },
52
+ triggerEvent: function(type, target) {
53
+ return this.triggerHandler(type, [jQuery.event.fix({ type: type, target: target })]);
54
+ }
55
+ })
56
+ })(jQuery);
lib/jquery.tools.min.js ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * jquery.tools 1.1.2 - The missing UI library for the Web
3
+ *
4
+ * [tools.scrollable-1.1.2, tools.scrollable.autoscroll-1.0.1, tools.scrollable.navigator-1.0.2, tools.scrollable.mousewheel-1.0.1]
5
+ *
6
+ * Copyright (c) 2009 Tero Piirainen
7
+ * http://flowplayer.org/tools/
8
+ *
9
+ * Dual licensed under MIT and GPL 2+ licenses
10
+ * http://www.opensource.org/licenses
11
+ *
12
+ * -----
13
+ *
14
+ * jquery.event.wheel.js - rev 1
15
+ * Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)
16
+ * Liscensed under the MIT License (MIT-LICENSE.txt)
17
+ * http://www.opensource.org/licenses/mit-license.php
18
+ * Created: 2008-07-01 | Updated: 2008-07-14
19
+ *
20
+ * -----
21
+ *
22
+ * File generated: Wed Oct 07 19:54:35 GMT+00:00 2009
23
+ */
24
+ (function(b){b.tools=b.tools||{};b.tools.scrollable={version:"1.1.2",conf:{size:5,vertical:false,speed:400,keyboard:true,keyboardSteps:null,disabledClass:"disabled",hoverClass:null,clickable:true,activeClass:"active",easing:"swing",loop:false,items:".items",item:null,prev:".prev",next:".next",prevPage:".prevPage",nextPage:".nextPage",api:false}};var c;function a(o,m){var r=this,p=b(this),d=!m.vertical,e=o.children(),k=0,i;if(!c){c=r}b.each(m,function(s,t){if(b.isFunction(t)){p.bind(s,t)}});if(e.length>1){e=b(m.items,o)}function l(t){var s=b(t);return m.globalNav?s:o.parent().find(t)}o.data("finder",l);var f=l(m.prev),h=l(m.next),g=l(m.prevPage),n=l(m.nextPage);b.extend(r,{getIndex:function(){return k},getClickIndex:function(){var s=r.getItems();return s.index(s.filter("."+m.activeClass))},getConf:function(){return m},getSize:function(){return r.getItems().size()},getPageAmount:function(){return Math.ceil(this.getSize()/m.size)},getPageIndex:function(){return Math.ceil(k/m.size)},getNaviButtons:function(){return f.add(h).add(g).add(n)},getRoot:function(){return o},getItemWrap:function(){return e},getItems:function(){return e.children(m.item)},getVisibleItems:function(){return r.getItems().slice(k,k+m.size)},seekTo:function(s,w,t){if(s<0){s=0}if(k===s){return r}if(b.isFunction(w)){t=w}if(s>r.getSize()-m.size){return m.loop?r.begin():this.end()}var u=r.getItems().eq(s);if(!u.length){return r}var v=b.Event("onBeforeSeek");p.trigger(v,[s]);if(v.isDefaultPrevented()){return r}if(w===undefined||b.isFunction(w)){w=m.speed}function x(){if(t){t.call(r,s)}p.trigger("onSeek",[s])}if(d){e.animate({left:-u.position().left},w,m.easing,x)}else{e.animate({top:-u.position().top},w,m.easing,x)}c=r;k=s;v=b.Event("onStart");p.trigger(v,[s]);if(v.isDefaultPrevented()){return r}f.add(g).toggleClass(m.disabledClass,s===0);h.add(n).toggleClass(m.disabledClass,s>=r.getSize()-m.size);return r},move:function(u,t,s){i=u>0;return this.seekTo(k+u,t,s)},next:function(t,s){return this.move(1,t,s)},prev:function(t,s){return this.move(-1,t,s)},movePage:function(w,v,u){i=w>0;var s=m.size*w;var t=k%m.size;if(t>0){s+=(w>0?-t:m.size-t)}return this.move(s,v,u)},prevPage:function(t,s){return this.movePage(-1,t,s)},nextPage:function(t,s){return this.movePage(1,t,s)},setPage:function(t,u,s){return this.seekTo(t*m.size,u,s)},begin:function(t,s){i=false;return this.seekTo(0,t,s)},end:function(t,s){i=true;var u=this.getSize()-m.size;return u>0?this.seekTo(u,t,s):r},reload:function(){p.trigger("onReload");return r},focus:function(){c=r;return r},click:function(u){var v=r.getItems().eq(u),s=m.activeClass,t=m.size;if(u<0||u>=r.getSize()){return r}if(t==1){if(m.loop){return r.next()}if(u===0||u==r.getSize()-1){i=(i===undefined)?true:!i}return i===false?r.prev():r.next()}if(t==2){if(u==k){u--}r.getItems().removeClass(s);v.addClass(s);return r.seekTo(u,time,fn)}if(!v.hasClass(s)){r.getItems().removeClass(s);v.addClass(s);var x=Math.floor(t/2);var w=u-x;if(w>r.getSize()-t){w=r.getSize()-t}if(w!==u){return r.seekTo(w)}}return r},bind:function(s,t){p.bind(s,t);return r},unbind:function(s){p.unbind(s);return r}});b.each("onBeforeSeek,onStart,onSeek,onReload".split(","),function(s,t){r[t]=function(u){return r.bind(t,u)}});f.addClass(m.disabledClass).click(function(){r.prev()});h.click(function(){r.next()});n.click(function(){r.nextPage()});if(r.getSize()<m.size){h.add(n).addClass(m.disabledClass)}g.addClass(m.disabledClass).click(function(){r.prevPage()});var j=m.hoverClass,q="keydown."+Math.random().toString().substring(10);r.onReload(function(){if(j){r.getItems().hover(function(){b(this).addClass(j)},function(){b(this).removeClass(j)})}if(m.clickable){r.getItems().each(function(s){b(this).unbind("click.scrollable").bind("click.scrollable",function(t){if(b(t.target).is("a")){return}return r.click(s)})})}if(m.keyboard){b(document).unbind(q).bind(q,function(t){if(t.altKey||t.ctrlKey){return}if(m.keyboard!="static"&&c!=r){return}var u=m.keyboardSteps;if(d&&(t.keyCode==37||t.keyCode==39)){r.move(t.keyCode==37?-u:u);return t.preventDefault()}if(!d&&(t.keyCode==38||t.keyCode==40)){r.move(t.keyCode==38?-u:u);return t.preventDefault()}return true})}else{b(document).unbind(q)}});r.reload()}b.fn.scrollable=function(d){var e=this.eq(typeof d=="number"?d:0).data("scrollable");if(e){return e}var f=b.extend({},b.tools.scrollable.conf);d=b.extend(f,d);d.keyboardSteps=d.keyboardSteps||d.size;this.each(function(){e=new a(b(this),d);b(this).data("scrollable",e)});return d.api?e:this}})(jQuery);
25
+ (function(b){var a=b.tools.scrollable;a.plugins=a.plugins||{};a.plugins.autoscroll={version:"1.0.1",conf:{autoplay:true,interval:3000,autopause:true,steps:1,api:false}};b.fn.autoscroll=function(d){if(typeof d=="number"){d={interval:d}}var e=b.extend({},a.plugins.autoscroll.conf),c;b.extend(e,d);this.each(function(){var g=b(this).scrollable();if(g){c=g}var i,f,h=true;g.play=function(){if(i){return}h=false;i=setInterval(function(){g.move(e.steps)},e.interval);g.move(e.steps)};g.pause=function(){i=clearInterval(i)};g.stop=function(){g.pause();h=true};if(e.autopause){g.getRoot().add(g.getNaviButtons()).hover(function(){g.pause();clearInterval(f)},function(){if(!h){f=setTimeout(g.play,e.interval)}})}if(e.autoplay){setTimeout(g.play,e.interval)}});return e.api?c:this}})(jQuery);
26
+ (function(b){var a=b.tools.scrollable;a.plugins=a.plugins||{};a.plugins.navigator={version:"1.0.2",conf:{navi:".navi",naviItem:null,activeClass:"active",indexed:false,api:false,idPrefix:null}};b.fn.navigator=function(d){var e=b.extend({},a.plugins.navigator.conf),c;if(typeof d=="string"){d={navi:d}}d=b.extend(e,d);this.each(function(){var i=b(this).scrollable(),f=i.getRoot(),l=f.data("finder").call(null,d.navi),g=null,k=i.getNaviButtons();if(i){c=i}i.getNaviButtons=function(){return k.add(l)};function j(){if(!l.children().length||l.data("navi")==i){l.empty();l.data("navi",i);for(var m=0;m<i.getPageAmount();m++){l.append(b("<"+(d.naviItem||"a")+"/>"))}g=l.children().each(function(n){var o=b(this);o.click(function(p){i.setPage(n);return p.preventDefault()});if(d.indexed){o.text(n)}if(d.idPrefix){o.attr("id",d.idPrefix+n)}})}else{g=d.naviItem?l.find(d.naviItem):l.children();g.each(function(n){var o=b(this);o.click(function(p){i.setPage(n);return p.preventDefault()})})}g.eq(0).addClass(d.activeClass)}i.onStart(function(o,n){var m=d.activeClass;g.removeClass(m).eq(i.getPageIndex()).addClass(m)});i.onReload(function(){j()});j();var h=g.filter("[href="+location.hash+"]");if(h.length){i.move(g.index(h))}});return d.api?c:this}})(jQuery);
27
+ (function(b){b.fn.wheel=function(e){return this[e?"bind":"trigger"]("wheel",e)};b.event.special.wheel={setup:function(){b.event.add(this,d,c,{})},teardown:function(){b.event.remove(this,d,c)}};var d=!b.browser.mozilla?"mousewheel":"DOMMouseScroll"+(b.browser.version<"1.9"?" mousemove":"");function c(e){switch(e.type){case"mousemove":return b.extend(e.data,{clientX:e.clientX,clientY:e.clientY,pageX:e.pageX,pageY:e.pageY});case"DOMMouseScroll":b.extend(e,e.data);e.delta=-e.detail/3;break;case"mousewheel":e.delta=e.wheelDelta/120;break}e.type="wheel";return b.event.handle.call(this,e,e.delta)}var a=b.tools.scrollable;a.plugins=a.plugins||{};a.plugins.mousewheel={version:"1.0.1",conf:{api:false,speed:50}};b.fn.mousewheel=function(f){var g=b.extend({},a.plugins.mousewheel.conf),e;if(typeof f=="number"){f={speed:f}}f=b.extend(g,f);this.each(function(){var h=b(this).scrollable();if(h){e=h}h.getRoot().wheel(function(i,j){h.move(j<0?1:-1,f.speed||50);return false})});return f.api?e:this}})(jQuery);
lib/jquery.tooltip.css ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ #tooltip {
2
+ position: absolute;
3
+ z-index: 3000;
4
+ border: 1px solid #111;
5
+ background-color: #eee;
6
+ padding: 5px;
7
+ opacity: 0.85;
8
+ }
9
+ #tooltip h3, #tooltip div { margin: 0; }
lib/jquery.tooltip.js ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * jQuery Tooltip plugin 1.3
3
+ *
4
+ * http://bassistance.de/jquery-plugins/jquery-plugin-tooltip/
5
+ * http://docs.jquery.com/Plugins/Tooltip
6
+ *
7
+ * Copyright (c) 2006 - 2008 Jörn Zaefferer
8
+ *
9
+ * jQueryId: jquery.tooltip.js 5741 2008-06-21 15:22:16Z joern.zaefferer jQuery
10
+ *
11
+ * Dual licensed under the MIT and GPL licenses:
12
+ * http://www.opensource.org/licenses/mit-license.php
13
+ * http://www.gnu.org/licenses/gpl.html
14
+ */
15
+
16
+ ;(function(jQuery) {
17
+
18
+ // the tooltip element
19
+ var helper = {},
20
+ // the current tooltipped element
21
+ current,
22
+ // the title of the current element, used for restoring
23
+ title,
24
+ // timeout id for delayed tooltips
25
+ tID,
26
+ //timout for hiding delayed tooltips
27
+ htID,
28
+ // IE 5.5 or 6
29
+ IE = jQuery.browser.msie && /MSIE\s(5\.5|6\.)/.test(navigator.userAgent),
30
+ // flag for mouse tracking
31
+ track = false;
32
+
33
+ jQuery.tooltip = {
34
+ blocked: false,
35
+ defaults: {
36
+ delay: 200,
37
+ fade: false,
38
+ showURL: true,
39
+ extraClass: "",
40
+ top: 15,
41
+ left: 15,
42
+ id: "tooltip"
43
+ },
44
+ block: function() {
45
+ jQuery.tooltip.blocked = !jQuery.tooltip.blocked;
46
+ }
47
+ };
48
+
49
+ jQuery.fn.extend({
50
+ tooltip: function(settings) {
51
+ settings = jQuery.extend({}, jQuery.tooltip.defaults, settings);
52
+ createHelper(settings);
53
+ return this.each(function() {
54
+ jQuery.data(this, "tooltip", settings);
55
+ this.tOpacity = helper.parent.css("opacity");
56
+ // copy tooltip into its own expando and remove the title
57
+ this.tooltipText = this.title;
58
+ jQuery(this).removeAttr("title");
59
+ // also remove alt attribute to prevent default tooltip in IE
60
+ this.alt = "";
61
+ })
62
+ .mouseover(save)
63
+ .mouseout(hide)
64
+ .click(hide);
65
+ },
66
+ fixPNG: IE ? function() {
67
+ return this.each(function () {
68
+ var image = jQuery(this).css('backgroundImage');
69
+ if (image.match(/^url\(["']?(.*\.png)["']?\)jQuery/i)) {
70
+ image = RegExp.jQuery1;
71
+ jQuery(this).css({
72
+ 'backgroundImage': 'none',
73
+ 'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')"
74
+ }).each(function () {
75
+ var position = jQuery(this).css('position');
76
+ if (position != 'absolute' && position != 'relative')
77
+ jQuery(this).css('position', 'relative');
78
+ });
79
+ }
80
+ });
81
+ } : function() { return this; },
82
+ unfixPNG: IE ? function() {
83
+ return this.each(function () {
84
+ jQuery(this).css({'filter': '', backgroundImage: ''});
85
+ });
86
+ } : function() { return this; },
87
+ hideWhenEmpty: function() {
88
+ return this.each(function() {
89
+ jQuery(this)[ jQuery(this).html() ? "show" : "hide" ]();
90
+ });
91
+ },
92
+ url: function() {
93
+ return this.attr('href') || this.attr('src');
94
+ }
95
+ });
96
+
97
+ function createHelper(settings) {
98
+ // there can be only one tooltip helper
99
+ if( helper.parent )
100
+ return;
101
+ // create the helper, h3 for title, div for url
102
+ helper.parent = jQuery('<div id="' + settings.id + '"><h3></h3><div class="body"></div><div class="url"></div></div>')
103
+ // add to document
104
+ .appendTo(document.body)
105
+ // hide it at first
106
+ .hide();
107
+
108
+ // apply bgiframe if available
109
+ if ( jQuery.fn.bgiframe )
110
+ helper.parent.bgiframe();
111
+
112
+ // save references to title and url elements
113
+ helper.title = jQuery('h3', helper.parent);
114
+ helper.body = jQuery('div.body', helper.parent);
115
+ helper.url = jQuery('div.url', helper.parent);
116
+ jQuery("#" + settings.id).click(function() {
117
+ hide();
118
+ });
119
+ }
120
+
121
+ function tooltipsettings(element) {
122
+ if (element) {
123
+ return jQuery.data(element, "tooltip");
124
+ } else {
125
+ jQuery("#tooltip").hide();
126
+ }
127
+ }
128
+
129
+ // main event handler to start showing tooltips
130
+ function handle(event) {
131
+ // show helper, either with timeout or on instant
132
+ if( tooltipsettings(this).delay )
133
+ tID = setTimeout(show, tooltipsettings(this).delay);
134
+ else
135
+ show();
136
+
137
+ // if selected, update the helper position when the mouse moves
138
+ track = !!tooltipsettings(this).track;
139
+ jQuery(document.body).bind('mousemove', update);
140
+
141
+ // update at least once
142
+ update(event);
143
+ }
144
+
145
+ // save elements title before the tooltip is displayed
146
+ function save() {
147
+ // if this is the current source, or it has no title (occurs with click event), stop
148
+ if ( jQuery.tooltip.blocked || this == current || (!this.tooltipText && !tooltipsettings(this).bodyHandler) )
149
+ return;
150
+
151
+ // save current
152
+ current = this;
153
+ title = this.tooltipText;
154
+
155
+ if ( tooltipsettings(this).bodyHandler ) {
156
+ helper.title.hide();
157
+ var bodyContent = tooltipsettings(this).bodyHandler.call(this);
158
+ if (bodyContent.nodeType || bodyContent.jquery) {
159
+ helper.body.empty().append(bodyContent)
160
+ } else {
161
+ helper.body.html( bodyContent );
162
+ }
163
+ helper.body.show();
164
+ } else if ( tooltipsettings(this).showBody ) {
165
+ var parts = title.split(tooltipsettings(this).showBody);
166
+ helper.title.html(parts.shift()).show();
167
+ helper.body.empty();
168
+ for(var i = 0, part; (part = parts[i]); i++) {
169
+ if(i > 0)
170
+ helper.body.append("<br/>");
171
+ helper.body.append(part);
172
+ }
173
+ helper.body.hideWhenEmpty();
174
+ } else {
175
+ helper.title.html(title).show();
176
+ helper.body.hide();
177
+ }
178
+
179
+ // if element has href or src, add and show it, otherwise hide it
180
+ if( tooltipsettings(this).showURL && jQuery(this).url() )
181
+ helper.url.html( jQuery(this).url().replace('http://', '') ).show();
182
+ else
183
+ helper.url.hide();
184
+
185
+ // add an optional class for this tip
186
+ helper.parent.addClass(tooltipsettings(this).extraClass);
187
+
188
+ // fix PNG background for IE
189
+ if (tooltipsettings(this).fixPNG )
190
+ helper.parent.fixPNG();
191
+
192
+ handle.apply(this, arguments);
193
+ }
194
+
195
+ // delete timeout and show helper
196
+ function show() {
197
+ if (!tooltipsettings(current)) {
198
+ return;
199
+ }
200
+ tID = null;
201
+ if ((!IE || !jQuery.fn.bgiframe) && tooltipsettings(current).fade) {
202
+ if (helper.parent.is(":animated"))
203
+ helper.parent.stop().show().fadeTo(tooltipsettings(current).fade, current.tOpacity);
204
+ else
205
+ helper.parent.is(':visible') ? helper.parent.fadeTo(tooltipsettings(current).fade, current.tOpacity) : helper.parent.fadeIn(tooltipsettings(current).fade);
206
+ } else {
207
+ helper.parent.show();
208
+ }
209
+ update();
210
+ }
211
+
212
+ /**
213
+ * callback for mousemove
214
+ * updates the helper position
215
+ * removes itself when no current element
216
+ */
217
+ function update(event) {
218
+ if(jQuery.tooltip.blocked)
219
+ return;
220
+ if (!tooltipsettings(current)) {
221
+ return;
222
+ }
223
+
224
+ if (event && event.target.tagName == "OPTION") {
225
+ return;
226
+ }
227
+
228
+ // stop updating when tracking is disabled and the tooltip is visible
229
+ if ( !track && helper.parent.is(":visible")) {
230
+ jQuery(document.body).unbind('mousemove', update)
231
+ }
232
+
233
+ // if no current element is available, remove this listener
234
+ if( current == null ) {
235
+ jQuery(document.body).unbind('mousemove', update);
236
+ return;
237
+ }
238
+
239
+ // remove position helper classes
240
+ helper.parent.removeClass("viewport-right").removeClass("viewport-bottom");
241
+
242
+ var left = helper.parent[0].offsetLeft;
243
+ var top = helper.parent[0].offsetTop;
244
+ if (event) {
245
+ // position the helper 15 pixel to bottom right, starting from mouse position
246
+ left = event.pageX + tooltipsettings(current).left;
247
+ top = event.pageY + tooltipsettings(current).top;
248
+ var right='auto';
249
+ if (tooltipsettings(current).positionLeft) {
250
+ right = jQuery(window).width() - left;
251
+ left = 'auto';
252
+ }
253
+ helper.parent.css({
254
+ left: left,
255
+ right: right,
256
+ top: top
257
+ });
258
+ }
259
+
260
+ var v = viewport(),
261
+ h = helper.parent[0];
262
+ // check horizontal position
263
+ if (v.x + v.cx < h.offsetLeft + h.offsetWidth) {
264
+ left -= h.offsetWidth + 20 + tooltipsettings(current).left + 75;
265
+ helper.parent.css({left: left + 'px'}).addClass("viewport-right");
266
+ }
267
+ // check vertical position
268
+ if (v.y + v.cy < h.offsetTop + h.offsetHeight) {
269
+ top -= h.offsetHeight + 20 + tooltipsettings(current).top;
270
+ helper.parent.css({top: top + 'px'}).addClass("viewport-bottom");
271
+ }
272
+ }
273
+
274
+ function viewport() {
275
+ return {
276
+ x: jQuery(window).scrollLeft(),
277
+ y: jQuery(window).scrollTop(),
278
+ cx: jQuery(window).width(),
279
+ cy: jQuery(window).height()
280
+ };
281
+ }
282
+
283
+ // hide helper and restore added classes and the title
284
+ function hide(event) {
285
+ if(jQuery.tooltip.blocked)
286
+ return;
287
+
288
+ if (!event) {
289
+ jQuery("#tooltip").hide();
290
+ return;
291
+ }
292
+
293
+ var offset = jQuery("#tooltip").offset();
294
+ /*
295
+ * We want to give the user a chance to get their mouse over the tooltip
296
+ * before we hide it.
297
+ */
298
+ if (event.pageX >= offset.left - 15 &&
299
+ event.pageY >= offset.top - 15 &&
300
+ event.pageX <= jQuery("#tooltip").width() + offset.left + 15 &&
301
+ event.pageY <= jQuery("#tooltip").height() + offset.top + 15) {
302
+ return;
303
+ }
304
+
305
+ // clear timeout if possible
306
+ if(tID)
307
+ clearTimeout(tID);
308
+ // no more current element
309
+ current = null;
310
+
311
+ var tsettings = tooltipsettings(this);
312
+ function complete() {
313
+ helper.parent.removeClass( tsettings.extraClass ).hide().css("opacity", "");
314
+ }
315
+ if ((!IE || !jQuery.fn.bgiframe) && tsettings.fade) {
316
+ if (helper.parent.is(':animated'))
317
+ helper.parent.stop().fadeTo(tsettings.fade, 0, complete);
318
+ else
319
+ helper.parent.stop().fadeOut(tsettings.fade, complete);
320
+ } else
321
+ complete();
322
+
323
+ if( tooltipsettings(this).fixPNG )
324
+ helper.parent.unfixPNG();
325
+
326
+ htID = null;
327
+ }
328
+
329
+ })(jQuery);
lib/json2.js ADDED
@@ -0,0 +1,480 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ http://www.JSON.org/json2.js
3
+ 2009-09-29
4
+
5
+ Public Domain.
6
+
7
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8
+
9
+ See http://www.JSON.org/js.html
10
+
11
+ This file creates a global JSON object containing two methods: stringify
12
+ and parse.
13
+
14
+ JSON.stringify(value, replacer, space)
15
+ value any JavaScript value, usually an object or array.
16
+
17
+ replacer an optional parameter that determines how object
18
+ values are stringified for objects. It can be a
19
+ function or an array of strings.
20
+
21
+ space an optional parameter that specifies the indentation
22
+ of nested structures. If it is omitted, the text will
23
+ be packed without extra whitespace. If it is a number,
24
+ it will specify the number of spaces to indent at each
25
+ level. If it is a string (such as '\t' or '&nbsp;'),
26
+ it contains the characters used to indent at each level.
27
+
28
+ This method produces a JSON text from a JavaScript value.
29
+
30
+ When an object value is found, if the object contains a toJSON
31
+ method, its toJSON method will be called and the result will be
32
+ stringified. A toJSON method does not serialize: it returns the
33
+ value represented by the name/value pair that should be serialized,
34
+ or undefined if nothing should be serialized. The toJSON method
35
+ will be passed the key associated with the value, and this will be
36
+ bound to the value
37
+
38
+ For example, this would serialize Dates as ISO strings.
39
+
40
+ Date.prototype.toJSON = function (key) {
41
+ function f(n) {
42
+ // Format integers to have at least two digits.
43
+ return n < 10 ? '0' + n : n;
44
+ }
45
+
46
+ return this.getUTCFullYear() + '-' +
47
+ f(this.getUTCMonth() + 1) + '-' +
48
+ f(this.getUTCDate()) + 'T' +
49
+ f(this.getUTCHours()) + ':' +
50
+ f(this.getUTCMinutes()) + ':' +
51
+ f(this.getUTCSeconds()) + 'Z';
52
+ };
53
+
54
+ You can provide an optional replacer method. It will be passed the
55
+ key and value of each member, with this bound to the containing
56
+ object. The value that is returned from your method will be
57
+ serialized. If your method returns undefined, then the member will
58
+ be excluded from the serialization.
59
+
60
+ If the replacer parameter is an array of strings, then it will be
61
+ used to select the members to be serialized. It filters the results
62
+ such that only members with keys listed in the replacer array are
63
+ stringified.
64
+
65
+ Values that do not have JSON representations, such as undefined or
66
+ functions, will not be serialized. Such values in objects will be
67
+ dropped; in arrays they will be replaced with null. You can use
68
+ a replacer function to replace those with JSON values.
69
+ JSON.stringify(undefined) returns undefined.
70
+
71
+ The optional space parameter produces a stringification of the
72
+ value that is filled with line breaks and indentation to make it
73
+ easier to read.
74
+
75
+ If the space parameter is a non-empty string, then that string will
76
+ be used for indentation. If the space parameter is a number, then
77
+ the indentation will be that many spaces.
78
+
79
+ Example:
80
+
81
+ text = JSON.stringify(['e', {pluribus: 'unum'}]);
82
+ // text is '["e",{"pluribus":"unum"}]'
83
+
84
+
85
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
86
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
87
+
88
+ text = JSON.stringify([new Date()], function (key, value) {
89
+ return this[key] instanceof Date ?
90
+ 'Date(' + this[key] + ')' : value;
91
+ });
92
+ // text is '["Date(---current time---)"]'
93
+
94
+
95
+ JSON.parse(text, reviver)
96
+ This method parses a JSON text to produce an object or array.
97
+ It can throw a SyntaxError exception.
98
+
99
+ The optional reviver parameter is a function that can filter and
100
+ transform the results. It receives each of the keys and values,
101
+ and its return value is used instead of the original value.
102
+ If it returns what it received, then the structure is not modified.
103
+ If it returns undefined then the member is deleted.
104
+
105
+ Example:
106
+
107
+ // Parse the text. Values that look like ISO date strings will
108
+ // be converted to Date objects.
109
+
110
+ myData = JSON.parse(text, function (key, value) {
111
+ var a;
112
+ if (typeof value === 'string') {
113
+ a =
114
+ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
115
+ if (a) {
116
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
117
+ +a[5], +a[6]));
118
+ }
119
+ }
120
+ return value;
121
+ });
122
+
123
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
124
+ var d;
125
+ if (typeof value === 'string' &&
126
+ value.slice(0, 5) === 'Date(' &&
127
+ value.slice(-1) === ')') {
128
+ d = new Date(value.slice(5, -1));
129
+ if (d) {
130
+ return d;
131
+ }
132
+ }
133
+ return value;
134
+ });
135
+
136
+
137
+ This is a reference implementation. You are free to copy, modify, or
138
+ redistribute.
139
+
140
+ This code should be minified before deployment.
141
+ See http://javascript.crockford.com/jsmin.html
142
+
143
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
144
+ NOT CONTROL.
145
+ */
146
+
147
+ /*jslint evil: true, strict: false */
148
+
149
+ /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
150
+ call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
151
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
152
+ lastIndex, length, parse, prototype, push, replace, slice, stringify,
153
+ test, toJSON, toString, valueOf
154
+ */
155
+
156
+
157
+ // Create a JSON object only if one does not already exist. We create the
158
+ // methods in a closure to avoid creating global variables.
159
+
160
+ if (!this.JSON) {
161
+ this.JSON = {};
162
+ }
163
+
164
+ (function () {
165
+
166
+ function f(n) {
167
+ // Format integers to have at least two digits.
168
+ return n < 10 ? '0' + n : n;
169
+ }
170
+
171
+ if (typeof Date.prototype.toJSON !== 'function') {
172
+
173
+ Date.prototype.toJSON = function (key) {
174
+
175
+ return isFinite(this.valueOf()) ?
176
+ this.getUTCFullYear() + '-' +
177
+ f(this.getUTCMonth() + 1) + '-' +
178
+ f(this.getUTCDate()) + 'T' +
179
+ f(this.getUTCHours()) + ':' +
180
+ f(this.getUTCMinutes()) + ':' +
181
+ f(this.getUTCSeconds()) + 'Z' : null;
182
+ };
183
+
184
+ String.prototype.toJSON =
185
+ Number.prototype.toJSON =
186
+ Boolean.prototype.toJSON = function (key) {
187
+ return this.valueOf();
188
+ };
189
+ }
190
+
191
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
192
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
193
+ gap,
194
+ indent,
195
+ meta = { // table of character substitutions
196
+ '\b': '\\b',
197
+ '\t': '\\t',
198
+ '\n': '\\n',
199
+ '\f': '\\f',
200
+ '\r': '\\r',
201
+ '"' : '\\"',
202
+ '\\': '\\\\'
203
+ },
204
+ rep;
205
+
206
+
207
+ function quote(string) {
208
+
209
+ // If the string contains no control characters, no quote characters, and no
210
+ // backslash characters, then we can safely slap some quotes around it.
211
+ // Otherwise we must also replace the offending characters with safe escape
212
+ // sequences.
213
+
214
+ escapable.lastIndex = 0;
215
+ return escapable.test(string) ?
216
+ '"' + string.replace(escapable, function (a) {
217
+ var c = meta[a];
218
+ return typeof c === 'string' ? c :
219
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
220
+ }) + '"' :
221
+ '"' + string + '"';
222
+ }
223
+
224
+
225
+ function str(key, holder) {
226
+
227
+ // Produce a string from holder[key].
228
+
229
+ var i, // The loop counter.
230
+ k, // The member key.
231
+ v, // The member value.
232
+ length,
233
+ mind = gap,
234
+ partial,
235
+ value = holder[key];
236
+
237
+ // If the value has a toJSON method, call it to obtain a replacement value.
238
+
239
+ if (value && typeof value === 'object' &&
240
+ typeof value.toJSON === 'function') {
241
+ value = value.toJSON(key);
242
+ }
243
+
244
+ // If we were called with a replacer function, then call the replacer to
245
+ // obtain a replacement value.
246
+
247
+ if (typeof rep === 'function') {
248
+ value = rep.call(holder, key, value);
249
+ }
250
+
251
+ // What happens next depends on the value's type.
252
+
253
+ switch (typeof value) {
254
+ case 'string':
255
+ return quote(value);
256
+
257
+ case 'number':
258
+
259
+ // JSON numbers must be finite. Encode non-finite numbers as null.
260
+
261
+ return isFinite(value) ? String(value) : 'null';
262
+
263
+ case 'boolean':
264
+ case 'null':
265
+
266
+ // If the value is a boolean or null, convert it to a string. Note:
267
+ // typeof null does not produce 'null'. The case is included here in
268
+ // the remote chance that this gets fixed someday.
269
+
270
+ return String(value);
271
+
272
+ // If the type is 'object', we might be dealing with an object or an array or
273
+ // null.
274
+
275
+ case 'object':
276
+
277
+ // Due to a specification blunder in ECMAScript, typeof null is 'object',
278
+ // so watch out for that case.
279
+
280
+ if (!value) {
281
+ return 'null';
282
+ }
283
+
284
+ // Make an array to hold the partial results of stringifying this object value.
285
+
286
+ gap += indent;
287
+ partial = [];
288
+
289
+ // Is the value an array?
290
+
291
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
292
+
293
+ // The value is an array. Stringify every element. Use null as a placeholder
294
+ // for non-JSON values.
295
+
296
+ length = value.length;
297
+ for (i = 0; i < length; i += 1) {
298
+ partial[i] = str(i, value) || 'null';
299
+ }
300
+
301
+ // Join all of the elements together, separated with commas, and wrap them in
302
+ // brackets.
303
+
304
+ v = partial.length === 0 ? '[]' :
305
+ gap ? '[\n' + gap +
306
+ partial.join(',\n' + gap) + '\n' +
307
+ mind + ']' :
308
+ '[' + partial.join(',') + ']';
309
+ gap = mind;
310
+ return v;
311
+ }
312
+
313
+ // If the replacer is an array, use it to select the members to be stringified.
314
+
315
+ if (rep && typeof rep === 'object') {
316
+ length = rep.length;
317
+ for (i = 0; i < length; i += 1) {
318
+ k = rep[i];
319
+ if (typeof k === 'string') {
320
+ v = str(k, value);
321
+ if (v) {
322
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
323
+ }
324
+ }
325
+ }
326
+ } else {
327
+
328
+ // Otherwise, iterate through all of the keys in the object.
329
+
330
+ for (k in value) {
331
+ if (Object.hasOwnProperty.call(value, k)) {
332
+ v = str(k, value);
333
+ if (v) {
334
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
335
+ }
336
+ }
337
+ }
338
+ }
339
+
340
+ // Join all of the member texts together, separated with commas,
341
+ // and wrap them in braces.
342
+
343
+ v = partial.length === 0 ? '{}' :
344
+ gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
345
+ mind + '}' : '{' + partial.join(',') + '}';
346
+ gap = mind;
347
+ return v;
348
+ }
349
+ }
350
+
351
+ // If the JSON object does not yet have a stringify method, give it one.
352
+
353
+ if (typeof JSON.stringify !== 'function') {
354
+ JSON.stringify = function (value, replacer, space) {
355
+
356
+ // The stringify method takes a value and an optional replacer, and an optional
357
+ // space parameter, and returns a JSON text. The replacer can be a function
358
+ // that can replace values, or an array of strings that will select the keys.
359
+ // A default replacer method can be provided. Use of the space parameter can
360
+ // produce text that is more easily readable.
361
+
362
+ var i;
363
+ gap = '';
364
+ indent = '';
365
+
366
+ // If the space parameter is a number, make an indent string containing that
367
+ // many spaces.
368
+
369
+ if (typeof space === 'number') {
370
+ for (i = 0; i < space; i += 1) {
371
+ indent += ' ';
372
+ }
373
+
374
+ // If the space parameter is a string, it will be used as the indent string.
375
+
376
+ } else if (typeof space === 'string') {
377
+ indent = space;
378
+ }
379
+
380
+ // If there is a replacer, it must be a function or an array.
381
+ // Otherwise, throw an error.
382
+
383
+ rep = replacer;
384
+ if (replacer && typeof replacer !== 'function' &&
385
+ (typeof replacer !== 'object' ||
386
+ typeof replacer.length !== 'number')) {
387
+ throw new Error('JSON.stringify');
388
+ }
389
+
390
+ // Make a fake root object containing our value under the key of ''.
391
+ // Return the result of stringifying the value.
392
+
393
+ return str('', {'': value});
394
+ };
395
+ }
396
+
397
+
398
+ // If the JSON object does not yet have a parse method, give it one.
399
+
400
+ if (typeof JSON.parseIt !== 'function') {
401
+ JSON.parseIt = function (text, reviver) {
402
+
403
+ // The parse method takes a text and an optional reviver function, and returns
404
+ // a JavaScript value if the text is a valid JSON text.
405
+
406
+ var j;
407
+
408
+ function walk(holder, key) {
409
+
410
+ // The walk method is used to recursively walk the resulting structure so
411
+ // that modifications can be made.
412
+
413
+ var k, v, value = holder[key];
414
+ if (value && typeof value === 'object') {
415
+ for (k in value) {
416
+ if (Object.hasOwnProperty.call(value, k)) {
417
+ v = walk(value, k);
418
+ if (v !== undefined) {
419
+ value[k] = v;
420
+ } else {
421
+ delete value[k];
422
+ }
423
+ }
424
+ }
425
+ }
426
+ return reviver.call(holder, key, value);
427
+ }
428
+
429
+
430
+ // Parsing happens in four stages. In the first stage, we replace certain
431
+ // Unicode characters with escape sequences. JavaScript handles many characters
432
+ // incorrectly, either silently deleting them, or treating them as line endings.
433
+
434
+ cx.lastIndex = 0;
435
+ if (cx.test(text)) {
436
+ text = text.replace(cx, function (a) {
437
+ return '\\u' +
438
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
439
+ });
440
+ }
441
+
442
+ // In the second stage, we run the text against regular expressions that look
443
+ // for non-JSON patterns. We are especially concerned with '()' and 'new'
444
+ // because they can cause invocation, and '=' because it can cause mutation.
445
+ // But just to be safe, we want to reject all unexpected forms.
446
+
447
+ // We split the second stage into 4 regexp operations in order to work around
448
+ // crippling inefficiencies in IE's and Safari's regexp engines. First we
449
+ // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
450
+ // replace all simple value tokens with ']' characters. Third, we delete all
451
+ // open brackets that follow a colon or comma or that begin the text. Finally,
452
+ // we look to see that the remaining characters are only whitespace or ']' or
453
+ // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
454
+
455
+ if (/^[\],:{}\s]*$/.
456
+ test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
457
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
458
+ replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
459
+
460
+ // In the third stage we use the eval function to compile the text into a
461
+ // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
462
+ // in JavaScript: it can begin a block or an object literal. We wrap the text
463
+ // in parens to eliminate the ambiguity.
464
+
465
+ j = eval('(' + text + ')');
466
+
467
+ // In the optional fourth stage, we recursively walk the new structure, passing
468
+ // each name/value pair to a reviver function for possible transformation.
469
+
470
+ return typeof reviver === 'function' ?
471
+ walk({'': j}, '') : j;
472
+ }
473
+
474
+ // If the text is not JSON parseable, then a SyntaxError is thrown.
475
+
476
+ throw new SyntaxError('JSON.parseIt');
477
+ };
478
+ }
479
+ }());
480
+
lib/tools.scrollable-1.1.2.js ADDED
@@ -0,0 +1,439 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * tools.scrollable 1.1.2 - Scroll your HTML with eye candy.
3
+ *
4
+ * Copyright (c) 2009 Tero Piirainen
5
+ * http://flowplayer.org/tools/scrollable.html
6
+ *
7
+ * Dual licensed under MIT and GPL 2+ licenses
8
+ * http://www.opensource.org/licenses
9
+ *
10
+ * Launch : March 2008
11
+ * Date: jQuery{date}
12
+ * Revision: jQuery{revision}
13
+ */
14
+ (function(jQuery) {
15
+
16
+ // static constructs
17
+ jQuery.tools = jQuery.tools || {};
18
+
19
+ jQuery.tools.scrollable = {
20
+ version: '1.1.2',
21
+
22
+ conf: {
23
+
24
+ // basics
25
+ size: 5,
26
+ vertical: false,
27
+ speed: 400,
28
+ keyboard: true,
29
+
30
+ // by default this is the same as size
31
+ keyboardSteps: null,
32
+
33
+ // other
34
+ disabledClass: 'disabled',
35
+ hoverClass: null,
36
+ clickable: true,
37
+ activeClass: 'active',
38
+ easing: 'swing',
39
+ loop: false,
40
+
41
+ items: '.items',
42
+ item: null,
43
+
44
+ // navigational elements
45
+ prev: '.prev',
46
+ next: '.next',
47
+ prevPage: '.prevPage',
48
+ nextPage: '.nextPage',
49
+ api: false
50
+
51
+ // CALLBACKS: onBeforeSeek, onSeek, onReload
52
+ }
53
+ };
54
+
55
+ var current;
56
+
57
+ // constructor
58
+ function Scrollable(root, conf) {
59
+
60
+ // current instance
61
+ var self = this, jQueryself = jQuery(this),
62
+ horizontal = !conf.vertical,
63
+ wrap = root.children(),
64
+ index = 0,
65
+ forward;
66
+
67
+
68
+ if (!current) { current = self; }
69
+
70
+ // bind all callbacks from configuration
71
+ jQuery.each(conf, function(name, fn) {
72
+ if (jQuery.isFunction(fn)) { jQueryself.bind(name, fn); }
73
+ });
74
+
75
+ if (wrap.length > 1) { wrap = jQuery(conf.items, root); }
76
+
77
+ // navigational items can be anywhere when globalNav = true
78
+ function find(query) {
79
+ var els = jQuery(query);
80
+ return conf.globalNav ? els : root.parent().find(query);
81
+ }
82
+
83
+ // to be used by plugins
84
+ root.data("finder", find);
85
+
86
+ // get handle to navigational elements
87
+ var prev = find(conf.prev),
88
+ next = find(conf.next),
89
+ prevPage = find(conf.prevPage),
90
+ nextPage = find(conf.nextPage);
91
+
92
+
93
+ // methods
94
+ jQuery.extend(self, {
95
+
96
+ getIndex: function() {
97
+ return index;
98
+ },
99
+
100
+ getClickIndex: function() {
101
+ var items = self.getItems();
102
+ return items.index(items.filter("." + conf.activeClass));
103
+ },
104
+
105
+ getConf: function() {
106
+ return conf;
107
+ },
108
+
109
+ getSize: function() {
110
+ return self.getItems().size();
111
+ },
112
+
113
+ getPageAmount: function() {
114
+ return Math.ceil(this.getSize() / conf.size);
115
+ },
116
+
117
+ getPageIndex: function() {
118
+ return Math.ceil(index / conf.size);
119
+ },
120
+
121
+ getNaviButtons: function() {
122
+ return prev.add(next).add(prevPage).add(nextPage);
123
+ },
124
+
125
+ getRoot: function() {
126
+ return root;
127
+ },
128
+
129
+ getItemWrap: function() {
130
+ return wrap;
131
+ },
132
+
133
+ getItems: function() {
134
+ return wrap.children(conf.item);
135
+ },
136
+
137
+ getVisibleItems: function() {
138
+ return self.getItems().slice(index, index + conf.size);
139
+ },
140
+
141
+ /* all seeking functions depend on this */
142
+ seekTo: function(i, time, fn) {
143
+
144
+ if (i < 0) { i = 0; }
145
+
146
+ // nothing happens
147
+ if (index === i) { return self; }
148
+
149
+ // function given as second argument
150
+ if (jQuery.isFunction(time)) {
151
+ fn = time;
152
+ }
153
+
154
+ // seeking exceeds the end
155
+ if (i > self.getSize() - conf.size) {
156
+ return conf.loop ? self.begin() : this.end();
157
+ }
158
+
159
+ var item = self.getItems().eq(i);
160
+ if (!item.length) { return self; }
161
+
162
+ // onBeforeSeek
163
+ var e = jQuery.Event("onBeforeSeek");
164
+
165
+ jQueryself.trigger(e, i > index);
166
+ if (e.isDefaultPrevented()) { return self; }
167
+
168
+ // get the (possibly altered) speed
169
+ if (time === undefined || jQuery.isFunction(time)) { time = conf.speed; }
170
+
171
+ function callback() {
172
+ if (fn) { fn.call(self, i); }
173
+ jQueryself.trigger("onSeek", [i]);
174
+ }
175
+
176
+ if (horizontal) {
177
+ wrap.animate({left: -item.position().left}, time, conf.easing, callback);
178
+ } else {
179
+ wrap.animate({top: -item.position().top}, time, conf.easing, callback);
180
+ }
181
+
182
+
183
+ current = self;
184
+ index = i;
185
+
186
+ // onStart
187
+ e = jQuery.Event("onStart");
188
+ jQueryself.trigger(e, [i]);
189
+ if (e.isDefaultPrevented()) { return self; }
190
+
191
+
192
+ /* default behaviour */
193
+
194
+ // prev/next buttons disabled flags
195
+ prev.add(prevPage).toggleClass(conf.disabledClass, i === 0);
196
+ next.add(nextPage).toggleClass(conf.disabledClass, i >= self.getSize() - conf.size);
197
+
198
+ return self;
199
+ },
200
+
201
+
202
+ move: function(offset, time, fn) {
203
+ forward = offset > 0;
204
+ return this.seekTo(index + offset, time, fn);
205
+ },
206
+
207
+ next: function(time, fn) {
208
+ return this.move(1, time, fn);
209
+ },
210
+
211
+ prev: function(time, fn) {
212
+ return this.move(-1, time, fn);
213
+ },
214
+
215
+ movePage: function(offset, time, fn) {
216
+ forward = offset > 0;
217
+ var steps = conf.size * offset;
218
+
219
+ var i = index % conf.size;
220
+ if (i > 0) {
221
+ steps += (offset > 0 ? -i : conf.size - i);
222
+ }
223
+
224
+ return this.move(steps, time, fn);
225
+ },
226
+
227
+ prevPage: function(time, fn) {
228
+ return this.movePage(-1, time, fn);
229
+ },
230
+
231
+ nextPage: function(time, fn) {
232
+ return this.movePage(1, time, fn);
233
+ },
234
+
235
+ setPage: function(page, time, fn) {
236
+ return this.seekTo(page * conf.size, time, fn);
237
+ },
238
+
239
+ begin: function(time, fn) {
240
+ forward = false;
241
+ return this.seekTo(0, time, fn);
242
+ },
243
+
244
+ end: function(time, fn) {
245
+ forward = true;
246
+ var to = this.getSize() - conf.size;
247
+ return to > 0 ? this.seekTo(to, time, fn) : self;
248
+ },
249
+
250
+ reload: function() {
251
+ jQueryself.trigger("onReload");
252
+ return self;
253
+ },
254
+
255
+ focus: function() {
256
+ current = self;
257
+ return self;
258
+ },
259
+
260
+ click: function(i) {
261
+
262
+ var item = self.getItems().eq(i),
263
+ klass = conf.activeClass,
264
+ size = conf.size;
265
+
266
+ // check that i is sane
267
+ if (i < 0 || i >= self.getSize()) { return self; }
268
+
269
+ // size == 1
270
+ if (size == 1) {
271
+ if (conf.loop) { return self.next(); }
272
+
273
+ if (i === 0 || i == self.getSize() -1) {
274
+ forward = (forward === undefined) ? true : !forward;
275
+ }
276
+ return forward === false ? self.prev() : self.next();
277
+ }
278
+
279
+ // size == 2
280
+ if (size == 2) {
281
+ if (i == index) { i--; }
282
+ self.getItems().removeClass(klass);
283
+ item.addClass(klass);
284
+ return self.seekTo(i, time, fn);
285
+ }
286
+
287
+ if (!item.hasClass(klass)) {
288
+ self.getItems().removeClass(klass);
289
+ item.addClass(klass);
290
+ var delta = Math.floor(size / 2);
291
+ var to = i - delta;
292
+
293
+ // next to last item must work
294
+ if (to > self.getSize() - size) {
295
+ to = self.getSize() - size;
296
+ }
297
+
298
+ if (to !== i) {
299
+ return self.seekTo(to);
300
+ }
301
+ }
302
+
303
+ return self;
304
+ },
305
+
306
+ // bind / unbind
307
+ bind: function(name, fn) {
308
+ jQueryself.bind(name, fn);
309
+ return self;
310
+ },
311
+
312
+ unbind: function(name) {
313
+ jQueryself.unbind(name);
314
+ return self;
315
+ }
316
+
317
+ });
318
+
319
+ // callbacks
320
+ jQuery.each("onBeforeSeek,onStart,onSeek,onReload".split(","), function(i, ev) {
321
+ self[ev] = function(fn) {
322
+ return self.bind(ev, fn);
323
+ };
324
+ });
325
+
326
+
327
+ // prev button
328
+ prev.addClass(conf.disabledClass).click(function() {
329
+ self.prev();
330
+ });
331
+
332
+
333
+ // next button
334
+ next.click(function() {
335
+ self.next();
336
+ });
337
+
338
+ // prev page button
339
+ nextPage.click(function() {
340
+ self.nextPage();
341
+ });
342
+
343
+ if (self.getSize() < conf.size) {
344
+ next.add(nextPage).addClass(conf.disabledClass);
345
+ }
346
+
347
+
348
+ // next page button
349
+ prevPage.addClass(conf.disabledClass).click(function() {
350
+ self.prevPage();
351
+ });
352
+
353
+
354
+ // hover
355
+ var hc = conf.hoverClass, keyId = "keydown." + Math.random().toString().substring(10);
356
+
357
+ self.onReload(function() {
358
+
359
+ // hovering
360
+ if (hc) {
361
+ self.getItems().hover(function() {
362
+ jQuery(this).addClass(hc);
363
+ }, function() {
364
+ jQuery(this).removeClass(hc);
365
+ });
366
+ }
367
+
368
+ // clickable
369
+ if (conf.clickable) {
370
+ self.getItems().each(function(i) {
371
+ jQuery(this).unbind("click.scrollable").bind("click.scrollable", function(e) {
372
+ if (jQuery(e.target).is("a")) { return; }
373
+ return self.click(i);
374
+ });
375
+ });
376
+ }
377
+
378
+ // keyboard
379
+ if (conf.keyboard) {
380
+
381
+ // keyboard works on one instance at the time. thus we need to unbind first
382
+ jQuery(document).unbind(keyId).bind(keyId, function(evt) {
383
+
384
+ // do nothing with CTRL / ALT buttons
385
+ if (evt.altKey || evt.ctrlKey) { return; }
386
+
387
+ // do nothing for unstatic and unfocused instances
388
+ if (conf.keyboard != 'static' && current != self) { return; }
389
+
390
+ var s = conf.keyboardSteps;
391
+
392
+ if (horizontal && (evt.keyCode == 37 || evt.keyCode == 39)) {
393
+ self.move(evt.keyCode == 37 ? -s : s);
394
+ return evt.preventDefault();
395
+ }
396
+
397
+ if (!horizontal && (evt.keyCode == 38 || evt.keyCode == 40)) {
398
+ self.move(evt.keyCode == 38 ? -s : s);
399
+ return evt.preventDefault();
400
+ }
401
+
402
+ return true;
403
+
404
+ });
405
+
406
+ } else {
407
+ jQuery(document).unbind(keyId);
408
+ }
409
+
410
+ });
411
+
412
+ self.reload();
413
+
414
+ }
415
+
416
+
417
+ // jQuery plugin implementation
418
+ jQuery.fn.scrollable = function(conf) {
419
+
420
+ // already constructed --> return API
421
+ var el = this.eq(typeof conf == 'number' ? conf : 0).data("scrollable");
422
+ if (el) { return el; }
423
+
424
+ var globals = jQuery.extend({}, jQuery.tools.scrollable.conf);
425
+ conf = jQuery.extend(globals, conf);
426
+
427
+ conf.keyboardSteps = conf.keyboardSteps || conf.size;
428
+
429
+ this.each(function() {
430
+ el = new Scrollable(jQuery(this), conf);
431
+ jQuery(this).data("scrollable", el);
432
+ });
433
+
434
+ return conf.api ? el: this;
435
+
436
+ };
437
+
438
+
439
+ })(jQuery);
lib/tools.scrollable.mousewheel-1.0.1.js ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * jQuery TOOLS plugin :: scrollable.mousewheel 1.0.1
3
+ *
4
+ * Copyright (c) 2009 Tero Piirainen
5
+ * http://flowplayer.org/tools/scrollable.html#mousewheel
6
+ *
7
+ * Dual licensed under MIT and GPL 2+ licenses
8
+ * http://www.opensource.org/licenses
9
+ *
10
+ * Launch : September 2009
11
+ * Date: jQuery{date}
12
+ * Revision: jQuery{revision}
13
+ *
14
+ *
15
+ * jquery.event.wheel.js - rev 1
16
+ * Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)
17
+ * Liscensed under the MIT License (MIT-LICENSE.txt)
18
+ * http://www.opensource.org/licenses/mit-license.php
19
+ * Created: 2008-07-01 | Updated: 2008-07-14
20
+ */
21
+ (function(jQuery) {
22
+
23
+ jQuery.fn.wheel = function( fn ){
24
+ return this[ fn ? "bind" : "trigger" ]( "wheel", fn );
25
+ };
26
+
27
+ // special event config
28
+ jQuery.event.special.wheel = {
29
+ setup: function(){
30
+ jQuery.event.add( this, wheelEvents, wheelHandler, {} );
31
+ },
32
+ teardown: function(){
33
+ jQuery.event.remove( this, wheelEvents, wheelHandler );
34
+ }
35
+ };
36
+
37
+ // events to bind ( browser sniffed... )
38
+ var wheelEvents = !jQuery.browser.mozilla ? "mousewheel" : // IE, opera, safari
39
+ "DOMMouseScroll"+( jQuery.browser.version<"1.9" ? " mousemove" : "" ); // firefox
40
+
41
+ // shared event handler
42
+ function wheelHandler( event ) {
43
+
44
+ switch ( event.type ){
45
+
46
+ // FF2 has incorrect event positions
47
+ case "mousemove":
48
+ return jQuery.extend( event.data, { // store the correct properties
49
+ clientX: event.clientX, clientY: event.clientY,
50
+ pageX: event.pageX, pageY: event.pageY
51
+ });
52
+
53
+ // firefox
54
+ case "DOMMouseScroll":
55
+ jQuery.extend( event, event.data ); // fix event properties in FF2
56
+ event.delta = -event.detail / 3; // normalize delta
57
+ break;
58
+
59
+ // IE, opera, safari
60
+ case "mousewheel":
61
+ event.delta = event.wheelDelta / 120;
62
+ break;
63
+ }
64
+
65
+ event.type = "wheel"; // hijack the event
66
+ return jQuery.event.handle.call( this, event, event.delta );
67
+ }
68
+
69
+
70
+ // version number
71
+ var t = jQuery.tools.scrollable;
72
+ t.plugins = t.plugins || {};
73
+ t.plugins.mousewheel = {
74
+ version: '1.0.1',
75
+ conf: {
76
+ api: false,
77
+ speed: 50
78
+ }
79
+ };
80
+
81
+ // scrollable mousewheel implementation
82
+ jQuery.fn.mousewheel = function(conf) {
83
+
84
+ var globals = jQuery.extend({}, t.plugins.mousewheel.conf), ret;
85
+ if (typeof conf == 'number') { conf = {speed: conf}; }
86
+ conf = jQuery.extend(globals, conf);
87
+
88
+ this.each(function() {
89
+
90
+ var api = jQuery(this).scrollable();
91
+ if (api) { ret = api; }
92
+
93
+ api.getRoot().wheel(function(e, delta) {
94
+ api.move(delta < 0 ? 1 : -1, conf.speed || 50);
95
+ return false;
96
+ });
97
+ });
98
+
99
+ return conf.api ? ret : this;
100
+ };
101
+
102
+ })(jQuery);
103
+
lib/ui.core.js ADDED
@@ -0,0 +1,519 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * jQuery UI 1.7.2
3
+ *
4
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
5
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
6
+ * and GPL (GPL-LICENSE.txt) licenses.
7
+ *
8
+ * http://docs.jquery.com/UI
9
+ */
10
+ ;jQuery.ui || (function(jQuery) {
11
+
12
+ var _remove = jQuery.fn.remove,
13
+ isFF2 = jQuery.browser.mozilla && (parseFloat(jQuery.browser.version) < 1.9);
14
+
15
+ //Helper functions and ui object
16
+ jQuery.ui = {
17
+ version: "1.7.2",
18
+
19
+ // jQuery.ui.plugin is deprecated. Use the proxy pattern instead.
20
+ plugin: {
21
+ add: function(module, option, set) {
22
+ var proto = jQuery.ui[module].prototype;
23
+ for(var i in set) {
24
+ proto.plugins[i] = proto.plugins[i] || [];
25
+ proto.plugins[i].push([option, set[i]]);
26
+ }
27
+ },
28
+ call: function(instance, name, args) {
29
+ var set = instance.plugins[name];
30
+ if(!set || !instance.element[0].parentNode) { return; }
31
+
32
+ for (var i = 0; i < set.length; i++) {
33
+ if (instance.options[set[i][0]]) {
34
+ set[i][1].apply(instance.element, args);
35
+ }
36
+ }
37
+ }
38
+ },
39
+
40
+ contains: function(a, b) {
41
+ return document.compareDocumentPosition
42
+ ? a.compareDocumentPosition(b) & 16
43
+ : a !== b && a.contains(b);
44
+ },
45
+
46
+ hasScroll: function(el, a) {
47
+
48
+ //If overflow is hidden, the element might have extra content, but the user wants to hide it
49
+ if (jQuery(el).css('overflow') == 'hidden') { return false; }
50
+
51
+ var scroll = (a && a == 'left') ? 'scrollLeft' : 'scrollTop',
52
+ has = false;
53
+
54
+ if (el[scroll] > 0) { return true; }
55
+
56
+ // TODO: determine which cases actually cause this to happen
57
+ // if the element doesn't have the scroll set, see if it's possible to
58
+ // set the scroll
59
+ el[scroll] = 1;
60
+ has = (el[scroll] > 0);
61
+ el[scroll] = 0;
62
+ return has;
63
+ },
64
+
65
+ isOverAxis: function(x, reference, size) {
66
+ //Determines when x coordinate is over "b" element axis
67
+ return (x > reference) && (x < (reference + size));
68
+ },
69
+
70
+ isOver: function(y, x, top, left, height, width) {
71
+ //Determines when x, y coordinates is over "b" element
72
+ return jQuery.ui.isOverAxis(y, top, height) && jQuery.ui.isOverAxis(x, left, width);
73
+ },
74
+
75
+ keyCode: {
76
+ BACKSPACE: 8,
77
+ CAPS_LOCK: 20,
78
+ COMMA: 188,
79
+ CONTROL: 17,
80
+ DELETE: 46,
81
+ DOWN: 40,
82
+ END: 35,
83
+ ENTER: 13,
84
+ ESCAPE: 27,
85
+ HOME: 36,
86
+ INSERT: 45,
87
+ LEFT: 37,
88
+ NUMPAD_ADD: 107,
89
+ NUMPAD_DECIMAL: 110,
90
+ NUMPAD_DIVIDE: 111,
91
+ NUMPAD_ENTER: 108,
92
+ NUMPAD_MULTIPLY: 106,
93
+ NUMPAD_SUBTRACT: 109,
94
+ PAGE_DOWN: 34,
95
+ PAGE_UP: 33,
96
+ PERIOD: 190,
97
+ RIGHT: 39,
98
+ SHIFT: 16,
99
+ SPACE: 32,
100
+ TAB: 9,
101
+ UP: 38
102
+ }
103
+ };
104
+
105
+ // WAI-ARIA normalization
106
+ if (isFF2) {
107
+ var attr = jQuery.attr,
108
+ removeAttr = jQuery.fn.removeAttr,
109
+ ariaNS = "http://www.w3.org/2005/07/aaa",
110
+ ariaState = /^aria-/,
111
+ ariaRole = /^wairole:/;
112
+
113
+ jQuery.attr = function(elem, name, value) {
114
+ var set = value !== undefined;
115
+
116
+ return (name == 'role'
117
+ ? (set
118
+ ? attr.call(this, elem, name, "wairole:" + value)
119
+ : (attr.apply(this, arguments) || "").replace(ariaRole, ""))
120
+ : (ariaState.test(name)
121
+ ? (set
122
+ ? elem.setAttributeNS(ariaNS,
123
+ name.replace(ariaState, "aaa:"), value)
124
+ : attr.call(this, elem, name.replace(ariaState, "aaa:")))
125
+ : attr.apply(this, arguments)));
126
+ };
127
+
128
+ jQuery.fn.removeAttr = function(name) {
129
+ return (ariaState.test(name)
130
+ ? this.each(function() {
131
+ this.removeAttributeNS(ariaNS, name.replace(ariaState, ""));
132
+ }) : removeAttr.call(this, name));
133
+ };
134
+ }
135
+
136
+ //jQuery plugins
137
+ jQuery.fn.extend({
138
+ remove: function() {
139
+ // Safari has a native remove event which actually removes DOM elements,
140
+ // so we have to use triggerHandler instead of trigger (#3037).
141
+ jQuery("*", this).add(this).each(function() {
142
+ jQuery(this).triggerHandler("remove");
143
+ });
144
+ return _remove.apply(this, arguments );
145
+ },
146
+
147
+ enableSelection: function() {
148
+ return this
149
+ .attr('unselectable', 'off')
150
+ .css('MozUserSelect', '')
151
+ .unbind('selectstart.ui');
152
+ },
153
+
154
+ disableSelection: function() {
155
+ return this
156
+ .attr('unselectable', 'on')
157
+ .css('MozUserSelect', 'none')
158
+ .bind('selectstart.ui', function() { return false; });
159
+ },
160
+
161
+ scrollParent: function() {
162
+ var scrollParent;
163
+ if((jQuery.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
164
+ scrollParent = this.parents().filter(function() {
165
+ return (/(relative|absolute|fixed)/).test(jQuery.curCSS(this,'position',1)) && (/(auto|scroll)/).test(jQuery.curCSS(this,'overflow',1)+jQuery.curCSS(this,'overflow-y',1)+jQuery.curCSS(this,'overflow-x',1));
166
+ }).eq(0);
167
+ } else {
168
+ scrollParent = this.parents().filter(function() {
169
+ return (/(auto|scroll)/).test(jQuery.curCSS(this,'overflow',1)+jQuery.curCSS(this,'overflow-y',1)+jQuery.curCSS(this,'overflow-x',1));
170
+ }).eq(0);
171
+ }
172
+
173
+ return (/fixed/).test(this.css('position')) || !scrollParent.length ? jQuery(document) : scrollParent;
174
+ }
175
+ });
176
+
177
+
178
+ //Additional selectors
179
+ jQuery.extend(jQuery.expr[':'], {
180
+ data: function(elem, i, match) {
181
+ return !!jQuery.data(elem, match[3]);
182
+ },
183
+
184
+ focusable: function(element) {
185
+ var nodeName = element.nodeName.toLowerCase(),
186
+ tabIndex = jQuery.attr(element, 'tabindex');
187
+ return (/input|select|textarea|button|object/.test(nodeName)
188
+ ? !element.disabled
189
+ : 'a' == nodeName || 'area' == nodeName
190
+ ? element.href || !isNaN(tabIndex)
191
+ : !isNaN(tabIndex))
192
+ // the element and all of its ancestors must be visible
193
+ // the browser may report that the area is hidden
194
+ && !jQuery(element)['area' == nodeName ? 'parents' : 'closest'](':hidden').length;
195
+ },
196
+
197
+ tabbable: function(element) {
198
+ var tabIndex = jQuery.attr(element, 'tabindex');
199
+ return (isNaN(tabIndex) || tabIndex >= 0) && jQuery(element).is(':focusable');
200
+ }
201
+ });
202
+
203
+
204
+ // jQuery.widget is a factory to create jQuery plugins
205
+ // taking some boilerplate code out of the plugin code
206
+ function getter(namespace, plugin, method, args) {
207
+ function getMethods(type) {
208
+ var methods = jQuery[namespace][plugin][type] || [];
209
+ return (typeof methods == 'string' ? methods.split(/,?\s+/) : methods);
210
+ }
211
+
212
+ var methods = getMethods('getter');
213
+ if (args.length == 1 && typeof args[0] == 'string') {
214
+ methods = methods.concat(getMethods('getterSetter'));
215
+ }
216
+ return (jQuery.inArray(method, methods) != -1);
217
+ }
218
+
219
+ jQuery.widget = function(name, prototype) {
220
+ var namespace = name.split(".")[0];
221
+ name = name.split(".")[1];
222
+
223
+ // create plugin method
224
+ jQuery.fn[name] = function(options) {
225
+ var isMethodCall = (typeof options == 'string'),
226
+ args = Array.prototype.slice.call(arguments, 1);
227
+
228
+ // prevent calls to internal methods
229
+ if (isMethodCall && options.substring(0, 1) == '_') {
230
+ return this;
231
+ }
232
+
233
+ // handle getter methods
234
+ if (isMethodCall && getter(namespace, name, options, args)) {
235
+ var instance = jQuery.data(this[0], name);
236
+ return (instance ? instance[options].apply(instance, args)
237
+ : undefined);
238
+ }
239
+
240
+ // handle initialization and non-getter methods
241
+ return this.each(function() {
242
+ var instance = jQuery.data(this, name);
243
+
244
+ // constructor
245
+ (!instance && !isMethodCall &&
246
+ jQuery.data(this, name, new jQuery[namespace][name](this, options))._init());
247
+
248
+ // method call
249
+ (instance && isMethodCall && jQuery.isFunction(instance[options]) &&
250
+ instance[options].apply(instance, args));
251
+ });
252
+ };
253
+
254
+ // create widget constructor
255
+ jQuery[namespace] = jQuery[namespace] || {};
256
+ jQuery[namespace][name] = function(element, options) {
257
+ var self = this;
258
+
259
+ this.namespace = namespace;
260
+ this.widgetName = name;
261
+ this.widgetEventPrefix = jQuery[namespace][name].eventPrefix || name;
262
+ this.widgetBaseClass = namespace + '-' + name;
263
+
264
+ this.options = jQuery.extend({},
265
+ jQuery.widget.defaults,
266
+ jQuery[namespace][name].defaults,
267
+ jQuery.metadata && jQuery.metadata.get(element)[name],
268
+ options);
269
+
270
+ this.element = jQuery(element)
271
+ .bind('setData.' + name, function(event, key, value) {
272
+ if (event.target == element) {
273
+ return self._setData(key, value);
274
+ }
275
+ })
276
+ .bind('getData.' + name, function(event, key) {
277
+ if (event.target == element) {
278
+ return self._getData(key);
279
+ }
280
+ })
281
+ .bind('remove', function() {
282
+ return self.destroy();
283
+ });
284
+ };
285
+
286
+ // add widget prototype
287
+ jQuery[namespace][name].prototype = jQuery.extend({}, jQuery.widget.prototype, prototype);
288
+
289
+ // TODO: merge getter and getterSetter properties from widget prototype
290
+ // and plugin prototype
291
+ jQuery[namespace][name].getterSetter = 'option';
292
+ };
293
+
294
+ jQuery.widget.prototype = {
295
+ _init: function() {},
296
+ destroy: function() {
297
+ this.element.removeData(this.widgetName)
298
+ .removeClass(this.widgetBaseClass + '-disabled' + ' ' + this.namespace + '-state-disabled')
299
+ .removeAttr('aria-disabled');
300
+ },
301
+
302
+ option: function(key, value) {
303
+ var options = key,
304
+ self = this;
305
+
306
+ if (typeof key == "string") {
307
+ if (value === undefined) {
308
+ return this._getData(key);
309
+ }
310
+ options = {};
311
+ options[key] = value;
312
+ }
313
+
314
+ jQuery.each(options, function(key, value) {
315
+ self._setData(key, value);
316
+ });
317
+ },
318
+ _getData: function(key) {
319
+ return this.options[key];
320
+ },
321
+ _setData: function(key, value) {
322
+ this.options[key] = value;
323
+
324
+ if (key == 'disabled') {
325
+ this.element
326
+ [value ? 'addClass' : 'removeClass'](
327
+ this.widgetBaseClass + '-disabled' + ' ' +
328
+ this.namespace + '-state-disabled')
329
+ .attr("aria-disabled", value);
330
+ }
331
+ },
332
+
333
+ enable: function() {
334
+ this._setData('disabled', false);
335
+ },
336
+ disable: function() {
337
+ this._setData('disabled', true);
338
+ },
339
+
340
+ _trigger: function(type, event, data) {
341
+ var callback = this.options[type],
342
+ eventName = (type == this.widgetEventPrefix
343
+ ? type : this.widgetEventPrefix + type);
344
+
345
+ event = jQuery.Event(event);
346
+ event.type = eventName;
347
+
348
+ // copy original event properties over to the new event
349
+ // this would happen if we could call jQuery.event.fix instead of jQuery.Event
350
+ // but we don't have a way to force an event to be fixed multiple times
351
+ if (event.originalEvent) {
352
+ for (var i = jQuery.event.props.length, prop; i;) {
353
+ prop = jQuery.event.props[--i];
354
+ event[prop] = event.originalEvent[prop];
355
+ }
356
+ }
357
+
358
+ this.element.trigger(event, data);
359
+
360
+ return !(jQuery.isFunction(callback) && callback.call(this.element[0], event, data) === false
361
+ || event.isDefaultPrevented());
362
+ }
363
+ };
364
+
365
+ jQuery.widget.defaults = {
366
+ disabled: false
367
+ };
368
+
369
+
370
+ /** Mouse Interaction Plugin **/
371
+
372
+ jQuery.ui.mouse = {
373
+ _mouseInit: function() {
374
+ var self = this;
375
+
376
+ this.element
377
+ .bind('mousedown.'+this.widgetName, function(event) {
378
+ return self._mouseDown(event);
379
+ })
380
+ .bind('click.'+this.widgetName, function(event) {
381
+ if(self._preventClickEvent) {
382
+ self._preventClickEvent = false;
383
+ event.stopImmediatePropagation();
384
+ return false;
385
+ }
386
+ });
387
+
388
+ // Prevent text selection in IE
389
+ if (jQuery.browser.msie) {
390
+ this._mouseUnselectable = this.element.attr('unselectable');
391
+ this.element.attr('unselectable', 'on');
392
+ }
393
+
394
+ this.started = false;
395
+ },
396
+
397
+ // TODO: make sure destroying one instance of mouse doesn't mess with
398
+ // other instances of mouse
399
+ _mouseDestroy: function() {
400
+ this.element.unbind('.'+this.widgetName);
401
+
402
+ // Restore text selection in IE
403
+ (jQuery.browser.msie
404
+ && this.element.attr('unselectable', this._mouseUnselectable));
405
+ },
406
+
407
+ _mouseDown: function(event) {
408
+ // don't let more than one widget handle mouseStart
409
+ // TODO: figure out why we have to use originalEvent
410
+ event.originalEvent = event.originalEvent || {};
411
+ if (event.originalEvent.mouseHandled) { return; }
412
+
413
+ // we may have missed mouseup (out of window)
414
+ (this._mouseStarted && this._mouseUp(event));
415
+
416
+ this._mouseDownEvent = event;
417
+
418
+ var self = this,
419
+ btnIsLeft = (event.which == 1),
420
+ elIsCancel = (typeof this.options.cancel == "string" ? jQuery(event.target).parents().add(event.target).filter(this.options.cancel).length : false);
421
+ if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
422
+ return true;
423
+ }
424
+
425
+ this.mouseDelayMet = !this.options.delay;
426
+ if (!this.mouseDelayMet) {
427
+ this._mouseDelayTimer = setTimeout(function() {
428
+ self.mouseDelayMet = true;
429
+ }, this.options.delay);
430
+ }
431
+
432
+ if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
433
+ this._mouseStarted = (this._mouseStart(event) !== false);
434
+ if (!this._mouseStarted) {
435
+ event.preventDefault();
436
+ return true;
437
+ }
438
+ }
439
+
440
+ // these delegates are required to keep context
441
+ this._mouseMoveDelegate = function(event) {
442
+ return self._mouseMove(event);
443
+ };
444
+ this._mouseUpDelegate = function(event) {
445
+ return self._mouseUp(event);
446
+ };
447
+ jQuery(document)
448
+ .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
449
+ .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
450
+
451
+ // preventDefault() is used to prevent the selection of text here -
452
+ // however, in Safari, this causes select boxes not to be selectable
453
+ // anymore, so this fix is needed
454
+ (jQuery.browser.safari || event.preventDefault());
455
+
456
+ event.originalEvent.mouseHandled = true;
457
+ return true;
458
+ },
459
+
460
+ _mouseMove: function(event) {
461
+ // IE mouseup check - mouseup happened when mouse was out of window
462
+ if (jQuery.browser.msie && !event.button) {
463
+ return this._mouseUp(event);
464
+ }
465
+
466
+ if (this._mouseStarted) {
467
+ this._mouseDrag(event);
468
+ return event.preventDefault();
469
+ }
470
+
471
+ if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
472
+ this._mouseStarted =
473
+ (this._mouseStart(this._mouseDownEvent, event) !== false);
474
+ (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
475
+ }
476
+
477
+ return !this._mouseStarted;
478
+ },
479
+
480
+ _mouseUp: function(event) {
481
+ jQuery(document)
482
+ .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
483
+ .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
484
+
485
+ if (this._mouseStarted) {
486
+ this._mouseStarted = false;
487
+ this._preventClickEvent = (event.target == this._mouseDownEvent.target);
488
+ this._mouseStop(event);
489
+ }
490
+
491
+ return false;
492
+ },
493
+
494
+ _mouseDistanceMet: function(event) {
495
+ return (Math.max(
496
+ Math.abs(this._mouseDownEvent.pageX - event.pageX),
497
+ Math.abs(this._mouseDownEvent.pageY - event.pageY)
498
+ ) >= this.options.distance
499
+ );
500
+ },
501
+
502
+ _mouseDelayMet: function(event) {
503
+ return this.mouseDelayMet;
504
+ },
505
+
506
+ // These are placeholder methods, to be overriden by extending plugin
507
+ _mouseStart: function(event) {},
508
+ _mouseDrag: function(event) {},
509
+ _mouseStop: function(event) {},
510
+ _mouseCapture: function(event) { return true; }
511
+ };
512
+
513
+ jQuery.ui.mouse.defaults = {
514
+ cancel: null,
515
+ distance: 1,
516
+ delay: 0
517
+ };
518
+
519
+ })(jQuery);
lib/ui.draggable.js ADDED
@@ -0,0 +1,773 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * jQuery UI Draggable 1.7.2
3
+ *
4
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
5
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
6
+ * and GPL (GPL-LICENSE.txt) licenses.
7
+ *
8
+ * http://docs.jquery.com/UI/Draggables
9
+ *
10
+ * Depends:
11
+ * ui.core.js
12
+ */
13
+ (function(jQuery) {
14
+
15
+ jQuery.widget("ui.draggable", jQuery.extend({}, jQuery.ui.mouse, {
16
+
17
+ _init: function() {
18
+
19
+ if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
20
+ this.element[0].style.position = 'relative';
21
+
22
+ (this.options.addClasses && this.element.addClass("ui-draggable"));
23
+ (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
24
+
25
+ this._mouseInit();
26
+
27
+ },
28
+
29
+ destroy: function() {
30
+ if(!this.element.data('draggable')) return;
31
+ this.element
32
+ .removeData("draggable")
33
+ .unbind(".draggable")
34
+ .removeClass("ui-draggable"
35
+ + " ui-draggable-dragging"
36
+ + " ui-draggable-disabled");
37
+ this._mouseDestroy();
38
+ },
39
+
40
+ _mouseCapture: function(event) {
41
+
42
+ var o = this.options;
43
+
44
+ if (this.helper || o.disabled || jQuery(event.target).is('.ui-resizable-handle'))
45
+ return false;
46
+
47
+ //Quit if we're not on a valid handle
48
+ this.handle = this._getHandle(event);
49
+ if (!this.handle)
50
+ return false;
51
+
52
+ return true;
53
+
54
+ },
55
+
56
+ _mouseStart: function(event) {
57
+
58
+ var o = this.options;
59
+
60
+ //Create and append the visible helper
61
+ this.helper = this._createHelper(event);
62
+
63
+ //Cache the helper size
64
+ this._cacheHelperProportions();
65
+
66
+ //If ddmanager is used for droppables, set the global draggable
67
+ if(jQuery.ui.ddmanager)
68
+ jQuery.ui.ddmanager.current = this;
69
+
70
+ /*
71
+ * - Position generation -
72
+ * This block generates everything position related - it's the core of draggables.
73
+ */
74
+
75
+ //Cache the margins of the original element
76
+ this._cacheMargins();
77
+
78
+ //Store the helper's css position
79
+ this.cssPosition = this.helper.css("position");
80
+ this.scrollParent = this.helper.scrollParent();
81
+
82
+ //The element's absolute position on the page minus margins
83
+ this.offset = this.element.offset();
84
+ this.offset = {
85
+ top: this.offset.top - this.margins.top,
86
+ left: this.offset.left - this.margins.left
87
+ };
88
+
89
+ jQuery.extend(this.offset, {
90
+ click: { //Where the click happened, relative to the element
91
+ left: event.pageX - this.offset.left,
92
+ top: event.pageY - this.offset.top
93
+ },
94
+ parent: this._getParentOffset(),
95
+ relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
96
+ });
97
+
98
+ //Generate the original position
99
+ this.originalPosition = this._generatePosition(event);
100
+ this.originalPageX = event.pageX;
101
+ this.originalPageY = event.pageY;
102
+
103
+ //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
104
+ if(o.cursorAt)
105
+ this._adjustOffsetFromHelper(o.cursorAt);
106
+
107
+ //Set a containment if given in the options
108
+ if(o.containment)
109
+ this._setContainment();
110
+
111
+ //Call plugins and callbacks
112
+ this._trigger("start", event);
113
+
114
+ //Recache the helper size
115
+ this._cacheHelperProportions();
116
+
117
+ //Prepare the droppable offsets
118
+ if (jQuery.ui.ddmanager && !o.dropBehaviour)
119
+ jQuery.ui.ddmanager.prepareOffsets(this, event);
120
+
121
+ this.helper.addClass("ui-draggable-dragging");
122
+ this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
123
+ return true;
124
+ },
125
+
126
+ _mouseDrag: function(event, noPropagation) {
127
+
128
+ //Compute the helpers position
129
+ this.position = this._generatePosition(event);
130
+ this.positionAbs = this._convertPositionTo("absolute");
131
+
132
+ //Call plugins and callbacks and use the resulting position if something is returned
133
+ if (!noPropagation) {
134
+ var ui = this._uiHash();
135
+ this._trigger('drag', event, ui);
136
+ this.position = ui.position;
137
+ }
138
+
139
+ if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
140
+ if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
141
+ if(jQuery.ui.ddmanager) jQuery.ui.ddmanager.drag(this, event);
142
+
143
+ return false;
144
+ },
145
+
146
+ _mouseStop: function(event) {
147
+
148
+ //If we are using droppables, inform the manager about the drop
149
+ var dropped = false;
150
+ if (jQuery.ui.ddmanager && !this.options.dropBehaviour)
151
+ dropped = jQuery.ui.ddmanager.drop(this, event);
152
+
153
+ //if a drop comes from outside (a sortable)
154
+ if(this.dropped) {
155
+ dropped = this.dropped;
156
+ this.dropped = false;
157
+ }
158
+
159
+ if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || (jQuery.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
160
+ var self = this;
161
+ jQuery(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
162
+ self._trigger("stop", event);
163
+ self._clear();
164
+ });
165
+ } else {
166
+ this._trigger("stop", event);
167
+ this._clear();
168
+ }
169
+
170
+ return false;
171
+ },
172
+
173
+ _getHandle: function(event) {
174
+
175
+ var handle = !this.options.handle || !jQuery(this.options.handle, this.element).length ? true : false;
176
+ jQuery(this.options.handle, this.element)
177
+ .find("*")
178
+ .andSelf()
179
+ .each(function() {
180
+ if(this == event.target) handle = true;
181
+ });
182
+
183
+ return handle;
184
+
185
+ },
186
+
187
+ _createHelper: function(event) {
188
+
189
+ var o = this.options;
190
+ var helper = jQuery.isFunction(o.helper) ? jQuery(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element);
191
+
192
+ if(!helper.parents('body').length)
193
+ helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
194
+
195
+ if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
196
+ helper.css("position", "absolute");
197
+
198
+ return helper;
199
+
200
+ },
201
+
202
+ _adjustOffsetFromHelper: function(obj) {
203
+ if(obj.left != undefined) this.offset.click.left = obj.left + this.margins.left;
204
+ if(obj.right != undefined) this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
205
+ if(obj.top != undefined) this.offset.click.top = obj.top + this.margins.top;
206
+ if(obj.bottom != undefined) this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
207
+ },
208
+
209
+ _getParentOffset: function() {
210
+
211
+ //Get the offsetParent and cache its position
212
+ this.offsetParent = this.helper.offsetParent();
213
+ var po = this.offsetParent.offset();
214
+
215
+ // This is a special case where we need to modify a offset calculated on start, since the following happened:
216
+ // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
217
+ // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
218
+ // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
219
+ if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && jQuery.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
220
+ po.left += this.scrollParent.scrollLeft();
221
+ po.top += this.scrollParent.scrollTop();
222
+ }
223
+
224
+ if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
225
+ || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && jQuery.browser.msie)) //Ugly IE fix
226
+ po = { top: 0, left: 0 };
227
+
228
+ return {
229
+ top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
230
+ left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
231
+ };
232
+
233
+ },
234
+
235
+ _getRelativeOffset: function() {
236
+
237
+ if(this.cssPosition == "relative") {
238
+ var p = this.element.position();
239
+ return {
240
+ top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
241
+ left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
242
+ };
243
+ } else {
244
+ return { top: 0, left: 0 };
245
+ }
246
+
247
+ },
248
+
249
+ _cacheMargins: function() {
250
+ this.margins = {
251
+ left: (parseInt(this.element.css("marginLeft"),10) || 0),
252
+ top: (parseInt(this.element.css("marginTop"),10) || 0)
253
+ };
254
+ },
255
+
256
+ _cacheHelperProportions: function() {
257
+ this.helperProportions = {
258
+ width: this.helper.outerWidth(),
259
+ height: this.helper.outerHeight()
260
+ };
261
+ },
262
+
263
+ _setContainment: function() {
264
+
265
+ var o = this.options;
266
+ if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
267
+ if(o.containment == 'document' || o.containment == 'window') this.containment = [
268
+ 0 - this.offset.relative.left - this.offset.parent.left,
269
+ 0 - this.offset.relative.top - this.offset.parent.top,
270
+ jQuery(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
271
+ (jQuery(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
272
+ ];
273
+
274
+ if(!(/^(document|window|parent)jQuery/).test(o.containment) && o.containment.constructor != Array) {
275
+ var ce = jQuery(o.containment)[0]; if(!ce) return;
276
+ var co = jQuery(o.containment).offset();
277
+ var over = (jQuery(ce).css("overflow") != 'hidden');
278
+
279
+ this.containment = [
280
+ co.left + (parseInt(jQuery(ce).css("borderLeftWidth"),10) || 0) + (parseInt(jQuery(ce).css("paddingLeft"),10) || 0) - this.margins.left,
281
+ co.top + (parseInt(jQuery(ce).css("borderTopWidth"),10) || 0) + (parseInt(jQuery(ce).css("paddingTop"),10) || 0) - this.margins.top,
282
+ co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt(jQuery(ce).css("borderLeftWidth"),10) || 0) - (parseInt(jQuery(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
283
+ co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt(jQuery(ce).css("borderTopWidth"),10) || 0) - (parseInt(jQuery(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
284
+ ];
285
+ } else if(o.containment.constructor == Array) {
286
+ this.containment = o.containment;
287
+ }
288
+
289
+ },
290
+
291
+ _convertPositionTo: function(d, pos) {
292
+
293
+ if(!pos) pos = this.position;
294
+ var mod = d == "absolute" ? 1 : -1;
295
+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && jQuery.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
296
+
297
+ return {
298
+ top: (
299
+ pos.top // The absolute mouse position
300
+ + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
301
+ + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
302
+ - (jQuery.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
303
+ ),
304
+ left: (
305
+ pos.left // The absolute mouse position
306
+ + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
307
+ + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
308
+ - (jQuery.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
309
+ )
310
+ };
311
+
312
+ },
313
+
314
+ _generatePosition: function(event) {
315
+
316
+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && jQuery.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
317
+
318
+ // This is another very weird special case that only happens for relative elements:
319
+ // 1. If the css position is relative
320
+ // 2. and the scroll parent is the document or similar to the offset parent
321
+ // we have to refresh the relative offset during the scroll so there are no jumps
322
+ if(this.cssPosition == 'relative' && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) {
323
+ this.offset.relative = this._getRelativeOffset();
324
+ }
325
+
326
+ var pageX = event.pageX;
327
+ var pageY = event.pageY;
328
+
329
+ /*
330
+ * - Position constraining -
331
+ * Constrain the position to a mix of grid, containment.
332
+ */
333
+
334
+ if(this.originalPosition) { //If we are not dragging yet, we won't check for options
335
+
336
+ if(this.containment) {
337
+ if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
338
+ if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
339
+ if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
340
+ if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
341
+ }
342
+
343
+ if(o.grid) {
344
+ var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
345
+ pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
346
+
347
+ var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
348
+ pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
349
+ }
350
+
351
+ }
352
+
353
+ return {
354
+ top: (
355
+ pageY // The absolute mouse position
356
+ - this.offset.click.top // Click offset (relative to the element)
357
+ - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
358
+ - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
359
+ + (jQuery.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
360
+ ),
361
+ left: (
362
+ pageX // The absolute mouse position
363
+ - this.offset.click.left // Click offset (relative to the element)
364
+ - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
365
+ - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
366
+ + (jQuery.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
367
+ )
368
+ };
369
+
370
+ },
371
+
372
+ _clear: function() {
373
+ this.helper.removeClass("ui-draggable-dragging");
374
+ if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
375
+ //if(jQuery.ui.ddmanager) jQuery.ui.ddmanager.current = null;
376
+ this.helper = null;
377
+ this.cancelHelperRemoval = false;
378
+ },
379
+
380
+ // From now on bulk stuff - mainly helpers
381
+
382
+ _trigger: function(type, event, ui) {
383
+ ui = ui || this._uiHash();
384
+ jQuery.ui.plugin.call(this, type, [event, ui]);
385
+ if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
386
+ return jQuery.widget.prototype._trigger.call(this, type, event, ui);
387
+ },
388
+
389
+ plugins: {},
390
+
391
+ _uiHash: function(event) {
392
+ return {
393
+ helper: this.helper,
394
+ position: this.position,
395
+ absolutePosition: this.positionAbs, //deprecated
396
+ offset: this.positionAbs
397
+ };
398
+ }
399
+
400
+ }));
401
+
402
+ jQuery.extend(jQuery.ui.draggable, {
403
+ version: "1.7.2",
404
+ eventPrefix: "drag",
405
+ defaults: {
406
+ addClasses: true,
407
+ appendTo: "parent",
408
+ axis: false,
409
+ cancel: ":input,option",
410
+ connectToSortable: false,
411
+ containment: false,
412
+ cursor: "auto",
413
+ cursorAt: false,
414
+ delay: 0,
415
+ distance: 1,
416
+ grid: false,
417
+ handle: false,
418
+ helper: "original",
419
+ iframeFix: false,
420
+ opacity: false,
421
+ refreshPositions: false,
422
+ revert: false,
423
+ revertDuration: 500,
424
+ scope: "default",
425
+ scroll: true,
426
+ scrollSensitivity: 20,
427
+ scrollSpeed: 20,
428
+ snap: false,
429
+ snapMode: "both",
430
+ snapTolerance: 20,
431
+ stack: false,
432
+ zIndex: false
433
+ }
434
+ });
435
+
436
+ jQuery.ui.plugin.add("draggable", "connectToSortable", {
437
+ start: function(event, ui) {
438
+
439
+ var inst = jQuery(this).data("draggable"), o = inst.options,
440
+ uiSortable = jQuery.extend({}, ui, { item: inst.element });
441
+ inst.sortables = [];
442
+ jQuery(o.connectToSortable).each(function() {
443
+ var sortable = jQuery.data(this, 'sortable');
444
+ if (sortable && !sortable.options.disabled) {
445
+ inst.sortables.push({
446
+ instance: sortable,
447
+ shouldRevert: sortable.options.revert
448
+ });
449
+ sortable._refreshItems(); //Do a one-time refresh at start to refresh the containerCache
450
+ sortable._trigger("activate", event, uiSortable);
451
+ }
452
+ });
453
+
454
+ },
455
+ stop: function(event, ui) {
456
+
457
+ //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
458
+ var inst = jQuery(this).data("draggable"),
459
+ uiSortable = jQuery.extend({}, ui, { item: inst.element });
460
+
461
+ jQuery.each(inst.sortables, function() {
462
+ if(this.instance.isOver) {
463
+
464
+ this.instance.isOver = 0;
465
+
466
+ inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
467
+ this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
468
+
469
+ //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
470
+ if(this.shouldRevert) this.instance.options.revert = true;
471
+
472
+ //Trigger the stop of the sortable
473
+ this.instance._mouseStop(event);
474
+
475
+ this.instance.options.helper = this.instance.options._helper;
476
+
477
+ //If the helper has been the original item, restore properties in the sortable
478
+ if(inst.options.helper == 'original')
479
+ this.instance.currentItem.css({ top: 'auto', left: 'auto' });
480
+
481
+ } else {
482
+ this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
483
+ this.instance._trigger("deactivate", event, uiSortable);
484
+ }
485
+
486
+ });
487
+
488
+ },
489
+ drag: function(event, ui) {
490
+
491
+ var inst = jQuery(this).data("draggable"), self = this;
492
+
493
+ var checkPos = function(o) {
494
+ var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
495
+ var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
496
+ var itemHeight = o.height, itemWidth = o.width;
497
+ var itemTop = o.top, itemLeft = o.left;
498
+
499
+ return jQuery.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
500
+ };
501
+
502
+ jQuery.each(inst.sortables, function(i) {
503
+
504
+ //Copy over some variables to allow calling the sortable's native _intersectsWith
505
+ this.instance.positionAbs = inst.positionAbs;
506
+ this.instance.helperProportions = inst.helperProportions;
507
+ this.instance.offset.click = inst.offset.click;
508
+
509
+ if(this.instance._intersectsWith(this.instance.containerCache)) {
510
+
511
+ //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
512
+ if(!this.instance.isOver) {
513
+
514
+ this.instance.isOver = 1;
515
+ //Now we fake the start of dragging for the sortable instance,
516
+ //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
517
+ //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
518
+ this.instance.currentItem = jQuery(self).clone().appendTo(this.instance.element).data("sortable-item", true);
519
+ this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
520
+ this.instance.options.helper = function() { return ui.helper[0]; };
521
+
522
+ event.target = this.instance.currentItem[0];
523
+ this.instance._mouseCapture(event, true);
524
+ this.instance._mouseStart(event, true, true);
525
+
526
+ //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
527
+ this.instance.offset.click.top = inst.offset.click.top;
528
+ this.instance.offset.click.left = inst.offset.click.left;
529
+ this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
530
+ this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
531
+
532
+ inst._trigger("toSortable", event);
533
+ inst.dropped = this.instance.element; //draggable revert needs that
534
+ //hack so receive/update callbacks work (mostly)
535
+ inst.currentItem = inst.element;
536
+ this.instance.fromOutside = inst;
537
+
538
+ }
539
+
540
+ //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
541
+ if(this.instance.currentItem) this.instance._mouseDrag(event);
542
+
543
+ } else {
544
+
545
+ //If it doesn't intersect with the sortable, and it intersected before,
546
+ //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
547
+ if(this.instance.isOver) {
548
+
549
+ this.instance.isOver = 0;
550
+ this.instance.cancelHelperRemoval = true;
551
+
552
+ //Prevent reverting on this forced stop
553
+ this.instance.options.revert = false;
554
+
555
+ // The out event needs to be triggered independently
556
+ this.instance._trigger('out', event, this.instance._uiHash(this.instance));
557
+
558
+ this.instance._mouseStop(event, true);
559
+ this.instance.options.helper = this.instance.options._helper;
560
+
561
+ //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
562
+ this.instance.currentItem.remove();
563
+ if(this.instance.placeholder) this.instance.placeholder.remove();
564
+
565
+ inst._trigger("fromSortable", event);
566
+ inst.dropped = false; //draggable revert needs that
567
+ }
568
+
569
+ };
570
+
571
+ });
572
+
573
+ }
574
+ });
575
+
576
+ jQuery.ui.plugin.add("draggable", "cursor", {
577
+ start: function(event, ui) {
578
+ var t = jQuery('body'), o = jQuery(this).data('draggable').options;
579
+ if (t.css("cursor")) o._cursor = t.css("cursor");
580
+ t.css("cursor", o.cursor);
581
+ },
582
+ stop: function(event, ui) {
583
+ /*
584
+ * ZTG - Firefox and Chrome and fine with this, but
585
+ * IE throws an error when we try access the draggable
586
+ * options. This is just a safety precaution.
587
+ */
588
+ if(jQuery(this).data('draggable')) {
589
+ var o = jQuery(this).data('draggable').options;
590
+ if (o._cursor) jQuery('body').css("cursor", o._cursor);
591
+ }
592
+ }
593
+ });
594
+
595
+ jQuery.ui.plugin.add("draggable", "iframeFix", {
596
+ start: function(event, ui) {
597
+ var o = jQuery(this).data('draggable').options;
598
+ jQuery(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
599
+ jQuery('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
600
+ .css({
601
+ width: this.offsetWidth+"px", height: this.offsetHeight+"px",
602
+ position: "absolute", opacity: "0.001", zIndex: 1000
603
+ })
604
+ .css(jQuery(this).offset())
605
+ .appendTo("body");
606
+ });
607
+ },
608
+ stop: function(event, ui) {
609
+ jQuery("div.ui-draggable-iframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers
610
+ }
611
+ });
612
+
613
+ jQuery.ui.plugin.add("draggable", "opacity", {
614
+ start: function(event, ui) {
615
+ var t = jQuery(ui.helper), o = jQuery(this).data('draggable').options;
616
+ if(t.css("opacity")) o._opacity = t.css("opacity");
617
+ t.css('opacity', o.opacity);
618
+ },
619
+ stop: function(event, ui) {
620
+ var o = jQuery(this).data('draggable').options;
621
+ if(o._opacity) jQuery(ui.helper).css('opacity', o._opacity);
622
+ }
623
+ });
624
+
625
+ jQuery.ui.plugin.add("draggable", "scroll", {
626
+ start: function(event, ui) {
627
+ var i = jQuery(this).data("draggable");
628
+ if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
629
+ },
630
+ drag: function(event, ui) {
631
+
632
+ var i = jQuery(this).data("draggable"), o = i.options, scrolled = false;
633
+
634
+ if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
635
+
636
+ if(!o.axis || o.axis != 'x') {
637
+ if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
638
+ i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
639
+ else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
640
+ i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
641
+ }
642
+
643
+ if(!o.axis || o.axis != 'y') {
644
+ if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
645
+ i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
646
+ else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
647
+ i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
648
+ }
649
+
650
+ } else {
651
+
652
+ if(!o.axis || o.axis != 'x') {
653
+ if(event.pageY - jQuery(document).scrollTop() < o.scrollSensitivity)
654
+ scrolled = jQuery(document).scrollTop(jQuery(document).scrollTop() - o.scrollSpeed);
655
+ else if(jQuery(window).height() - (event.pageY - jQuery(document).scrollTop()) < o.scrollSensitivity)
656
+ scrolled = jQuery(document).scrollTop(jQuery(document).scrollTop() + o.scrollSpeed);
657
+ }
658
+
659
+ if(!o.axis || o.axis != 'y') {
660
+ if(event.pageX - jQuery(document).scrollLeft() < o.scrollSensitivity)
661
+ scrolled = jQuery(document).scrollLeft(jQuery(document).scrollLeft() - o.scrollSpeed);
662
+ else if(jQuery(window).width() - (event.pageX - jQuery(document).scrollLeft()) < o.scrollSensitivity)
663
+ scrolled = jQuery(document).scrollLeft(jQuery(document).scrollLeft() + o.scrollSpeed);
664
+ }
665
+
666
+ }
667
+
668
+ if(scrolled !== false && jQuery.ui.ddmanager && !o.dropBehaviour)
669
+ jQuery.ui.ddmanager.prepareOffsets(i, event);
670
+
671
+ }
672
+ });
673
+
674
+ jQuery.ui.plugin.add("draggable", "snap", {
675
+ start: function(event, ui) {
676
+
677
+ var i = jQuery(this).data("draggable"), o = i.options;
678
+ i.snapElements = [];
679
+
680
+ jQuery(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
681
+ var jQueryt = jQuery(this); var jQueryo = jQueryt.offset();
682
+ if(this != i.element[0]) i.snapElements.push({
683
+ item: this,
684
+ width: jQueryt.outerWidth(), height: jQueryt.outerHeight(),
685
+ top: jQueryo.top, left: jQueryo.left
686
+ });
687
+ });
688
+
689
+ },
690
+ drag: function(event, ui) {
691
+
692
+ var inst = jQuery(this).data("draggable"), o = inst.options;
693
+ var d = o.snapTolerance;
694
+
695
+ var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
696
+ y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
697
+
698
+ for (var i = inst.snapElements.length - 1; i >= 0; i--){
699
+
700
+ var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
701
+ t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
702
+
703
+ //Yes, I know, this is insane ;)
704
+ if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
705
+ if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, jQuery.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
706
+ inst.snapElements[i].snapping = false;
707
+ continue;
708
+ }
709
+
710
+ if(o.snapMode != 'inner') {
711
+ var ts = Math.abs(t - y2) <= d;
712
+ var bs = Math.abs(b - y1) <= d;
713
+ var ls = Math.abs(l - x2) <= d;
714
+ var rs = Math.abs(r - x1) <= d;
715
+ if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
716
+ if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
717
+ if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
718
+ if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
719
+ }
720
+
721
+ var first = (ts || bs || ls || rs);
722
+
723
+ if(o.snapMode != 'outer') {
724
+ var ts = Math.abs(t - y1) <= d;
725
+ var bs = Math.abs(b - y2) <= d;
726
+ var ls = Math.abs(l - x1) <= d;
727
+ var rs = Math.abs(r - x2) <= d;
728
+ if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
729
+ if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
730
+ if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
731
+ if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
732
+ }
733
+
734
+ if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
735
+ (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, jQuery.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
736
+ inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
737
+
738
+ };
739
+
740
+ }
741
+ });
742
+
743
+ jQuery.ui.plugin.add("draggable", "stack", {
744
+ start: function(event, ui) {
745
+
746
+ var o = jQuery(this).data("draggable").options;
747
+
748
+ var group = jQuery.makeArray(jQuery(o.stack.group)).sort(function(a,b) {
749
+ return (parseInt(jQuery(a).css("zIndex"),10) || o.stack.min) - (parseInt(jQuery(b).css("zIndex"),10) || o.stack.min);
750
+ });
751
+
752
+ jQuery(group).each(function(i) {
753
+ this.style.zIndex = o.stack.min + i;
754
+ });
755
+
756
+ this[0].style.zIndex = o.stack.min + group.length;
757
+
758
+ }
759
+ });
760
+
761
+ jQuery.ui.plugin.add("draggable", "zIndex", {
762
+ start: function(event, ui) {
763
+ var t = jQuery(ui.helper), o = jQuery(this).data("draggable").options;
764
+ if(t.css("zIndex")) o._zIndex = t.css("zIndex");
765
+ t.css('zIndex', o.zIndex);
766
+ },
767
+ stop: function(event, ui) {
768
+ var o = jQuery(this).data("draggable").options;
769
+ if(o._zIndex) jQuery(ui.helper).css('zIndex', o._zIndex);
770
+ }
771
+ });
772
+
773
+ })(jQuery);
lib/ui.droppable.js ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * jQuery UI Droppable 1.7.2
3
+ *
4
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
5
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
6
+ * and GPL (GPL-LICENSE.txt) licenses.
7
+ *
8
+ * http://docs.jquery.com/UI/Droppables
9
+ *
10
+ * Depends:
11
+ * ui.core.js
12
+ * ui.draggable.js
13
+ */
14
+ (function(jQuery) {
15
+
16
+ jQuery.widget("ui.droppable", {
17
+
18
+ _init: function() {
19
+
20
+ var o = this.options, accept = o.accept;
21
+ this.isover = 0; this.isout = 1;
22
+
23
+ this.options.accept = this.options.accept && jQuery.isFunction(this.options.accept) ? this.options.accept : function(d) {
24
+ return d.is(accept);
25
+ };
26
+
27
+ //Store the droppable's proportions
28
+ this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
29
+
30
+ // Add the reference and positions to the manager
31
+ jQuery.ui.ddmanager.droppables[this.options.scope] = jQuery.ui.ddmanager.droppables[this.options.scope] || [];
32
+ jQuery.ui.ddmanager.droppables[this.options.scope].push(this);
33
+
34
+ (this.options.addClasses && this.element.addClass("ui-droppable"));
35
+
36
+ },
37
+
38
+ destroy: function() {
39
+ var drop = jQuery.ui.ddmanager.droppables[this.options.scope];
40
+ for ( var i = 0; i < drop.length; i++ )
41
+ if ( drop[i] == this )
42
+ drop.splice(i, 1);
43
+
44
+ this.element
45
+ .removeClass("ui-droppable ui-droppable-disabled")
46
+ .removeData("droppable")
47
+ .unbind(".droppable");
48
+ },
49
+
50
+ _setData: function(key, value) {
51
+
52
+ if(key == 'accept') {
53
+ this.options.accept = value && jQuery.isFunction(value) ? value : function(d) {
54
+ return d.is(value);
55
+ };
56
+ } else {
57
+ jQuery.widget.prototype._setData.apply(this, arguments);
58
+ }
59
+
60
+ },
61
+
62
+ _activate: function(event) {
63
+ var draggable = jQuery.ui.ddmanager.current;
64
+ if(this.options.activeClass) this.element.addClass(this.options.activeClass);
65
+ (draggable && this._trigger('activate', event, this.ui(draggable)));
66
+ },
67
+
68
+ _deactivate: function(event) {
69
+ var draggable = jQuery.ui.ddmanager.current;
70
+ if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
71
+ (draggable && this._trigger('deactivate', event, this.ui(draggable)));
72
+ },
73
+
74
+ _over: function(event) {
75
+
76
+ var draggable = jQuery.ui.ddmanager.current;
77
+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
78
+
79
+ if (this.options.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
80
+ if(this.options.hoverClass) this.element.addClass(this.options.hoverClass);
81
+ this._trigger('over', event, this.ui(draggable));
82
+ }
83
+
84
+ },
85
+
86
+ _out: function(event) {
87
+
88
+ var draggable = jQuery.ui.ddmanager.current;
89
+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
90
+
91
+ if (this.options.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
92
+ if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
93
+ this._trigger('out', event, this.ui(draggable));
94
+ }
95
+
96
+ },
97
+
98
+ _drop: function(event,custom) {
99
+
100
+ var draggable = custom || jQuery.ui.ddmanager.current;
101
+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element
102
+
103
+ var childrenIntersection = false;
104
+ this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
105
+ var inst = jQuery.data(this, 'droppable');
106
+ if(inst.options.greedy && jQuery.ui.intersect(draggable, jQuery.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)) {
107
+ childrenIntersection = true; return false;
108
+ }
109
+ });
110
+ if(childrenIntersection) return false;
111
+
112
+ if(this.options.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
113
+ if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
114
+ if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
115
+ this._trigger('drop', event, this.ui(draggable));
116
+ return this.element;
117
+ }
118
+
119
+ return false;
120
+
121
+ },
122
+
123
+ ui: function(c) {
124
+ return {
125
+ draggable: (c.currentItem || c.element),
126
+ helper: c.helper,
127
+ position: c.position,
128
+ absolutePosition: c.positionAbs, //deprecated
129
+ offset: c.positionAbs
130
+ };
131
+ }
132
+
133
+ });
134
+
135
+ jQuery.extend(jQuery.ui.droppable, {
136
+ version: "1.7.2",
137
+ eventPrefix: 'drop',
138
+ defaults: {
139
+ accept: '*',
140
+ activeClass: false,
141
+ addClasses: true,
142
+ greedy: false,
143
+ hoverClass: false,
144
+ scope: 'default',
145
+ tolerance: 'intersect'
146
+ }
147
+ });
148
+
149
+ jQuery.ui.intersect = function(draggable, droppable, toleranceMode) {
150
+
151
+ if (!droppable.offset) return false;
152
+
153
+ var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
154
+ y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
155
+ var l = droppable.offset.left, r = l + droppable.proportions.width,
156
+ t = droppable.offset.top, b = t + droppable.proportions.height;
157
+
158
+ switch (toleranceMode) {
159
+ case 'fit':
160
+ return (l < x1 && x2 < r
161
+ && t < y1 && y2 < b);
162
+ break;
163
+ case 'intersect':
164
+ return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
165
+ && x2 - (draggable.helperProportions.width / 2) < r // Left Half
166
+ && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
167
+ && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
168
+ break;
169
+ case 'pointer':
170
+ var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left),
171
+ draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top),
172
+ isOver = jQuery.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width);
173
+ return isOver;
174
+ break;
175
+ case 'touch':
176
+ return (
177
+ (y1 >= t && y1 <= b) || // Top edge touching
178
+ (y2 >= t && y2 <= b) || // Bottom edge touching
179
+ (y1 < t && y2 > b) // Surrounded vertically
180
+ ) && (
181
+ (x1 >= l && x1 <= r) || // Left edge touching
182
+ (x2 >= l && x2 <= r) || // Right edge touching
183
+ (x1 < l && x2 > r) // Surrounded horizontally
184
+ );
185
+ break;
186
+ default:
187
+ return false;
188
+ break;
189
+ }
190
+
191
+ };
192
+
193
+ /*
194
+ This manager tracks offsets of draggables and droppables
195
+ */
196
+ jQuery.ui.ddmanager = {
197
+ current: null,
198
+ droppables: { 'default': [] },
199
+ prepareOffsets: function(t, event) {
200
+
201
+ var m = jQuery.ui.ddmanager.droppables[t.options.scope];
202
+ var type = event ? event.type : null; // workaround for #2317
203
+ var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
204
+
205
+ droppablesLoop: for (var i = 0; i < m.length; i++) {
206
+
207
+ if(m[i].options.disabled || (t && !m[i].options.accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted
208
+ for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item
209
+ m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
210
+
211
+ m[i].offset = m[i].element.offset();
212
+ m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
213
+
214
+ if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables
215
+
216
+ }
217
+
218
+ },
219
+ drop: function(draggable, event) {
220
+
221
+ var dropped = false;
222
+ jQuery.each(jQuery.ui.ddmanager.droppables[draggable.options.scope], function() {
223
+
224
+ if(!this.options) return;
225
+ if (!this.options.disabled && this.visible && jQuery.ui.intersect(draggable, this, this.options.tolerance))
226
+ dropped = this._drop.call(this, event);
227
+
228
+ if (!this.options.disabled && this.visible && this.options.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
229
+ this.isout = 1; this.isover = 0;
230
+ this._deactivate.call(this, event);
231
+ }
232
+
233
+ });
234
+ return dropped;
235
+
236
+ },
237
+ drag: function(draggable, event) {
238
+
239
+ //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
240
+ if(draggable.options.refreshPositions) jQuery.ui.ddmanager.prepareOffsets(draggable, event);
241
+
242
+ //Run through all droppables and check their positions based on specific tolerance options
243
+
244
+ jQuery.each(jQuery.ui.ddmanager.droppables[draggable.options.scope], function() {
245
+
246
+ if(this.options.disabled || this.greedyChild || !this.visible) return;
247
+ var intersects = jQuery.ui.intersect(draggable, this, this.options.tolerance);
248
+
249
+ var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
250
+ if(!c) return;
251
+
252
+ var parentInstance;
253
+ if (this.options.greedy) {
254
+ var parent = this.element.parents(':data(droppable):eq(0)');
255
+ if (parent.length) {
256
+ parentInstance = jQuery.data(parent[0], 'droppable');
257
+ parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
258
+ }
259
+ }
260
+
261
+ // we just moved into a greedy child
262
+ if (parentInstance && c == 'isover') {
263
+ parentInstance['isover'] = 0;
264
+ parentInstance['isout'] = 1;
265
+ parentInstance._out.call(parentInstance, event);
266
+ }
267
+
268
+ this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
269
+ this[c == "isover" ? "_over" : "_out"].call(this, event);
270
+
271
+ // we just moved out of a greedy child
272
+ if (parentInstance && c == 'isout') {
273
+ parentInstance['isout'] = 0;
274
+ parentInstance['isover'] = 1;
275
+ parentInstance._over.call(parentInstance, event);
276
+ }
277
+ });
278
+
279
+ }
280
+ };
281
+
282
+ })(jQuery);
readme.txt ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Editorial Calendar ===
2
+ Contributors: MaryVogt, zgrossbart
3
+ Tags: posts
4
+ Requires at least: 2.8.5
5
+ Tested up to: 2.8.6
6
+ Stable tag: 0.1
7
+
8
+ THIS PLUGIN IS NOT READY FOR DOWNLOAD. Editorial calendar lets you see all your posts and drag and drop them to change the date.
9
+
10
+ == Description ==
11
+
12
+ *THIS PLUGIN IS NOT READY FOR DOWNLOAD. DO NOT RUN IT OUTSIDE OF A TEST ENVIRONMENT.*
13
+
14
+ The editorial calendar lets WordPress administrators and editors manage the dates for multiple posts at once. You see all of your posts in a calendar view and can arrange them via an easy drag and drop interface. The editorial calendar great improves any blog that plans posts in advance or takes contributions from multiple users.
15
+
16
+ == Installation ==
17
+
18
+ 1. Upload the plugin directory <code>editorial-calendar</code> to the <code>wp-content/plugins</code> directory.
19
+ 1. Activate the plugin through the 'Plugins' menu in WordPress.
20
+ 1. Click the new 'Calendar' option under the 'Posts' menu.
21
+
22
+
23
+
24
+ == Screenshots ==
25
+
26
+ 1. We'll get some screen shots soon
27
+
28
+ == Changelog ==
29
+
30
+ = 0.1 =
31
+ * This version is just for beta testers
32
+
33
+ `<?php code(); // goes in backticks ?>`