The Events Calendar - Version 4.3

Version Description

= [4.3] =

Please see the changelog for the complete list of changes in this release. Remember to always make a backup of your database and files before updating!

Download this release

Release Info

Developer borkweb
Plugin Icon The Events Calendar
Version 4.3
Comparing to
See all releases

Code changes from version 4.2.7 to 4.3

Files changed (99) hide show
  1. common/lang/tribe-common.pot +245 -89
  2. common/src/Tribe/Admin/Activation_Page.php +292 -0
  3. common/src/Tribe/Admin/Help_Page.php +6 -6
  4. common/src/Tribe/Admin/Helpers.php +6 -7
  5. common/src/Tribe/Admin/Live_Date_Preview.php +1 -1
  6. common/src/Tribe/Admin/Notice/Archive_Slug_Conflict.php +0 -101
  7. common/src/Tribe/Admin/Notices.php +326 -0
  8. common/src/Tribe/App_Shop.php +23 -28
  9. common/src/Tribe/Asset/Factory.php +0 -1
  10. common/src/Tribe/Assets.php +405 -0
  11. common/src/Tribe/Date_Utils.php +13 -9
  12. common/src/Tribe/Error.php +200 -0
  13. common/src/Tribe/Field.php +143 -72
  14. common/src/Tribe/JSON_LD/Abstract.php +129 -9
  15. common/src/Tribe/Log.php +6 -1
  16. common/src/Tribe/Log/Admin.php +12 -5
  17. common/src/Tribe/Log/File_Logger.php +19 -2
  18. common/src/Tribe/Main.php +133 -72
  19. common/src/Tribe/PUE/Checker.php +473 -187
  20. common/src/Tribe/PUE/Notices.php +373 -0
  21. common/src/Tribe/PUE/Plugin_Info.php +7 -0
  22. common/src/Tribe/PUE/Utility.php +19 -1
  23. common/src/Tribe/Post_History.php +134 -0
  24. common/src/Tribe/Settings_Manager.php +3 -23
  25. common/src/Tribe/Support.php +138 -3
  26. common/src/Tribe/Templates.php +2 -2
  27. common/src/Tribe/Utils/JSON.php +38 -0
  28. common/src/Tribe/View_Helpers.php +1 -1
  29. common/src/admin-views/app-shop.php +5 -5
  30. common/src/admin-views/event-log.php +3 -2
  31. common/src/admin-views/tribe-options-help.php +13 -3
  32. common/src/admin-views/tribe-options-licenses.php +24 -21
  33. common/src/deprecated/Tribe__Admin__Notice__Archive_Slug_Conflict.php +28 -0
  34. common/src/functions/template-tags/general.php +130 -24
  35. common/src/functions/utils.php +62 -0
  36. common/src/resources/css/app-shop.css +48 -37
  37. common/src/resources/css/app-shop.min.css +1 -1
  38. common/src/resources/css/bumpdown.css +71 -0
  39. common/src/resources/css/bumpdown.min.css +1 -0
  40. common/src/resources/css/datatables.css +395 -0
  41. common/src/resources/css/datatables.min.css +1 -0
  42. common/src/resources/css/dependency.css +29 -0
  43. common/src/resources/css/dependency.min.css +1 -0
  44. common/src/resources/css/tribe-common-admin.css +129 -36
  45. common/src/resources/css/tribe-common-admin.min.css +1 -1
  46. common/src/resources/images/app-shop-facebook.jpg +0 -0
  47. common/src/resources/images/app-shop-pro.jpg +0 -0
  48. {src → common/src}/resources/images/donate-link-pro-screenshot.jpg +0 -0
  49. {src → common/src}/resources/images/donate-link-screenshot.jpg +0 -0
  50. common/src/resources/images/spirit-animal.png +0 -0
  51. common/src/resources/js/bumpdown.js +276 -0
  52. common/src/resources/js/bumpdown.min.js +1 -0
  53. common/src/resources/js/dependency.js +88 -0
  54. common/src/resources/js/dependency.min.js +1 -0
  55. common/src/resources/js/inline-bumpdown.js +0 -103
  56. common/src/resources/js/inline-bumpdown.min.js +0 -1
  57. common/src/resources/js/jquery.ba-dotimeout.js +0 -285
  58. common/src/resources/js/jquery.ba-dotimeout.min.js +0 -1
  59. common/src/resources/js/notice-dismiss.js +9 -1
  60. common/src/resources/js/notice-dismiss.min.js +1 -1
  61. common/src/resources/js/pue-notices.js +31 -0
  62. common/src/resources/js/pue-notices.min.js +1 -0
  63. common/src/resources/js/tribe-common.js +92 -0
  64. common/src/resources/js/tribe-common.min.js +1 -0
  65. common/src/resources/js/tribe-datatables.js +117 -0
  66. common/src/resources/js/tribe-datatables.min.js +1 -0
  67. common/src/resources/postcss/app-shop.pcss +74 -48
  68. common/src/resources/postcss/bumpdown.pcss +66 -0
  69. common/src/resources/postcss/datatables.pcss +477 -0
  70. common/src/resources/postcss/dependency.pcss +24 -0
  71. common/src/resources/postcss/tribe-common-admin.pcss +173 -36
  72. common/tribe-common.php +1 -1
  73. common/vendor/clipboard/clipboard.js +742 -0
  74. common/vendor/clipboard/clipboard.min.js +7 -0
  75. common/vendor/datatables/extensions/FixedHeader/css/fixedHeader.dataTables.css +19 -0
  76. common/vendor/datatables/extensions/FixedHeader/css/fixedHeader.dataTables.min.css +1 -0
  77. common/vendor/datatables/extensions/FixedHeader/js/dataTables.fixedHeader.js +672 -0
  78. common/vendor/datatables/extensions/FixedHeader/js/dataTables.fixedHeader.min.js +17 -0
  79. common/vendor/datatables/extensions/Responsive/css/responsive.dataTables.css +178 -0
  80. common/vendor/datatables/extensions/Responsive/css/responsive.dataTables.min.css +1 -0
  81. common/vendor/datatables/extensions/Responsive/js/dataTables.responsive.js +1232 -0
  82. common/vendor/datatables/extensions/Responsive/js/dataTables.responsive.min.js +26 -0
  83. common/vendor/datatables/extensions/Scroller/css/scroller.dataTables.css +20 -0
  84. common/vendor/datatables/extensions/Scroller/css/scroller.dataTables.min.css +1 -0
  85. common/vendor/datatables/extensions/Scroller/js/dataTables.scroller.js +1349 -0
  86. common/vendor/datatables/extensions/Scroller/js/dataTables.scroller.min.js +27 -0
  87. common/vendor/datatables/extensions/Select/css/select.dataTables.css +100 -0
  88. common/vendor/datatables/extensions/Select/css/select.dataTables.min.css +1 -0
  89. common/vendor/datatables/extensions/Select/js/dataTables.select.js +1109 -0
  90. common/vendor/datatables/extensions/Select/js/dataTables.select.min.js +26 -0
  91. common/vendor/datatables/media/css/jquery.dataTables.css +452 -0
  92. common/vendor/datatables/media/css/jquery.dataTables.min.css +1 -0
  93. common/vendor/datatables/media/css/jquery.dataTables_themeroller.css +416 -0
  94. common/vendor/datatables/media/images/sort_asc.png +0 -0
  95. common/vendor/datatables/media/images/sort_asc_disabled.png +0 -0
  96. common/vendor/datatables/media/images/sort_both.png +0 -0
  97. common/vendor/datatables/media/images/sort_desc.png +0 -0
  98. common/vendor/datatables/media/images/sort_desc_disabled.png +0 -0
  99. common/vendor/datatables/media/js/jquery.dataTables.js +15003 -0
common/lang/tribe-common.pot CHANGED
@@ -2,16 +2,32 @@
2
  # This file is distributed under the same license as the Tribe Common package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Tribe Common 4.2.7\n"
6
  "Report-Msgid-Bugs-To: http://m.tri.be/191x\n"
7
- "POT-Creation-Date: 2016-09-14 15:54:58+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
- "PO-Revision-Date: 2016-09-14 15:54\n"
12
  "Last-Translator: \n"
13
  "Language-Team: \n"
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  #: src/Tribe/Admin/Help_Page.php:48
16
  msgid "The Events Calendar"
17
  msgstr ""
@@ -40,19 +56,19 @@ msgstr ""
40
  msgid " and "
41
  msgstr ""
42
 
43
- #: src/Tribe/Admin/Help_Page.php:183 src/Tribe/App_Shop.php:109
44
  msgid "Events Calendar PRO"
45
  msgstr ""
46
 
47
- #: src/Tribe/Admin/Help_Page.php:192 src/Tribe/App_Shop.php:142
48
  msgid "Eventbrite Tickets"
49
  msgstr ""
50
 
51
- #: src/Tribe/Admin/Help_Page.php:200 src/Tribe/App_Shop.php:119
52
  msgid "Community Events"
53
  msgstr ""
54
 
55
- #: src/Tribe/Admin/Help_Page.php:208 src/Tribe/App_Shop.php:152
56
  msgid "Facebook Events"
57
  msgstr ""
58
 
@@ -60,11 +76,11 @@ msgstr ""
60
  msgid "Events Filter Bar"
61
  msgstr ""
62
 
63
- #: src/Tribe/Admin/Help_Page.php:224 src/Tribe/App_Shop.php:132
64
  msgid "Event Tickets Plus"
65
  msgstr ""
66
 
67
- #: src/Tribe/Admin/Help_Page.php:233 src/Tribe/App_Shop.php:125
68
  msgid "Community Tickets"
69
  msgstr ""
70
 
@@ -137,72 +153,52 @@ msgstr ""
137
  msgid "Visit the Add-on Page"
138
  msgstr ""
139
 
140
- #: src/Tribe/Admin/Notice/Archive_Slug_Conflict.php:88
141
- msgid "<a href=\"%s\">Edit the page slug</a>"
 
142
  msgstr ""
143
 
144
- #: src/Tribe/Admin/Notice/Archive_Slug_Conflict.php:89
145
- msgid "Ask the site administrator to edit the page slug"
146
  msgstr ""
147
 
148
- #: src/Tribe/Admin/Notice/Archive_Slug_Conflict.php:94
149
- msgid "<a href=\"%s\">edit Events settings</a>."
150
  msgstr ""
151
 
152
- #: src/Tribe/Admin/Notice/Archive_Slug_Conflict.php:95
153
- msgid " ask the site administrator set a different Events URL slug."
154
  msgstr ""
155
 
156
- #: src/Tribe/App_Shop.php:48 src/Tribe/App_Shop.php:49
157
- #: src/Tribe/App_Shop.php:72
158
- msgid "Event Add-Ons"
159
  msgstr ""
160
 
161
- #: src/Tribe/App_Shop.php:103
162
  msgid "Filter Bar"
163
  msgstr ""
164
 
165
- #: src/Tribe/App_Shop.php:105
166
  msgid "It is awesome that your calendar is <em>THE PLACE</em> to get hooked up with prime choice ways to spend time. You have more events than Jabba the Hutt has rolls. Too bad visitors are hiring a personal assistant to go through all the choices. Ever wish you could just filter the calendar to only show events in walking distance, on a weekend, that are free? BOOM. Now you can. Introducing… the Filter Bar."
167
  msgstr ""
168
 
169
- #: src/Tribe/App_Shop.php:112
170
- msgid "The Events Calendar PRO is a paid Add-On to our open source WordPress plugin %1$sThe Events Calendar%2$s. PRO offers a whole host of calendar features including recurring events, custom event attributes, saved venues and organizers, venue pages, advanced event admin and lots more."
171
- msgstr ""
172
-
173
- #: src/Tribe/App_Shop.php:121
174
  msgid "Enable users to submit events to your calendar with Community Events. You can require user accounts or allow visitors to submit without an account. Want to make sure that nothing fishy is going on? Just turn on moderation. Decide if users can edit and manage their own events, or simply submit. Plus, no scary form setup! Just activate, configure the options & off you go."
175
  msgstr ""
176
 
177
- #: src/Tribe/App_Shop.php:127
178
  msgid "Enable Community Events organizers to offer tickets to their events. You can set flexible payment and fee options. They can even check-in attendees to their events! All of this managed from the front-end of your site without ever needing to grant access to your admin"
179
  msgstr ""
180
 
181
- #: src/Tribe/App_Shop.php:128
182
  msgctxt "Names of required plugins for Community Tickets"
183
  msgid "Event Tickets Plus and Community Events"
184
  msgstr ""
185
 
186
- #: src/Tribe/App_Shop.php:135
187
- msgid "Event Tickets Plus allows you to sell tickets to your events using WooCommerce, Shopp, WP eCommerce, or Easy Digital Downloads. Use it on your posts and pages, or add %1$sThe Events Calendar%2$s and sell tickets from your events listings."
188
- msgstr ""
189
-
190
- #: src/Tribe/App_Shop.php:145
191
  msgid "The Eventbrite Tickets add-on allows you to create & sell tickets through The Events Calendar using the power of %1$sEventbrite%2$s. Whether you’re creating your ticket on the WordPress dashboard or importing the details of an already-existing event from %1$sEventbrite.com%2$s, this add-on brings the power of the Eventbrite API to your calendar."
192
  msgstr ""
193
 
194
- #: src/Tribe/App_Shop.php:154
195
- msgid "With the Facebook Events add-on, imported events are manually or automagically created as entries in The Events Calendar. Basic event data along with venue and organizer are populated appropriately. No more entering information in two places, or having to recreate someone else's listing for a public event you want to include on your WordPress calendar."
196
- msgstr ""
197
-
198
- #: src/Tribe/App_Shop.php:158
199
- msgid "iCal Importer"
200
- msgstr ""
201
-
202
- #: src/Tribe/App_Shop.php:160
203
- msgid "The iCal Importer helps you keep your events calendar full of interesting events! You can import events from any website that publishes an iCal (aka ICS) feed and add them to your listings. The recurring import feature lets you keep your calendar brimming without manual oversight (though you can review every imported event if you like). Add filtering by keyword or geographic region and you can be sure that the kinds of events you get are the kinds you want."
204
- msgstr ""
205
-
206
  #: src/Tribe/Credits.php:31
207
  msgid "This calendar is powered by %1$s."
208
  msgstr ""
@@ -215,33 +211,37 @@ msgstr ""
215
  msgid "Rate %1$sEvent Tickets%2$s %3$s"
216
  msgstr ""
217
 
218
- #: src/Tribe/Field.php:209
 
 
 
 
219
  msgid "Invalid field type specified"
220
  msgstr ""
221
 
222
- #: src/Tribe/Field.php:466
223
  msgid "No radio options specified"
224
  msgstr ""
225
 
226
- #: src/Tribe/Field.php:502
227
  msgid "No checkbox options specified"
228
  msgstr ""
229
 
230
- #: src/Tribe/Field.php:558
231
  msgid "No select options specified"
232
  msgstr ""
233
 
234
- #: src/Tribe/Log/Admin.php:132
235
  msgctxt "log selector"
236
  msgid "None currently available"
237
  msgstr ""
238
 
239
- #: src/Tribe/Log/Admin.php:147
240
  msgctxt "log engines"
241
  msgid "None currently available"
242
  msgstr ""
243
 
244
- #: src/Tribe/Log/File_Logger.php:116
245
  msgid "Default (uses temporary files)"
246
  msgstr ""
247
 
@@ -269,40 +269,147 @@ msgstr ""
269
  msgid "Full debug (all events)"
270
  msgstr ""
271
 
272
- #: src/Tribe/PUE/Checker.php:308
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  msgid "License Key"
274
  msgstr ""
275
 
276
- #: src/Tribe/PUE/Checker.php:309
277
  msgid "A valid license key is required for support and updates"
278
  msgstr ""
279
 
280
- #: src/Tribe/PUE/Checker.php:380
281
  msgid "License key(s) updated."
282
  msgstr ""
283
 
284
- #: src/Tribe/PUE/Checker.php:419
 
 
 
 
285
  msgid "unknown date"
286
  msgstr ""
287
 
288
- #: src/Tribe/PUE/Checker.php:422
289
  msgid "Sorry, key validation server is not available."
290
  msgstr ""
291
 
292
- #: src/Tribe/PUE/Checker.php:432
293
  msgid "Valid Key! Expires on %s"
294
  msgstr ""
295
 
296
- #: src/Tribe/PUE/Checker.php:437
297
- msgid "Thanks for setting up a valid key, it will expire on %s"
298
  msgstr ""
299
 
300
- #: src/Tribe/PUE/Checker.php:445
301
- msgid "Hmmm... something's wrong with this validator. Please contact %ssupport%s."
 
 
 
 
 
 
 
 
 
 
 
 
302
  msgstr ""
303
 
304
- #: src/Tribe/PUE/Checker.php:457
305
- msgid "Sorry, there is a problem with your license key. You'll need to %scheck your license%s to have access to updates, downloads, and support."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  msgstr ""
307
 
308
  #: src/Tribe/Plugin_Download_Notice.php:68
@@ -364,27 +471,27 @@ msgstr[1] ""
364
  msgid "Settings saved."
365
  msgstr ""
366
 
367
- #: src/Tribe/Settings_Manager.php:55
368
  msgid "General"
369
  msgstr ""
370
 
371
- #: src/Tribe/Settings_Manager.php:56
372
  msgid "Display"
373
  msgstr ""
374
 
375
- #: src/Tribe/Settings_Manager.php:62 src/Tribe/Settings_Manager.php:289
376
- msgid "Help"
377
- msgstr ""
378
-
379
- #: src/Tribe/Settings_Manager.php:229
380
  msgid "Network"
381
  msgstr ""
382
 
383
- #: src/Tribe/Settings_Manager.php:263
384
- #: src/admin-views/tribe-options-licenses.php:46
385
  msgid "Licenses"
386
  msgstr ""
387
 
 
 
 
 
388
  #: src/Tribe/Settings_Tab.php:222
389
  msgid "There are no fields setup for this tab yet."
390
  msgstr ""
@@ -417,18 +524,38 @@ msgstr ""
417
  msgid "Information about recent template changes and potentially impacted template overrides is provided below."
418
  msgstr ""
419
 
420
- #: src/Tribe/Support.php:159
421
  msgid "English"
422
  msgstr ""
423
 
424
- #: src/Tribe/Support.php:176 src/Tribe/Support.php:177
425
  msgid "Unknown or not set"
426
  msgstr ""
427
 
428
- #: src/Tribe/Support.php:187
429
  msgid "Rewrite rules were purged on load of this help page. Chances are there is a rewrite rule flush occurring in a plugin or theme!"
430
  msgstr ""
431
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
  #: src/Tribe/Validate.php:76 src/Tribe/Validate.php:117
433
  msgid "Invalid or incomplete field passed"
434
  msgstr ""
@@ -737,7 +864,7 @@ msgid "Costa Rica"
737
  msgstr ""
738
 
739
  #: src/Tribe/View_Helpers.php:99
740
- msgid "Cote D'Ivoire"
741
  msgstr ""
742
 
743
  #: src/Tribe/View_Helpers.php:100
@@ -1685,7 +1812,15 @@ msgid "Wyoming"
1685
  msgstr ""
1686
 
1687
  #: src/admin-views/app-shop.php:4
1688
- msgid "Tribe Event Add-Ons"
 
 
 
 
 
 
 
 
1689
  msgstr ""
1690
 
1691
  #: src/admin-views/event-log.php:21
@@ -1700,11 +1835,11 @@ msgstr ""
1700
  msgid "View"
1701
  msgstr ""
1702
 
1703
- #: src/admin-views/event-log.php:99
1704
  msgid "The selected log file is empty or has not been generated yet."
1705
  msgstr ""
1706
 
1707
- #: src/admin-views/event-log.php:104
1708
  msgid "Download log"
1709
  msgstr ""
1710
 
@@ -1842,30 +1977,51 @@ msgid "System Information"
1842
  msgstr ""
1843
 
1844
  #: src/admin-views/tribe-options-help.php:35
1845
- msgid "The details of your calendar plugin and settings is often needed for you or our staff to help troubleshoot an issue. We may ask you to share this information if you ask for support. If you post in one of our premium forums, please copy and paste this information into the System Information field and it will help us help you faster!"
1846
  msgstr ""
1847
 
1848
- #: src/admin-views/tribe-options-help.php:37
1849
  msgid "Recent Template Changes"
1850
  msgstr ""
1851
 
1852
- #: src/admin-views/tribe-options-help.php:57
 
 
 
 
1853
  msgid "News and Tutorials"
1854
  msgstr ""
1855
 
1856
- #: src/admin-views/tribe-options-help.php:63
1857
  msgid "More..."
1858
  msgstr ""
1859
 
1860
- #: src/admin-views/tribe-options-licenses.php:13
1861
- msgid "<p>The license key you received when completing your purchase from %1$s will grant you access to support and updates until it expires. You do not need to enter the key below for the plugins to work, but you will need to enter it to get automatic updates. <strong>Find your license keys at <a href=\"%2$s\" target=\"_blank\">%3$s</a></strong>.</p> <p>Each paid add-on has its own unique license key. Simply paste the key into its appropriate field on below, and give it a moment to validate. You know you're set when a green expiration date appears alongside a \"valid\" message.</p> <p>If you're seeing a red message telling you that your key isn't valid or is out of installs, visit <a href=\"%4$s\" target=\"_blank\">%5$s</a> to manage your installs or renew / upgrade your license.</p><p>Not seeing an update but expecting one? In WordPress, go to <a href=\"%6$s\">Dashboard > Updates</a> and click \"Check Again\".</p>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1862
  msgstr ""
1863
 
1864
- #: src/admin-views/tribe-options-licenses.php:18
1865
  msgid "%1$s Using our plugins in a multisite network? %2$s Please note that your license key will be applied to the entire network, not just this site."
1866
  msgstr ""
1867
 
1868
- #: src/admin-views/tribe-options-licenses.php:27
1869
  msgid "Only license fields for %1$snetwork activated%2$s plugins will be listed on this screen. "
1870
  msgstr ""
1871
 
2
  # This file is distributed under the same license as the Tribe Common package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Tribe Common 4.3\n"
6
  "Report-Msgid-Bugs-To: http://m.tri.be/191x\n"
7
+ "POT-Creation-Date: 2016-10-06 11:31:19+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2016-10-06 11:31\n"
12
  "Last-Translator: \n"
13
  "Language-Team: \n"
14
 
15
+ #: src/Tribe/Admin/Activation_Page.php:77
16
+ msgid "Go to plugins page"
17
+ msgstr ""
18
+
19
+ #: src/Tribe/Admin/Activation_Page.php:77
20
+ msgid "Return to Plugins page"
21
+ msgstr ""
22
+
23
+ #: src/Tribe/Admin/Activation_Page.php:85
24
+ msgid "Go to WordPress Updates page"
25
+ msgstr ""
26
+
27
+ #: src/Tribe/Admin/Activation_Page.php:85
28
+ msgid "Return to WordPress Updates"
29
+ msgstr ""
30
+
31
  #: src/Tribe/Admin/Help_Page.php:48
32
  msgid "The Events Calendar"
33
  msgstr ""
56
  msgid " and "
57
  msgstr ""
58
 
59
+ #: src/Tribe/Admin/Help_Page.php:183 src/Tribe/App_Shop.php:110
60
  msgid "Events Calendar PRO"
61
  msgstr ""
62
 
63
+ #: src/Tribe/Admin/Help_Page.php:192 src/Tribe/App_Shop.php:149
64
  msgid "Eventbrite Tickets"
65
  msgstr ""
66
 
67
+ #: src/Tribe/Admin/Help_Page.php:200 src/Tribe/App_Shop.php:136
68
  msgid "Community Events"
69
  msgstr ""
70
 
71
+ #: src/Tribe/Admin/Help_Page.php:208
72
  msgid "Facebook Events"
73
  msgstr ""
74
 
76
  msgid "Events Filter Bar"
77
  msgstr ""
78
 
79
+ #: src/Tribe/Admin/Help_Page.php:224 src/Tribe/App_Shop.php:120
80
  msgid "Event Tickets Plus"
81
  msgstr ""
82
 
83
+ #: src/Tribe/Admin/Help_Page.php:233 src/Tribe/App_Shop.php:142
84
  msgid "Community Tickets"
85
  msgstr ""
86
 
153
  msgid "Visit the Add-on Page"
154
  msgstr ""
155
 
156
+ #: src/Tribe/App_Shop.php:48 src/Tribe/App_Shop.php:49
157
+ #: src/Tribe/App_Shop.php:72
158
+ msgid "Event Add-Ons"
159
  msgstr ""
160
 
161
+ #: src/Tribe/App_Shop.php:104
162
+ msgid "Event Aggregator"
163
  msgstr ""
164
 
165
+ #: src/Tribe/App_Shop.php:106
166
+ msgid "Importing events from multiple sources has never been easier! Event Aggregator helps you curate and manage event import feeds from Facebook, Meetup, Google Calendar, iCalendar, CSV, and ICS. Schedule automatic imports or manually import events when you’re ready. Event Aggregator provides a convenient dashboard to manage bulk imports, filters, one-way sync, import history, and more."
167
  msgstr ""
168
 
169
+ #: src/Tribe/App_Shop.php:113
170
+ msgid "The Events Calendar PRO is a paid Add-On to our open source WordPress plugin %1$sThe Events Calendar%2$s. PRO offers a whole host of calendar features including recurring events, custom event attributes, saved venues and organizers, venue pages, advanced event admin and lots more."
171
  msgstr ""
172
 
173
+ #: src/Tribe/App_Shop.php:123
174
+ msgid "Event Tickets Plus allows you to sell tickets to your events using WooCommerce, Shopp, WP eCommerce, or Easy Digital Downloads. Use it on your posts and pages, or add %1$sThe Events Calendar%2$s and sell tickets from your events listings."
 
175
  msgstr ""
176
 
177
+ #: src/Tribe/App_Shop.php:130
178
  msgid "Filter Bar"
179
  msgstr ""
180
 
181
+ #: src/Tribe/App_Shop.php:132
182
  msgid "It is awesome that your calendar is <em>THE PLACE</em> to get hooked up with prime choice ways to spend time. You have more events than Jabba the Hutt has rolls. Too bad visitors are hiring a personal assistant to go through all the choices. Ever wish you could just filter the calendar to only show events in walking distance, on a weekend, that are free? BOOM. Now you can. Introducing… the Filter Bar."
183
  msgstr ""
184
 
185
+ #: src/Tribe/App_Shop.php:138
 
 
 
 
186
  msgid "Enable users to submit events to your calendar with Community Events. You can require user accounts or allow visitors to submit without an account. Want to make sure that nothing fishy is going on? Just turn on moderation. Decide if users can edit and manage their own events, or simply submit. Plus, no scary form setup! Just activate, configure the options & off you go."
187
  msgstr ""
188
 
189
+ #: src/Tribe/App_Shop.php:144
190
  msgid "Enable Community Events organizers to offer tickets to their events. You can set flexible payment and fee options. They can even check-in attendees to their events! All of this managed from the front-end of your site without ever needing to grant access to your admin"
191
  msgstr ""
192
 
193
+ #: src/Tribe/App_Shop.php:145
194
  msgctxt "Names of required plugins for Community Tickets"
195
  msgid "Event Tickets Plus and Community Events"
196
  msgstr ""
197
 
198
+ #: src/Tribe/App_Shop.php:152
 
 
 
 
199
  msgid "The Eventbrite Tickets add-on allows you to create & sell tickets through The Events Calendar using the power of %1$sEventbrite%2$s. Whether you’re creating your ticket on the WordPress dashboard or importing the details of an already-existing event from %1$sEventbrite.com%2$s, this add-on brings the power of the Eventbrite API to your calendar."
200
  msgstr ""
201
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  #: src/Tribe/Credits.php:31
203
  msgid "This calendar is powered by %1$s."
204
  msgstr ""
211
  msgid "Rate %1$sEvent Tickets%2$s %3$s"
212
  msgstr ""
213
 
214
+ #: src/Tribe/Error.php:38
215
+ msgid "An Unknown error occurred"
216
+ msgstr ""
217
+
218
+ #: src/Tribe/Field.php:210
219
  msgid "Invalid field type specified"
220
  msgstr ""
221
 
222
+ #: src/Tribe/Field.php:482
223
  msgid "No radio options specified"
224
  msgstr ""
225
 
226
+ #: src/Tribe/Field.php:518
227
  msgid "No checkbox options specified"
228
  msgstr ""
229
 
230
+ #: src/Tribe/Field.php:574
231
  msgid "No select options specified"
232
  msgstr ""
233
 
234
+ #: src/Tribe/Log/Admin.php:136
235
  msgctxt "log selector"
236
  msgid "None currently available"
237
  msgstr ""
238
 
239
+ #: src/Tribe/Log/Admin.php:151
240
  msgctxt "log engines"
241
  msgid "None currently available"
242
  msgstr ""
243
 
244
+ #: src/Tribe/Log/File_Logger.php:123
245
  msgid "Default (uses temporary files)"
246
  msgstr ""
247
 
269
  msgid "Full debug (all events)"
270
  msgstr ""
271
 
272
+ #: src/Tribe/Main.php:168 src/admin-views/tribe-options-help.php:43
273
+ msgid "Copy to clipboard"
274
+ msgstr ""
275
+
276
+ #: src/Tribe/Main.php:169
277
+ msgid "System info copied"
278
+ msgstr ""
279
+
280
+ #: src/Tribe/Main.php:170
281
+ msgid "Press \"Cmd + C\" to copy"
282
+ msgstr ""
283
+
284
+ #: src/Tribe/Main.php:187
285
+ msgid ": activate to sort column ascending"
286
+ msgstr ""
287
+
288
+ #: src/Tribe/Main.php:188
289
+ msgid ": activate to sort column descending"
290
+ msgstr ""
291
+
292
+ #: src/Tribe/Main.php:190
293
+ msgid "Show _MENU_ entries"
294
+ msgstr ""
295
+
296
+ #: src/Tribe/Main.php:191
297
+ msgid "No data available in table"
298
+ msgstr ""
299
+
300
+ #: src/Tribe/Main.php:192
301
+ msgid "Showing _START_ to _END_ of _TOTAL_ entries"
302
+ msgstr ""
303
+
304
+ #: src/Tribe/Main.php:193
305
+ msgid "Showing 0 to 0 of 0 entries"
306
+ msgstr ""
307
+
308
+ #: src/Tribe/Main.php:194
309
+ msgid "(filtered from _MAX_ total entries)"
310
+ msgstr ""
311
+
312
+ #: src/Tribe/Main.php:195
313
+ msgid "No matching records found"
314
+ msgstr ""
315
+
316
+ #: src/Tribe/Main.php:196
317
+ msgid "Search:"
318
+ msgstr ""
319
+
320
+ #: src/Tribe/Main.php:198
321
+ msgid "All"
322
+ msgstr ""
323
+
324
+ #: src/Tribe/Main.php:199
325
+ msgid "Next"
326
+ msgstr ""
327
+
328
+ #: src/Tribe/Main.php:200
329
+ msgid "Previous"
330
+ msgstr ""
331
+
332
+ #: src/Tribe/Main.php:205
333
+ msgid ": Selected %d rows"
334
+ msgstr ""
335
+
336
+ #: src/Tribe/Main.php:206
337
+ msgid ": Selected 1 row"
338
+ msgstr ""
339
+
340
+ #: src/Tribe/PUE/Checker.php:414
341
  msgid "License Key"
342
  msgstr ""
343
 
344
+ #: src/Tribe/PUE/Checker.php:415
345
  msgid "A valid license key is required for support and updates"
346
  msgstr ""
347
 
348
+ #: src/Tribe/PUE/Checker.php:497
349
  msgid "License key(s) updated."
350
  msgstr ""
351
 
352
+ #: src/Tribe/PUE/Checker.php:506
353
+ msgid "Hmmm... something's wrong with this validator. Please contact %ssupport%s."
354
+ msgstr ""
355
+
356
+ #: src/Tribe/PUE/Checker.php:541
357
  msgid "unknown date"
358
  msgstr ""
359
 
360
+ #: src/Tribe/PUE/Checker.php:547
361
  msgid "Sorry, key validation server is not available."
362
  msgstr ""
363
 
364
+ #: src/Tribe/PUE/Checker.php:560
365
  msgid "Valid Key! Expires on %s"
366
  msgstr ""
367
 
368
+ #: src/Tribe/PUE/Checker.php:565
369
+ msgid "Thanks for setting up a valid key. It will expire on %s"
370
  msgstr ""
371
 
372
+ #: src/Tribe/PUE/Checker.php:586 src/Tribe/PUE/Notices.php:267
373
+ msgid "Renew Your License Now"
374
+ msgstr ""
375
+
376
+ #: src/Tribe/PUE/Checker.php:588 src/Tribe/PUE/Notices.php:269
377
+ msgid " (opens in a new window)"
378
+ msgstr ""
379
+
380
+ #: src/Tribe/PUE/Checker.php:614
381
+ msgid "There is an update for %s. You'll need to %scheck your license%s to have access to updates, downloads, and support."
382
+ msgstr ""
383
+
384
+ #: src/Tribe/PUE/Checker.php:641
385
+ msgid "There is an update for %s. %sRenew your license%s to get access to bug fixes, security updates, and new features."
386
  msgstr ""
387
 
388
+ #: src/Tribe/PUE/Notices.php:221
389
+ msgid "It looks like you're using %s, but the license key you supplied does not appear to be valid or is missing. Please review and fix so that you can always have access to our latest versions!"
390
+ msgid_plural "It looks like you're using %s, but the license keys you supplied do not appear to be valid or are missing. Please review and fix so that you can always have access to our latest versions!"
391
+ msgstr[0] ""
392
+ msgstr[1] ""
393
+
394
+ #: src/Tribe/PUE/Notices.php:254
395
+ msgid "There is an update available for %1$s but your license has expired. %2$sVisit the Events Calendar website to renew your license.%3$s"
396
+ msgid_plural "Updates are available for %1$s but your license keys have expired. %2$sVisit the Events Calendar website to renew your licenses.%3$s"
397
+ msgstr[0] ""
398
+ msgstr[1] ""
399
+
400
+ #: src/Tribe/PUE/Notices.php:286
401
+ msgid "You have entered a license key for %1$s but the key is out of installs. %2$sVisit the Events Calendar website%3$s to to manage your installs, upgrade your license, or purchase a new one."
402
+ msgid_plural "You have entered license keys for %1$s but your keys are out of installs. %2$sVisit the Events Calendar website%3$s to to manage your installs, upgrade your licenses, or purchase new ones."
403
+ msgstr[0] ""
404
+ msgstr[1] ""
405
+
406
+ #: src/Tribe/PUE/Notices.php:324
407
+ msgid "You can find your license keys by logging in to %1$syour account on theeventscalendar.com%2$s and you can enter them over on the %3$ssettings page%2$s."
408
+ msgstr ""
409
+
410
+ #: src/Tribe/PUE/Notices.php:368
411
+ msgctxt "formatted plugin list"
412
+ msgid "%1$s and %2$s"
413
  msgstr ""
414
 
415
  #: src/Tribe/Plugin_Download_Notice.php:68
471
  msgid "Settings saved."
472
  msgstr ""
473
 
474
+ #: src/Tribe/Settings_Manager.php:54
475
  msgid "General"
476
  msgstr ""
477
 
478
+ #: src/Tribe/Settings_Manager.php:55
479
  msgid "Display"
480
  msgstr ""
481
 
482
+ #: src/Tribe/Settings_Manager.php:219
 
 
 
 
483
  msgid "Network"
484
  msgstr ""
485
 
486
+ #: src/Tribe/Settings_Manager.php:253
487
+ #: src/admin-views/tribe-options-licenses.php:66
488
  msgid "Licenses"
489
  msgstr ""
490
 
491
+ #: src/Tribe/Settings_Manager.php:279
492
+ msgid "Help"
493
+ msgstr ""
494
+
495
  #: src/Tribe/Settings_Tab.php:222
496
  msgid "There are no fields setup for this tab yet."
497
  msgstr ""
524
  msgid "Information about recent template changes and potentially impacted template overrides is provided below."
525
  msgstr ""
526
 
527
+ #: src/Tribe/Support.php:163
528
  msgid "English"
529
  msgstr ""
530
 
531
+ #: src/Tribe/Support.php:179 src/Tribe/Support.php:180
532
  msgid "Unknown or not set"
533
  msgstr ""
534
 
535
+ #: src/Tribe/Support.php:190
536
  msgid "Rewrite rules were purged on load of this help page. Chances are there is a rewrite rule flush occurring in a plugin or theme!"
537
  msgstr ""
538
 
539
+ #: src/Tribe/Support.php:289
540
+ msgid "Yes, automatically share my system information with the Modern Tribe support team"
541
+ msgstr ""
542
+
543
+ #: src/Tribe/Support.php:290
544
+ msgid "Your system information will only be used by the Modern Tribe support team. All information is stored securely. We do not share this information with any third parties."
545
+ msgstr ""
546
+
547
+ #: src/Tribe/Support.php:309 src/Tribe/Support.php:314
548
+ msgid "Invalid Key"
549
+ msgstr ""
550
+
551
+ #: src/Tribe/Support.php:342 src/Tribe/Support.php:368
552
+ msgid "Permission Error"
553
+ msgstr ""
554
+
555
+ #: src/Tribe/Support.php:356
556
+ msgid "Unique System Info Key Generated"
557
+ msgstr ""
558
+
559
  #: src/Tribe/Validate.php:76 src/Tribe/Validate.php:117
560
  msgid "Invalid or incomplete field passed"
561
  msgstr ""
864
  msgstr ""
865
 
866
  #: src/Tribe/View_Helpers.php:99
867
+ msgid "C&ocirc;te d'Ivoire"
868
  msgstr ""
869
 
870
  #: src/Tribe/View_Helpers.php:100
1812
  msgstr ""
1813
 
1814
  #: src/admin-views/app-shop.php:4
1815
+ msgid "Events Add-Ons"
1816
+ msgstr ""
1817
+
1818
+ #: src/admin-views/app-shop.php:5
1819
+ msgid "Browse All Add-Ons"
1820
+ msgstr ""
1821
+
1822
+ #: src/admin-views/app-shop.php:32
1823
+ msgid "Buy This Add-On"
1824
  msgstr ""
1825
 
1826
  #: src/admin-views/event-log.php:21
1835
  msgid "View"
1836
  msgstr ""
1837
 
1838
+ #: src/admin-views/event-log.php:88
1839
  msgid "The selected log file is empty or has not been generated yet."
1840
  msgstr ""
1841
 
1842
+ #: src/admin-views/event-log.php:105
1843
  msgid "Download log"
1844
  msgstr ""
1845
 
1977
  msgstr ""
1978
 
1979
  #: src/admin-views/tribe-options-help.php:35
1980
+ msgid "The details of your calendar plugin and settings is often needed for you or our staff to help troubleshoot an issue. Please opt-in below to automatically share your system information with our support team. This will allow us to assist you faster if you post in our forums."
1981
  msgstr ""
1982
 
1983
+ #: src/admin-views/tribe-options-help.php:45
1984
  msgid "Recent Template Changes"
1985
  msgstr ""
1986
 
1987
+ #: src/admin-views/tribe-options-help.php:48
1988
+ msgid "Event Log"
1989
+ msgstr ""
1990
+
1991
+ #: src/admin-views/tribe-options-help.php:67
1992
  msgid "News and Tutorials"
1993
  msgstr ""
1994
 
1995
+ #: src/admin-views/tribe-options-help.php:73
1996
  msgid "More..."
1997
  msgstr ""
1998
 
1999
+ #: src/admin-views/tribe-options-licenses.php:11
2000
+ #: src/admin-views/tribe-options-licenses.php:16
2001
+ msgid " (opens in new window)"
2002
+ msgstr ""
2003
+
2004
+ #: src/admin-views/tribe-options-licenses.php:15
2005
+ msgid "The license key you received when completing your purchase from %1$s will grant you access to support and updates until it expires. You do not need to enter the key below for the plugins to work, but you will need to enter it to get automatic updates. %3$sFind your license keys at %2$s%4$s."
2006
+ msgstr ""
2007
+
2008
+ #: src/admin-views/tribe-options-licenses.php:22
2009
+ msgid "Each paid add-on has its own unique license key. Simply paste the key into its appropriate field below, and give it a moment to validate. You know you're set when a green expiration date appears alongside a \"valid\" message."
2010
+ msgstr ""
2011
+
2012
+ #: src/admin-views/tribe-options-licenses.php:25
2013
+ msgid "If you're seeing a red message telling you that your key isn't valid or is out of installs, visit %1$s to manage your installs or renew / upgrade your license."
2014
+ msgstr ""
2015
+
2016
+ #: src/admin-views/tribe-options-licenses.php:30
2017
+ msgid "Not seeing an update but expecting one? In WordPress, go to %1$sDashboard > Updates%2$s and click \"Check Again\"."
2018
  msgstr ""
2019
 
2020
+ #: src/admin-views/tribe-options-licenses.php:38
2021
  msgid "%1$s Using our plugins in a multisite network? %2$s Please note that your license key will be applied to the entire network, not just this site."
2022
  msgstr ""
2023
 
2024
+ #: src/admin-views/tribe-options-licenses.php:47
2025
  msgid "Only license fields for %1$snetwork activated%2$s plugins will be listed on this screen. "
2026
  msgstr ""
2027
 
common/src/Tribe/Admin/Activation_Page.php ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Shows a welcome or update message after the plugin is installed/updated.
4
+ */
5
+ class Tribe__Admin__Activation_Page {
6
+ protected $args = array();
7
+ public $update_slug = 'update-message-';
8
+ public $welcome_slug = 'welcome-message-';
9
+ protected $current_context = '';
10
+
11
+ /**
12
+ * Handles the update/welcome splash screen.
13
+ *
14
+ * @param array $args {
15
+ * Plugin specific slugs and option names used to manage when the splash screen displays.
16
+ *
17
+ * @type string $slug
18
+ * @type string $version
19
+ * @type string $plugin_path
20
+ * @type string $version_history_slug
21
+ * @type string $update_page_title
22
+ * @type string $update_page_template
23
+ * @type string $welcome_page_title
24
+ * @type string $welcome_page_template
25
+ * }
26
+ */
27
+ public function __construct( array $args = array() ) {
28
+ $this->args = wp_parse_args( $args, array(
29
+ 'slug' => '',
30
+ 'activation_transient' => '',
31
+ 'version' => '',
32
+ 'plugin_path' => '',
33
+ 'version_history_slug' => '',
34
+ 'update_page_title' => '',
35
+ 'update_page_template' => '',
36
+ 'welcome_page_title' => '',
37
+ 'welcome_page_template' => '',
38
+ ) );
39
+
40
+ $this->update_slug .= $this->args['slug'];
41
+ $this->welcome_slug .= $this->args['slug'];
42
+
43
+ $this->hooks();
44
+ }
45
+
46
+ /**
47
+ * Listen for opportunities to show update and welcome splash pages.
48
+ */
49
+ public function hooks() {
50
+ add_action( 'admin_init', array( $this, 'maybe_redirect' ), 10, 0 );
51
+ add_action( 'admin_menu', array( $this, 'register_page' ), 100, 0 ); // come in after the default page is registered
52
+
53
+ add_action( 'update_plugin_complete_actions', array( $this, 'update_complete_actions' ), 15, 2 );
54
+ add_action( 'update_bulk_plugins_complete_actions', array( $this, 'update_complete_actions' ), 15, 2 );
55
+ }
56
+
57
+ /**
58
+ * Filter the Default WordPress actions when updating the plugin to prevent users to be redirected if they have an
59
+ * specific intention of going back to the plugins page.
60
+ *
61
+ * @param array $actions The Array of links (html)
62
+ * @param string $plugin Which plugins are been updated
63
+ * @return array The filtered Links
64
+ */
65
+ public function update_complete_actions( $actions, $plugin ) {
66
+ $plugins = array();
67
+
68
+ if ( ! empty( $_GET['plugins'] ) ) {
69
+ $plugins = explode( ',', esc_attr( $_GET['plugins'] ) );
70
+ }
71
+
72
+ if ( ! in_array( $this->args['plugin_path'], $plugins ) ) {
73
+ return $actions;
74
+ }
75
+
76
+ if ( isset( $actions['plugins_page'] ) ) {
77
+ $actions['plugins_page'] = '<a href="' . esc_url( self_admin_url( 'plugins.php?tribe-skip-welcome' ) ) . '" title="' . esc_attr__( 'Go to plugins page', 'tribe-common' ) . '" target="_parent">' . esc_html__( 'Return to Plugins page' ) . '</a>';
78
+
79
+ if ( ! current_user_can( 'activate_plugins' ) ) {
80
+ unset( $actions['plugins_page'] );
81
+ }
82
+ }
83
+
84
+ if ( isset( $actions['updates_page'] ) ) {
85
+ $actions['updates_page'] = '<a href="' . esc_url( self_admin_url( 'update-core.php?tribe-skip-welcome' ) ) . '" title="' . esc_attr__( 'Go to WordPress Updates page', 'tribe-common' ) . '" target="_parent">' . esc_html__( 'Return to WordPress Updates' ) . '</a>';
86
+ }
87
+
88
+ return $actions;
89
+ }
90
+
91
+ /**
92
+ * Maybe redirect to the welcome page (or to the update page - though this is
93
+ * currently disabled).
94
+ */
95
+ public function maybe_redirect() {
96
+ if ( ! empty( $_POST ) ) {
97
+ return; // don't interrupt anything the user's trying to do
98
+ }
99
+
100
+ if ( ! is_admin() || defined( 'DOING_AJAX' ) ) {
101
+ return;
102
+ }
103
+
104
+ if ( defined( 'IFRAME_REQUEST' ) && IFRAME_REQUEST ) {
105
+ return; // probably the plugin update/install iframe
106
+ }
107
+
108
+ if ( isset( $_GET[ $this->welcome_slug ] ) || isset( $_GET[ $this->update_slug ] ) ) {
109
+ return; // no infinite redirects
110
+ }
111
+
112
+ if ( isset( $_GET['tribe-skip-welcome'] ) ) {
113
+ return; // a way to skip these checks and
114
+ }
115
+
116
+ // bail if we aren't activating a plugin
117
+ if ( ! get_transient( $this->args['activation_transient'] ) ) {
118
+ return;
119
+ }
120
+
121
+ delete_transient( $this->args['activation_transient'] );
122
+
123
+ if ( ! current_user_can( Tribe__Settings::instance()->requiredCap ) ) {
124
+ return;
125
+ }
126
+
127
+ if ( $this->showed_update_message_for_current_version() ) {
128
+ return;
129
+ }
130
+
131
+ // the redirect might be intercepted by another plugin, but
132
+ // we'll go ahead and mark it as viewed right now, just in case
133
+ // we end up in a redirect loop
134
+ // see #31088
135
+ $this->log_display_of_message_page();
136
+
137
+ if ( $this->is_new_install() ) {
138
+ $this->redirect_to_welcome_page();
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Have we shown the welcome/update message for the current version?
144
+ *
145
+ * @return bool
146
+ */
147
+ protected function showed_update_message_for_current_version() {
148
+ $message_version_displayed = Tribe__Settings_Manager::get_option( 'last-update-message-' . $this->args['slug'] );
149
+
150
+ if ( empty( $message_version_displayed ) ) {
151
+ return false;
152
+ }
153
+ if ( version_compare( $message_version_displayed, $this->args['version'], '<' ) ) {
154
+ return false;
155
+ }
156
+ return true;
157
+ }
158
+
159
+ /**
160
+ * Records the fact that we displayed the update message in relation to a specific
161
+ * version of the plugin (so we don't show it again until/unless they update to
162
+ * a higher version).
163
+ */
164
+ protected function log_display_of_message_page() {
165
+ Tribe__Settings_Manager::set_option( 'last-update-message-' . $this->args['slug'], $this->args['version'] );
166
+ }
167
+
168
+ /**
169
+ * The previous_ecp_versions option will be empty or set to 0
170
+ * if the current version is the first version to be installed.
171
+ *
172
+ * @return bool
173
+ * @see Tribe__Events__Main::maybeSetTECVersion()
174
+ */
175
+ protected function is_new_install() {
176
+ $previous_versions = Tribe__Settings_Manager::get_option( $this->args['version_history_slug'] );
177
+ return empty( $previous_versions ) || ( end( $previous_versions ) == '0' );
178
+ }
179
+
180
+ /**
181
+ * Handles taking a user to a post-installation welcome page.
182
+ */
183
+ protected function redirect_to_welcome_page() {
184
+ $url = $this->get_message_page_url( $this->welcome_slug );
185
+ wp_safe_redirect( $url );
186
+ exit();
187
+ }
188
+
189
+ /**
190
+ * Handles taking the user to a post-update splash screen.
191
+ *
192
+ * Disused since TEC PR 88 (targeting Tribe__Events__Activation_Page,
193
+ * which this class was derived from).
194
+ *
195
+ * @see https://github.com/moderntribe/the-events-calendar/pull/88
196
+ *
197
+ * @todo decide whether to reinstate or remove
198
+ */
199
+ protected function redirect_to_update_page() {
200
+ $url = $this->get_message_page_url( $this->update_slug );
201
+ wp_safe_redirect( $url );
202
+ exit();
203
+ }
204
+
205
+ /**
206
+ * Return the URL of the splash page.
207
+ *
208
+ * @param string $slug
209
+ *
210
+ * @return string
211
+ */
212
+ protected function get_message_page_url( $slug ) {
213
+ $settings = Tribe__Settings::instance();
214
+ // get the base settings page url
215
+ $url = apply_filters(
216
+ 'tribe_settings_url',
217
+ add_query_arg( 'page', $settings->adminSlug, admin_url( 'edit.php' ) )
218
+ );
219
+ $url = esc_url_raw( add_query_arg( $slug, 1, $url ) );
220
+ return $url;
221
+ }
222
+
223
+ /**
224
+ * Dynamically registers the splash page when required.
225
+ */
226
+ public function register_page() {
227
+ if ( isset( $_GET[ $this->welcome_slug ] ) ) {
228
+ $this->current_context = 'welcome';
229
+ } elseif ( isset( $_GET[ $this->update_slug ] ) ) {
230
+ $this->current_context = 'update';
231
+ }
232
+
233
+ if ( ! empty( $this->current_context ) ) {
234
+ $this->disable_default_settings_page();
235
+ add_action( Tribe__Settings::instance()->admin_page, array( $this, 'display_page' ) );
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Deactivates the regular settings screen (the splash screen will display
241
+ * in the Events > Settings slot instead, for this request only).
242
+ */
243
+ protected function disable_default_settings_page() {
244
+ remove_action( Tribe__Settings::instance()->admin_page, array( Tribe__Settings::instance(), 'generatePage' ) );
245
+ }
246
+
247
+ /**
248
+ * Prints the splash screen.
249
+ *
250
+ * @param string $context
251
+ *
252
+ * @return string|null
253
+ */
254
+ public function display_page() {
255
+ if ( empty( $this->args[ $this->current_context . '_page_title' ] ) || empty( $this->args[ $this->current_context . '_page_template'] ) ) {
256
+ return null;
257
+ }
258
+
259
+ do_action( 'tribe_settings_top' );
260
+
261
+ $context = isset( $_GET[ $this->welcome_slug ] ) ? 'welcome': 'update';
262
+ $title = esc_html( $this->args[ $context . '_page_title'] );
263
+ $html = $this->get_view( $this->args[ $context . '_page_template'] );
264
+
265
+ echo "
266
+ <div class='tribe_settings tribe_{$context}_page wrap'>
267
+ <h1> {$title} </h1>
268
+ {$html}
269
+ </div>
270
+ ";
271
+
272
+ do_action( 'tribe_settings_bottom' );
273
+ $this->log_display_of_message_page();
274
+ }
275
+
276
+ /**
277
+ * Returns the output of the specified template.
278
+ *
279
+ * @param string $path
280
+ *
281
+ * @return string
282
+ */
283
+ protected function get_view( $path ) {
284
+ if ( ! file_exists( $path ) ) {
285
+ return '';
286
+ }
287
+
288
+ ob_start();
289
+ include $path;
290
+ return ob_get_clean();
291
+ }
292
+ }
common/src/Tribe/Admin/Help_Page.php CHANGED
@@ -428,7 +428,7 @@ class Tribe__Admin__Help_Page {
428
  // Loop to start the HTML
429
  foreach ( $mixed as &$line ) {
430
  // If we have content we use that
431
- if ( ! empty( $line->content ) ) {
432
  $line = $line->content;
433
  }
434
 
@@ -503,14 +503,14 @@ class Tribe__Admin__Help_Page {
503
  * @return int
504
  */
505
  protected function by_priority( $a, $b ) {
506
- if ( empty( $a->priority ) || empty( $b->priority ) || $a->priority === $b->priority ) {
507
- if ( empty( $a->unique_call_order ) || empty( $b->unique_call_order ) ) {
508
  return 0;
509
  } else {
510
- return $a->unique_call_order - $b->unique_call_order;
511
  }
512
  } else {
513
- return $a->priority - $b->priority;
514
  }
515
  }
516
 
@@ -859,4 +859,4 @@ class Tribe__Admin__Help_Page {
859
  </div>
860
  <?php
861
  }
862
- }
428
  // Loop to start the HTML
429
  foreach ( $mixed as &$line ) {
430
  // If we have content we use that
431
+ if ( isset( $line->content ) ) {
432
  $line = $line->content;
433
  }
434
 
503
  * @return int
504
  */
505
  protected function by_priority( $a, $b ) {
506
+ if ( ! isset( $a->priority ) || ! isset( $b->priority ) || $a->priority === $b->priority ) {
507
+ if ( ! isset( $a->unique_call_order ) || ! isset( $b->unique_call_order ) ) {
508
  return 0;
509
  } else {
510
+ return $a->unique_call_order > $b->unique_call_order ? 1 : -1;
511
  }
512
  } else {
513
+ return $a->priority > $b->priority ? 1 : -1;
514
  }
515
  }
516
 
859
  </div>
860
  <?php
861
  }
862
+ }
common/src/Tribe/Admin/Helpers.php CHANGED
@@ -103,13 +103,6 @@ class Tribe__Admin__Helpers {
103
  return true;
104
  }
105
 
106
- // Match any post type page in the supported post types
107
- $defaults = apply_filters( 'tribe_is_post_type_screen_post_types', Tribe__Main::get_post_types() );
108
-
109
- if ( in_array( $current_screen->post_type, $defaults ) ) {
110
- return true;
111
- }
112
-
113
  // Match any of the pages set
114
  if ( ! is_scalar( $id ) && in_array( $current_screen->id, (array) $id ) ) {
115
  return true;
@@ -120,6 +113,12 @@ class Tribe__Admin__Helpers {
120
  return true;
121
  }
122
 
 
 
 
 
 
 
123
  return false;
124
  }
125
 
103
  return true;
104
  }
105
 
 
 
 
 
 
 
 
106
  // Match any of the pages set
107
  if ( ! is_scalar( $id ) && in_array( $current_screen->id, (array) $id ) ) {
108
  return true;
113
  return true;
114
  }
115
 
116
+ // Match any post type page in the supported post types
117
+ $defaults = apply_filters( 'tribe_is_post_type_screen_post_types', Tribe__Main::get_post_types() );
118
+ if ( ! in_array( $current_screen->post_type, $defaults ) ) {
119
+ return false;
120
+ }
121
+
122
  return false;
123
  }
124
 
common/src/Tribe/Admin/Live_Date_Preview.php CHANGED
@@ -52,7 +52,7 @@ class Tribe__Admin__Live_Date_Preview {
52
  * Enquues a script to handle live refresh of the date previews.
53
  */
54
  public function live_refresh_script() {
55
- $url = Tribe__Template_Factory::getMinFile( tribe_resource_url( 'admin-date-preview.js', false, 'common' ), true );
56
  wp_enqueue_script( 'tribe-date-live-refresh', $url, array( 'jquery' ), false, true );
57
  }
58
  }
52
  * Enquues a script to handle live refresh of the date previews.
53
  */
54
  public function live_refresh_script() {
55
+ $url = Tribe__Template_Factory::getMinFile( tribe_resource_url( 'admin-date-preview.js', false, null, Tribe__Main::instance() ), true );
56
  wp_enqueue_script( 'tribe-date-live-refresh', $url, array( 'jquery' ), false, true );
57
  }
58
  }
common/src/Tribe/Admin/Notice/Archive_Slug_Conflict.php DELETED
@@ -1,101 +0,0 @@
1
- <?php
2
-
3
-
4
- /**
5
- * Class Tribe__Admin__Notice__Archive_Slug_Conflict
6
- *
7
- * Takes care of adding an admin notice if a page with the `/events` slug has been created in the site.
8
- */
9
- class Tribe__Admin__Notice__Archive_Slug_Conflict {
10
-
11
- /**
12
- * @var static
13
- */
14
- protected static $instance;
15
-
16
- /**
17
- * @var string The slug of The Events Calendar archive page.
18
- */
19
- protected $archive_slug;
20
-
21
- /**
22
- * @var WP_Post The page post object.
23
- */
24
- protected $page;
25
-
26
- /**
27
- * @return Tribe__Admin__Notice__Archive_Slug_Conflict
28
- */
29
- public static function instance() {
30
- if ( empty( self::$instance ) ) {
31
- self::$instance = new self();
32
- }
33
-
34
- return self::$instance;
35
- }
36
-
37
- /**
38
- * Hooks the action to show an admin notice if a page with the `/events` slug exists on the site.
39
- */
40
- public function maybe_add_admin_notice() {
41
- $this->archive_slug = Tribe__Settings_Manager::get_option( 'eventsSlug', 'events' );
42
- $page = get_page_by_path( $this->archive_slug );
43
- if ( ! $page || $page->post_status == 'trash' ) {
44
- return;
45
- }
46
- $this->page = $page;
47
- $dismissed_notices = get_user_meta( get_current_user_id(), 'tribe-dismiss-notice' );
48
-
49
- if ( is_array( $dismissed_notices ) && in_array( 'archive-slug-conflict', $dismissed_notices ) ) {
50
- return;
51
- }
52
- add_action( 'admin_notices', array( $this, 'notice' ) );
53
- }
54
-
55
- /**
56
- * Hooked before maybe_add_admin_notice to prevent a notice to show it has been dimissed
57
- * @return void
58
- */
59
- public function maybe_dismiss() {
60
- if ( empty( $_GET['tribe-dismiss-notice'] ) ) {
61
- return;
62
- }
63
-
64
- $notice = esc_attr( $_GET['tribe-dismiss-notice'] );
65
-
66
- if ( 'archive-slug-conflict' !== $notice ) {
67
- return;
68
- }
69
-
70
- $dimissed_notices = get_user_meta( get_current_user_id(), 'tribe-dismiss-notice' );
71
- if ( in_array( 'archive-slug-conflict', $dimissed_notices ) ) {
72
- return;
73
- }
74
-
75
- add_user_meta( get_current_user_id(), 'tribe-dismiss-notice', 'archive-slug-conflict', false );
76
- }
77
-
78
- /**
79
- * Echoes the admin notice to the page
80
- */
81
- public function notice() {
82
- // What's happening?
83
- $page_title = apply_filters( 'the_title', $this->page->post_title, $this->page->ID );
84
- $line_1 = __( sprintf( 'The page "%1$s" uses the "/%2$s" slug: the Events Calendar plugin will show its calendar in place of the page.', $page_title, $this->archive_slug ), 'tribe-common' );
85
-
86
- // What the user can do
87
- $page_edit_link = get_edit_post_link( $this->page->ID );
88
- $can_edit_page_link = sprintf( __( '<a href="%s">Edit the page slug</a>', 'tribe-common' ), $page_edit_link );
89
- $page_edit_link_string = current_user_can( 'edit_pages' ) ? $can_edit_page_link : __( 'Ask the site administrator to edit the page slug', 'tribe-common' );
90
-
91
- $settings_cap = apply_filters( 'tribe_settings_req_cap', 'manage_options' );
92
- $admin_slug = apply_filters( 'tribe_settings_admin_slug', 'tribe-common' );
93
- $setting_page_link = apply_filters( 'tribe_settings_url', admin_url( 'edit.php?page=' . $admin_slug . '#tribe-field-eventsSlug' ) );
94
- $can_edit_settings_link = sprintf( __( '<a href="%s">edit Events settings</a>.', 'tribe-common' ), $setting_page_link );
95
- $events_settings_link_string = current_user_can( $settings_cap ) ? $can_edit_settings_link : __( ' ask the site administrator set a different Events URL slug.', 'tribe-common' );
96
-
97
- $line_2 = __( sprintf( '%1$s or %2$s', $page_edit_link_string, $events_settings_link_string ), 'tribe-common' );
98
-
99
- echo sprintf( '<div id="message" class="notice error is-dismissible tribe-dismiss-notice" data-ref="archive-slug-conflict"><p>%s</p><p>%s</p></div>', $line_1, $line_2 );
100
- }
101
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/Tribe/Admin/Notices.php ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Don't load directly
3
+ defined( 'WPINC' ) or die;
4
+
5
+ /**
6
+ * @since 4.3
7
+ */
8
+ class Tribe__Admin__Notices {
9
+ /**
10
+ * Static singleton variable
11
+ *
12
+ * @var self
13
+ */
14
+ private static $instance;
15
+
16
+ /**
17
+ * Static Singleton Factory Method
18
+ *
19
+ * @return self
20
+ */
21
+ public static function instance() {
22
+ if ( ! isset( self::$instance ) ) {
23
+ self::$instance = new self;
24
+ }
25
+
26
+ return self::$instance;
27
+ }
28
+
29
+ /**
30
+ * User Meta Key that stores which notices have been dimissed
31
+ *
32
+ * @var string
33
+ */
34
+ public static $meta_key = 'tribe-dismiss-notice';
35
+
36
+ /**
37
+ * Stores all the Notices and it's configurations
38
+ *
39
+ * @var array
40
+ */
41
+ private $notices = array();
42
+
43
+ /**
44
+ * Register the Methods in the correct places
45
+ */
46
+ private function __construct() {
47
+ // Not in the admin we don't even care
48
+ if ( ! is_admin() ) {
49
+ return;
50
+ }
51
+
52
+ // Before we bail on the
53
+ add_action( 'wp_ajax_tribe_notice_dismiss', array( $this, 'maybe_dismiss' ) );
54
+
55
+ // Doing AJAX? bail.
56
+ if ( Tribe__Main::instance()->doing_ajax() ) {
57
+ return;
58
+ }
59
+
60
+ // Hook the actual rendering of notices
61
+ add_action( 'current_screen', array( $this, 'hook' ), 20 );
62
+
63
+ // Add our notice dismissal script
64
+ tribe_asset(
65
+ Tribe__Main::instance(),
66
+ 'tribe-notice-dismiss',
67
+ 'notice-dismiss.js',
68
+ array( 'jquery' ),
69
+ 'admin_enqueue_scripts'
70
+ );
71
+ }
72
+
73
+ /**
74
+ * This will happen on the `current_screen` and will hook to the correct actions and display the notices
75
+ *
76
+ * @return void
77
+ */
78
+ public function hook() {
79
+ foreach ( $this->notices as $notice ) {
80
+ if ( $notice->dismiss && $this->has_user_dimissed( $notice->slug ) ) {
81
+ continue;
82
+ }
83
+
84
+ add_action( $notice->action, $notice->callback, $notice->priority );
85
+ }
86
+ }
87
+
88
+ /**
89
+ * This will allow the user to Dimiss the Notice using JS.
90
+ *
91
+ * We will dismiss the notice without checking to see if the slug was already
92
+ * registered (via a call to exists()) for the reason that, during a dismiss
93
+ * ajax request, some valid notices may not have been registered yet.
94
+ *
95
+ * @return void
96
+ */
97
+ public function maybe_dismiss() {
98
+ if ( empty( $_GET[ self::$meta_key ] ) ) {
99
+ wp_send_json( false );
100
+ }
101
+
102
+ $slug = sanitize_title_with_dashes( $_GET[ self::$meta_key ] );
103
+
104
+ // Send a JSON answer with the status of dimissal
105
+ wp_send_json( $this->dismiss( $slug ) );
106
+ }
107
+
108
+ /**
109
+ * Allows a Magic to remove the Requirement of creating a callback
110
+ *
111
+ * @param string $name Name of the Method used to create the Slug of the Notice
112
+ * @param array $arguments Which arguments were used, normally empty
113
+ *
114
+ * @return string
115
+ */
116
+ public function __call( $name, $arguments ) {
117
+ // Transform from Method name to Notice number
118
+ $slug = preg_replace( '/render_/', '', $name, 1 );
119
+
120
+ if ( ! $this->exists( $slug ) ) {
121
+ return false;
122
+ }
123
+
124
+ $notice = $this->get( $slug );
125
+
126
+ // Return the rendered HTML
127
+ return $this->render( $slug, $notice->content );
128
+ }
129
+
130
+ /**
131
+ * This is a helper to actually print the Message
132
+ *
133
+ * @param string $slug The Name of the Notice
134
+ * @param string $content The content of the notice
135
+ * @param boolean $return Echo or return the content
136
+ *
137
+ * @return boolean|string
138
+ */
139
+ public function render( $slug, $content = null, $return = false ) {
140
+ $notice = $this->get( $slug );
141
+
142
+ $classes = array( 'tribe-dismiss-notice', 'notice' );
143
+ $classes[] = sanitize_html_class( 'notice-' . $notice->type );
144
+ $classes[] = sanitize_html_class( 'tribe-notice-' . $notice->slug );
145
+
146
+ if ( $notice->dismiss ) {
147
+ $classes[] = 'is-dismissible';
148
+ }
149
+
150
+ $html = sprintf( '<div class="%s" data-ref="%s">%s</div>', implode( ' ', $classes ), $notice->slug, $content );
151
+
152
+ if ( ! $return ) {
153
+ echo $html;
154
+ }
155
+
156
+ return $html;
157
+ }
158
+
159
+ /**
160
+ * Checks if a given user has dimissed a given notice.
161
+ *
162
+ * @param string $slug The Name of the Notice
163
+ * @param int|null $user_id The user ID
164
+ *
165
+ * @return boolean
166
+ */
167
+ public function has_user_dimissed( $slug, $user_id = null ) {
168
+ if ( is_null( $user_id ) ) {
169
+ $user_id = get_current_user_id();
170
+ }
171
+
172
+ $dismissed_notices = get_user_meta( $user_id, self::$meta_key );
173
+
174
+ if ( ! is_array( $dismissed_notices ) ) {
175
+ return false;
176
+ }
177
+
178
+ if ( ! in_array( $slug, $dismissed_notices ) ) {
179
+ return false;
180
+ }
181
+
182
+ return true;
183
+ }
184
+
185
+ /**
186
+ * A Method to actually add the Meta value telling that this notice has been dismissed
187
+ *
188
+ * @param string $slug The Name of the Notice
189
+ * @param int|null $user_id The user ID
190
+ *
191
+ * @return boolean
192
+ */
193
+ public function dismiss( $slug, $user_id = null ) {
194
+ if ( is_null( $user_id ) ) {
195
+ $user_id = get_current_user_id();
196
+ }
197
+
198
+ // If this user has dimissed we don't care either
199
+ if ( $this->has_user_dimissed( $slug, $user_id ) ) {
200
+ return true;
201
+ }
202
+
203
+ return add_user_meta( $user_id, self::$meta_key, $slug, false );
204
+ }
205
+
206
+ /**
207
+ * Removes the User meta holding if a notice was dimissed
208
+ *
209
+ * @param string $slug The Name of the Notice
210
+ * @param int|null $user_id The user ID
211
+ *
212
+ * @return boolean
213
+ */
214
+ public function undismiss( $slug, $user_id = null ) {
215
+ if ( is_null( $user_id ) ) {
216
+ $user_id = get_current_user_id();
217
+ }
218
+
219
+ // If this user has dimissed we don't care either
220
+ if ( ! $this->has_user_dimissed( $slug, $user_id ) ) {
221
+ return false;
222
+ }
223
+
224
+ return delete_user_meta( $user_id, self::$meta_key, $slug );
225
+ }
226
+
227
+ /**
228
+ * Undismisses the specified notice for all users.
229
+ *
230
+ * @param string $slug
231
+ *
232
+ * @return int
233
+ */
234
+ public function undismiss_for_all( $slug ) {
235
+ $user_query = new WP_User_Query( array(
236
+ 'meta_key' => self::$meta_key,
237
+ 'meta_value' => $slug,
238
+ ) );
239
+
240
+ $affected = 0;
241
+
242
+ foreach ( $user_query->get_results() as $user ) {
243
+ if ( $this->undismiss( $slug, $user->ID ) ) {
244
+ $affected++;
245
+ }
246
+ }
247
+
248
+ return $affected;
249
+ }
250
+
251
+ /**
252
+ * Register a Notice and attach a callback to the required action to display it correctly
253
+ *
254
+ * @param string $slug Slug to save the notice
255
+ * @param callable|string $callback A callable Method/Fuction to actually display the notice
256
+ * @param array $arguments Arguments to Setup a notice
257
+ *
258
+ * @return stdClass
259
+ */
260
+ public function register( $slug, $callback, $arguments = array() ) {
261
+ // Prevent weird stuff here
262
+ $slug = sanitize_title_with_dashes( $slug );
263
+
264
+ $defaults = array(
265
+ 'callback' => null,
266
+ 'content' => null,
267
+ 'action' => 'admin_notices',
268
+ 'priority' => 10,
269
+ 'expire' => false,
270
+ 'dismiss' => false,
271
+ 'type' => 'error',
272
+ );
273
+
274
+ if ( is_callable( $callback ) ) {
275
+ $defaults['callback'] = $callback;
276
+ } else {
277
+ $defaults['callback'] = array( $this, 'render_' . $slug );
278
+ $defaults['content'] = $callback;
279
+ }
280
+
281
+ // Merge Arguments
282
+ $notice = (object) wp_parse_args( $arguments, $defaults );
283
+
284
+ // Enforce this one
285
+ $notice->slug = $slug;
286
+
287
+ // Clean these
288
+ $notice->priority = absint( $notice->priority );
289
+ $notice->expire = (bool) $notice->expire;
290
+ $notice->dismiss = (bool) $notice->dismiss;
291
+
292
+ // Set the Notice on the array of notices
293
+ $this->notices[ $slug ] = $notice;
294
+
295
+ // Return the notice Object because it might be modified
296
+ return $notice;
297
+ }
298
+
299
+ public function remove( $slug ) {
300
+ if ( ! $this->exists( $slug ) ) {
301
+ return false;
302
+ }
303
+
304
+ unset( $this->notices[ $slug ] );
305
+ return true;
306
+ }
307
+
308
+ public function get( $slug = null ) {
309
+ // Prevent weird stuff here
310
+ $slug = sanitize_title_with_dashes( $slug );
311
+
312
+ if ( is_null( $slug ) ) {
313
+ return $this->notices;
314
+ }
315
+
316
+ if ( ! empty( $this->notices[ $slug ] ) ) {
317
+ return $this->notices[ $slug ];
318
+ }
319
+
320
+ return null;
321
+ }
322
+
323
+ public function exists( $slug ) {
324
+ return is_object( $this->get( $slug ) ) ? true : false;
325
+ }
326
+ }
common/src/Tribe/App_Shop.php CHANGED
@@ -80,14 +80,15 @@ if ( ! class_exists( 'Tribe__App_Shop' ) ) {
80
  * Enqueue the styles and script
81
  */
82
  public function enqueue() {
83
- wp_enqueue_style( 'app-shop', tribe_resource_url( 'app-shop.css', false, 'common' ), array(), apply_filters( 'tribe_events_css_version', Tribe__Main::VERSION ) );
84
- wp_enqueue_script( 'app-shop', tribe_resource_url( 'app-shop.js', false, 'common' ), array(), apply_filters( 'tribe_events_js_version', Tribe__Main::VERSION ) );
85
  }
86
 
87
  /**
88
  * Renders the Shop App page
89
  */
90
  public function do_menu_page() {
 
91
  $products = $this->get_all_products();
92
  include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/app-shop.php';
93
  }
@@ -100,10 +101,10 @@ if ( ! class_exists( 'Tribe__App_Shop' ) ) {
100
  private function get_all_products() {
101
  $products = array(
102
  (object) array(
103
- 'title' => __( 'Filter Bar', 'tribe-common' ),
104
- 'link' => 'https://theeventscalendar.com/product/wordpress-events-filterbar/?utm_campaign=in-app&utm_source=addonspage&utm_medium=wordpress-events-filterbar&utm_content=appstoreembedded-1',
105
- 'description' => __( 'It is awesome that your calendar is <em>THE PLACE</em> to get hooked up with prime choice ways to spend time. You have more events than Jabba the Hutt has rolls. Too bad visitors are hiring a personal assistant to go through all the choices. Ever wish you could just filter the calendar to only show events in walking distance, on a weekend, that are free? BOOM. Now you can. Introducing… the Filter Bar.', 'tribe-common' ),
106
- 'image' => 'images/app-shop-filter-bar.jpg',
107
  ),
108
  (object) array(
109
  'title' => __( 'Events Calendar PRO', 'tribe-common' ),
@@ -115,6 +116,22 @@ if ( ! class_exists( 'Tribe__App_Shop' ) ) {
115
  ),
116
  'image' => 'images/app-shop-pro.jpg',
117
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  (object) array(
119
  'title' => __( 'Community Events', 'tribe-common' ),
120
  'link' => 'https://theeventscalendar.com/product/wordpress-community-events/?utm_campaign=in-app&utm_source=addonspage&utm_medium=wordpress-community-events&utm_content=appstoreembedded-1',
@@ -128,16 +145,6 @@ if ( ! class_exists( 'Tribe__App_Shop' ) ) {
128
  'requires' => _x( 'Event Tickets Plus and Community Events', 'Names of required plugins for Community Tickets', 'tribe-common' ),
129
  'image' => 'images/app-shop-community-tickets.jpg',
130
  ),
131
- (object) array(
132
- 'title' => __( 'Event Tickets Plus', 'tribe-common' ),
133
- 'link' => 'https://theeventscalendar.com/product/wordpress-event-tickets-plus/?utm_campaign=in-app&utm_source=addonspage&utm_medium=wordpress-event-tickets-plus&utm_content=appstoreembedded-1',
134
- 'description' => sprintf(
135
- __( 'Event Tickets Plus allows you to sell tickets to your events using WooCommerce, Shopp, WP eCommerce, or Easy Digital Downloads. Use it on your posts and pages, or add %1$sThe Events Calendar%2$s and sell tickets from your events listings.', 'tribe-common' ),
136
- '<a href="http://m.tri.be/18vc">',
137
- '</a>'
138
- ),
139
- 'image' => 'images/app-shop-tickets-plus.jpg',
140
- ),
141
  (object) array(
142
  'title' => __( 'Eventbrite Tickets', 'tribe-common' ),
143
  'link' => 'https://theeventscalendar.com/product/wordpress-eventbrite-tickets/?utm_campaign=in-app&utm_source=addonspage&utm_medium=wordpress-eventbrite-tickets&utm_content=appstoreembedded-1',
@@ -148,18 +155,6 @@ if ( ! class_exists( 'Tribe__App_Shop' ) ) {
148
  ),
149
  'image' => 'images/app-shop-eventbrite.jpg',
150
  ),
151
- (object) array(
152
- 'title' => __( 'Facebook Events', 'tribe-common' ),
153
- 'link' => 'https://theeventscalendar.com/product/facebook-events/?utm_campaign=in-app&utm_source=addonspage&utm_medium=facebook-events&utm_content=appstoreembedded-1',
154
- 'description' => __( 'With the Facebook Events add-on, imported events are manually or automagically created as entries in The Events Calendar. Basic event data along with venue and organizer are populated appropriately. No more entering information in two places, or having to recreate someone else\'s listing for a public event you want to include on your WordPress calendar.', 'tribe-common' ),
155
- 'image' => 'images/app-shop-facebook.jpg',
156
- ),
157
- (object) array(
158
- 'title' => __( 'iCal Importer', 'tribe-common' ),
159
- 'link' => 'https://theeventscalendar.com/product/ical-importer/?utm_campaign=in-app&utm_source=addonspage&utm_medium=ical-importer&utm_content=appstoreembedded-1',
160
- 'description' => __( 'The iCal Importer helps you keep your events calendar full of interesting events! You can import events from any website that publishes an iCal (aka ICS) feed and add them to your listings. The recurring import feature lets you keep your calendar brimming without manual oversight (though you can review every imported event if you like). Add filtering by keyword or geographic region and you can be sure that the kinds of events you get are the kinds you want.', 'tribe-common' ),
161
- 'image' => 'images/app-shop-ical.jpg',
162
- ),
163
  );
164
 
165
  return $products;
80
  * Enqueue the styles and script
81
  */
82
  public function enqueue() {
83
+ wp_enqueue_style( 'app-shop', tribe_resource_url( 'app-shop.css', false, null, Tribe__Main::instance() ), array(), apply_filters( 'tribe_events_css_version', Tribe__Main::VERSION ) );
84
+ wp_enqueue_script( 'app-shop', tribe_resource_url( 'app-shop.js', false, null, Tribe__Main::instance() ), array(), apply_filters( 'tribe_events_js_version', Tribe__Main::VERSION ) );
85
  }
86
 
87
  /**
88
  * Renders the Shop App page
89
  */
90
  public function do_menu_page() {
91
+ $main = Tribe__Main::instance();
92
  $products = $this->get_all_products();
93
  include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/app-shop.php';
94
  }
101
  private function get_all_products() {
102
  $products = array(
103
  (object) array(
104
+ 'title' => __( 'Event Aggregator', 'tribe-common' ),
105
+ 'link' => 'https://theeventscalendar.com/product/event-aggregator/?utm_campaign=in-app&utm_source=addonspage&utm_medium=event-aggregator&utm_content=appstoreembedded-1',
106
+ 'description' => __( 'Importing events from multiple sources has never been easier! Event Aggregator helps you curate and manage event import feeds from Facebook, Meetup, Google Calendar, iCalendar, CSV, and ICS. Schedule automatic imports or manually import events when you’re ready. Event Aggregator provides a convenient dashboard to manage bulk imports, filters, one-way sync, import history, and more.', 'tribe-common' ),
107
+ 'image' => 'images/app-shop-ical.jpg',
108
  ),
109
  (object) array(
110
  'title' => __( 'Events Calendar PRO', 'tribe-common' ),
116
  ),
117
  'image' => 'images/app-shop-pro.jpg',
118
  ),
119
+ (object) array(
120
+ 'title' => __( 'Event Tickets Plus', 'tribe-common' ),
121
+ 'link' => 'https://theeventscalendar.com/product/wordpress-event-tickets-plus/?utm_campaign=in-app&utm_source=addonspage&utm_medium=wordpress-event-tickets-plus&utm_content=appstoreembedded-1',
122
+ 'description' => sprintf(
123
+ __( 'Event Tickets Plus allows you to sell tickets to your events using WooCommerce, Shopp, WP eCommerce, or Easy Digital Downloads. Use it on your posts and pages, or add %1$sThe Events Calendar%2$s and sell tickets from your events listings.', 'tribe-common' ),
124
+ '<a href="http://m.tri.be/18vc">',
125
+ '</a>'
126
+ ),
127
+ 'image' => 'images/app-shop-tickets-plus.jpg',
128
+ ),
129
+ (object) array(
130
+ 'title' => __( 'Filter Bar', 'tribe-common' ),
131
+ 'link' => 'https://theeventscalendar.com/product/wordpress-events-filterbar/?utm_campaign=in-app&utm_source=addonspage&utm_medium=wordpress-events-filterbar&utm_content=appstoreembedded-1',
132
+ 'description' => __( 'It is awesome that your calendar is <em>THE PLACE</em> to get hooked up with prime choice ways to spend time. You have more events than Jabba the Hutt has rolls. Too bad visitors are hiring a personal assistant to go through all the choices. Ever wish you could just filter the calendar to only show events in walking distance, on a weekend, that are free? BOOM. Now you can. Introducing… the Filter Bar.', 'tribe-common' ),
133
+ 'image' => 'images/app-shop-filter-bar.jpg',
134
+ ),
135
  (object) array(
136
  'title' => __( 'Community Events', 'tribe-common' ),
137
  'link' => 'https://theeventscalendar.com/product/wordpress-community-events/?utm_campaign=in-app&utm_source=addonspage&utm_medium=wordpress-community-events&utm_content=appstoreembedded-1',
145
  'requires' => _x( 'Event Tickets Plus and Community Events', 'Names of required plugins for Community Tickets', 'tribe-common' ),
146
  'image' => 'images/app-shop-community-tickets.jpg',
147
  ),
 
 
 
 
 
 
 
 
 
 
148
  (object) array(
149
  'title' => __( 'Eventbrite Tickets', 'tribe-common' ),
150
  'link' => 'https://theeventscalendar.com/product/wordpress-eventbrite-tickets/?utm_campaign=in-app&utm_source=addonspage&utm_medium=wordpress-eventbrite-tickets&utm_content=appstoreembedded-1',
155
  ),
156
  'image' => 'images/app-shop-eventbrite.jpg',
157
  ),
 
 
 
 
 
 
 
 
 
 
 
 
158
  );
159
 
160
  return $products;
common/src/Tribe/Asset/Factory.php CHANGED
@@ -1,5 +1,4 @@
1
  <?php
2
-
3
  class Tribe__Asset__Factory {
4
  /**
5
  * @param string $name
1
  <?php
 
2
  class Tribe__Asset__Factory {
3
  /**
4
  * @param string $name
common/src/Tribe/Assets.php ADDED
@@ -0,0 +1,405 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Don't load directly
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ die( '-1' );
5
+ }
6
+
7
+ /**
8
+ * Class used to register and enqueue assets across our plugins
9
+ */
10
+ class Tribe__Assets {
11
+ /**
12
+ * Static Singleton Holder
13
+ *
14
+ * @var self|null
15
+ */
16
+ protected static $instance;
17
+
18
+ /**
19
+ * Stores all the Assets and it's configurations
20
+ *
21
+ * @var array
22
+ */
23
+ private $assets = array();
24
+
25
+ /**
26
+ * Static Singleton Factory Method
27
+ *
28
+ * @return self
29
+ */
30
+ public static function instance() {
31
+ if ( empty( self::$instance ) ) {
32
+ self::$instance = new self;
33
+ }
34
+
35
+ return self::$instance;
36
+ }
37
+
38
+ /**
39
+ * Register the Methods in the correct places
40
+ */
41
+ private function __construct() {
42
+ // Hook the actual registering of
43
+ add_action( 'init', array( $this, 'register_in_wp' ), 1, 0 );
44
+ }
45
+
46
+ /**
47
+ * Register the Assets on the correct hooks
48
+ *
49
+ * @return void
50
+ */
51
+ public function register_in_wp( $assets = null ) {
52
+ if ( is_null( $assets ) ) {
53
+ $assets = $this->assets;
54
+ }
55
+
56
+ if ( ! is_array( $assets ) ) {
57
+ $assets = array( $assets );
58
+ }
59
+
60
+ foreach ( $assets as $asset ) {
61
+ if ( 'js' === $asset->type ) {
62
+ wp_register_script( $asset->slug, $asset->url, $asset->deps, $asset->version, $asset->in_footer );
63
+ } else {
64
+ wp_register_style( $asset->slug, $asset->url, $asset->deps, $asset->version, $asset->media );
65
+ }
66
+
67
+ // Register that this asset is actually registered on the WP methods
68
+ $asset->is_registered = true;
69
+
70
+ // If we don't have an action we don't even register the action to enqueue
71
+ if ( ! is_string( $asset->action ) ) {
72
+ continue;
73
+ }
74
+
75
+ // Now add an action to enqueue the registered assets
76
+ add_action( $asset->action, array( $this, 'enqueue' ), $asset->priority );
77
+ }
78
+ }
79
+
80
+ public function enqueue() {
81
+ foreach ( $this->assets as $asset ) {
82
+ // Skip if we are not on the correct filter
83
+ if ( current_filter() !== $asset->action ) {
84
+ continue;
85
+ }
86
+
87
+ // If any single conditional returns true, then we need to enqueue the asset
88
+ if ( ! is_string( $asset->action ) ) {
89
+ continue;
90
+ }
91
+
92
+ // If this asset was late called
93
+ if ( ! $asset->is_registered ) {
94
+ $this->register_in_wp( $asset );
95
+ }
96
+
97
+ // Default to enqueuing the asset if there are no conditionals,
98
+ // and default to not enqueuing it if there *are* conditionals
99
+ $enqueue = empty( $asset->conditionals );
100
+
101
+ // If we have a set of conditionals we loop on then and get if they are true
102
+ foreach ( $asset->conditionals as $conditional ) {
103
+ $enqueue = call_user_func( $conditional );
104
+ if ( $enqueue ) {
105
+ break;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Allows developers to hook-in and prevent an asset from been loaded
111
+ *
112
+ * @param bool $enqueue If we should enqueue or not a given asset
113
+ * @param object $asset Which asset we are dealing with
114
+ */
115
+ $enqueue = apply_filters( 'tribe_asset_enqueue', $enqueue, $asset );
116
+
117
+ /**
118
+ * Allows developers to hook-in and prevent an asset from been loaded
119
+ *
120
+ * Note: When you pass callables on the `$asset->filter` argument this will be hooked here
121
+ *
122
+ * @param bool $enqueue If we should enqueue or not a given asset
123
+ * @param object $asset Which asset we are dealing with
124
+ */
125
+ $enqueue = apply_filters( 'tribe_asset_enqueue_' . $asset->slug, $enqueue, $asset );
126
+
127
+ if ( ! $enqueue ) {
128
+ continue;
129
+ }
130
+
131
+ if ( 'js' === $asset->type ) {
132
+ wp_enqueue_script( $asset->slug );
133
+
134
+ // Only localize on JS and if we have data
135
+ if ( ! empty( $asset->localize ) ) {
136
+ wp_localize_script( $asset->slug, $asset->localize->name, $asset->localize->data );
137
+ }
138
+ } else {
139
+ wp_enqueue_style( $asset->slug );
140
+ }
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Returns the path to a minified version of a js or css file, if it exists.
146
+ * If the file does not exist, returns false.
147
+ *
148
+ * @param string $url The path or URL to the un-minified file.
149
+ *
150
+ * @return string|false The path/url to minified version or false, if file not found.
151
+ */
152
+ public static function maybe_get_min_file( $url ) {
153
+ $urls = array();
154
+ // If need add the Min Files
155
+ if ( ! defined( 'SCRIPT_DEBUG' ) || SCRIPT_DEBUG === false ) {
156
+ if ( substr( $url, - 3, 3 ) === '.js' ) {
157
+ $urls[] = substr_replace( $url, '.min', - 3, 0 );
158
+ }
159
+
160
+ if ( substr( $url, - 4, 4 ) === '.css' ) {
161
+ $urls[] = substr_replace( $url, '.min', - 4, 0 );
162
+ }
163
+ }
164
+
165
+ // Add the actual url after having the Min file added
166
+ $urls[] = $url;
167
+
168
+ // Check for all Urls added to the array
169
+ foreach ( $urls as $key => $url ) {
170
+ //set path to file for Windows
171
+ $file = $url;
172
+ //Set variable for content normalized directory
173
+ $normalized_content_dir = wp_normalize_path( WP_CONTENT_DIR );
174
+
175
+ //Detect if $url is actually a file path
176
+ if ( false !== strpos( $url, $normalized_content_dir ) ) {
177
+ // Turn file Path to URL in Windows
178
+ $url = str_replace( $normalized_content_dir, WP_CONTENT_URL, $url );
179
+ } else {
180
+ // Turn URL into file Path
181
+ $file = str_replace( WP_CONTENT_URL, $normalized_content_dir, $url );
182
+ }
183
+
184
+ //if file exists return url
185
+ if ( file_exists( $file ) ) {
186
+ return $url;
187
+ }
188
+ }
189
+
190
+ // If we don't have any real file return false
191
+ return false;
192
+ }
193
+
194
+ /**
195
+ * Register an Asset and attach a callback to the required action to display it correctly
196
+ *
197
+ * @param object $origin The main Object for the plugin you are enqueueing the script/style for
198
+ * @param string $slug Slug to save the asset
199
+ * @param string $file Which file will be loaded, either CSS or JS
200
+ * @param array $deps Dependencies
201
+ * @param string|null $action (Optional) A WordPress Action, if set needs to happen after: `wp_enqueue_scripts`, `admin_enqueue_scripts`, or `login_enqueue_scripts`
202
+ * @param string|array $query {
203
+ * Optional. Array or string of parameters for this asset
204
+ *
205
+ * @type string|null $action Which WordPress action this asset will be loaded on
206
+ * @type int $priority Priority in which this asset will be loaded on the WordPress action
207
+ * @type string $file The relative path to the File that will be enqueued, uses the $origin to get the full path
208
+ * @type string $type Asset Type, `js` or `css`
209
+ * @type array $deps An array of other asset as dependencies
210
+ * @type string $version Version number, used for cache expiring
211
+ * @type string $media Used only for CSS, when to load the file
212
+ * @type bool $in_footer A boolean determining if the javascript should be loaded on the footer
213
+ * @type array|object $localize Variables needed on the JavaScript side {
214
+ * @type string $name Name of the JS variable
215
+ * @type string|array $data Contents of the JS variable
216
+ * }
217
+ * @type callable[] $conditionals An callable method or an array of them, that will determine if the asset is loaded or not
218
+ * }
219
+ *
220
+ * @return string
221
+ */
222
+ public function register( $origin, $slug, $file, $deps = array(), $action = null, $arguments = array() ) {
223
+ // Prevent weird stuff here
224
+ $slug = sanitize_title_with_dashes( $slug );
225
+
226
+ if ( $this->exists( $slug ) ) {
227
+ return $this->get( $slug );
228
+ }
229
+
230
+ if ( is_string( $origin ) ) {
231
+ // Origin needs to be a class with a `instance` method and a Version constant
232
+ if ( class_exists( $origin ) && method_exists( $origin, 'instance' ) && defined( $origin . '::VERSION' ) ) {
233
+ $origin = call_user_func( array( $origin, 'instance' ) );
234
+ }
235
+ }
236
+
237
+ if ( is_object( $origin ) ) {
238
+ $origin_name = get_class( $origin );
239
+
240
+ if ( ! defined( $origin_name . '::VERSION' ) ) {
241
+ // If we have a Object and we don't have instance or version
242
+ return false;
243
+ }
244
+ } else {
245
+ return false;
246
+ }
247
+
248
+ // Fetches the version on the Origin Version constant
249
+ $version = constant( $origin_name . '::VERSION' );
250
+
251
+ // Default variables to prevent notices
252
+ $defaults = array(
253
+ 'action' => null,
254
+ 'priority' => 10,
255
+ 'file' => false,
256
+ 'type' => null,
257
+ 'deps' => array(),
258
+ 'version' => $version,
259
+ 'media' => 'all',
260
+ 'in_footer' => true,
261
+ 'localize' => array(),
262
+ 'conditionals' => array(),
263
+ 'is_registered' => false,
264
+ );
265
+
266
+ // Merge Arguments
267
+ $asset = (object) wp_parse_args( $arguments, $defaults );
268
+
269
+ // Enforce these one
270
+ $asset->slug = $slug;
271
+ $asset->file = $file;
272
+ $asset->deps = $deps;
273
+ $asset->origin = $origin;
274
+ $asset->origin_name = $origin_name;
275
+ $asset->action = $action;
276
+
277
+ // If we don't have a type on the arguments we grab from the File path
278
+ if ( is_null( $asset->type ) ) {
279
+ if ( substr( $asset->file, -3, 3 ) === '.js' ) {
280
+ $asset->type = 'js';
281
+ } elseif ( substr( $asset->file, -4, 4 ) === '.css' ) {
282
+ $asset->type = 'css';
283
+ }
284
+ }
285
+
286
+ // If asset type is wrong don't register
287
+ if ( ! in_array( $asset->type, array( 'js', 'css' ) ) ) {
288
+ return false;
289
+ }
290
+
291
+ /**
292
+ * Deprecated filter to allow changing version based on the type of Asset
293
+ *
294
+ * @todo remove on 4.6
295
+ * @deprecated 4.3
296
+ *
297
+ * @param string $version
298
+ */
299
+ $asset->version = apply_filters( "tribe_events_{$asset->type}_version", $asset->version );
300
+
301
+ /**
302
+ * Filter to change version number on assets
303
+ *
304
+ * @param string $version
305
+ * @param object $asset
306
+ */
307
+ $asset->version = apply_filters( 'tribe_asset_version', $asset->version, $asset );
308
+
309
+ // Clean these
310
+ $asset->priority = absint( $asset->priority );
311
+ $asset->in_footer = (bool) $asset->in_footer;
312
+ $asset->media = esc_attr( $asset->media );
313
+
314
+ // Ensures that we have a priority over 1
315
+ if ( $asset->priority < 1 ) {
316
+ $asset->priority = 1;
317
+ }
318
+
319
+ $is_vendor = strpos( $asset->file, 'vendor/' ) !== false ? true : false;
320
+
321
+ // Setup the actual URL
322
+ if ( filter_var( $asset->file, FILTER_VALIDATE_URL ) ) {
323
+ $asset->url = $asset->file;
324
+ } else {
325
+ $asset->url = $this->maybe_get_min_file( tribe_resource_url( $asset->file, false, ( $is_vendor ? '' : null ), $asset->origin ) );
326
+ }
327
+
328
+ // If you are passing localize, you need `name` and `data`
329
+ if ( ! empty( $asset->localize ) && ( is_array( $asset->localize ) || is_object( $asset->localize ) ) ) {
330
+ $asset->localize = (object) $asset->localize;
331
+
332
+ // if we don't have both reset localize
333
+ if ( ! isset( $asset->localize->data, $asset->localize->name ) ) {
334
+ $asset->localize = array();
335
+ }
336
+ }
337
+
338
+ // Looks for a single conditional callable and places it in an Array
339
+ if ( ! empty( $asset->conditionals ) && is_callable( $asset->conditionals ) ) {
340
+ $asset->conditionals = array( $asset->conditionals );
341
+ }
342
+
343
+ /**
344
+ * Filter an Asset loading variables
345
+ *
346
+ * @param object $asset
347
+ */
348
+ $asset = apply_filters( 'tribe_asset_pre_register', $asset );
349
+
350
+ // Set the Asset on the array of notices
351
+ $this->assets[ $slug ] = $asset;
352
+
353
+ // Return the Slug because it might be modified
354
+ return $asset;
355
+ }
356
+
357
+ /**
358
+ * Removes an Asset from been registered and enqueue
359
+ *
360
+ * @param string $slug Slug of the Asset
361
+ *
362
+ * @return bool
363
+ */
364
+ public function remove( $slug ) {
365
+ if ( ! $this->exists( $slug ) ) {
366
+ return false;
367
+ }
368
+
369
+ unset( $this->assets[ $slug ] );
370
+ return true;
371
+ }
372
+
373
+ /**
374
+ * Get the Asset Object configuration
375
+ *
376
+ * @param string $slug Slug of the Asset
377
+ *
378
+ * @return bool
379
+ */
380
+ public function get( $slug = null ) {
381
+ // Prevent weird stuff here
382
+ $slug = sanitize_title_with_dashes( $slug );
383
+
384
+ if ( is_null( $slug ) ) {
385
+ return $this->assets;
386
+ }
387
+
388
+ if ( ! empty( $this->assets[ $slug ] ) ) {
389
+ return $this->assets[ $slug ];
390
+ }
391
+
392
+ return null;
393
+ }
394
+
395
+ /**
396
+ * Checks if an Asset exists
397
+ *
398
+ * @param string $slug Slug of the Asset
399
+ *
400
+ * @return bool
401
+ */
402
+ public function exists( $slug ) {
403
+ return is_object( $this->get( $slug ) ) ? true : false;
404
+ }
405
+ }
common/src/Tribe/Date_Utils.php CHANGED
@@ -134,16 +134,16 @@ if ( ! class_exists( 'Tribe__Date_Utils' ) ) {
134
  $dt = array();
135
 
136
  // Now try to match it
137
- if ( preg_match( '#^' . $regex . '$#', $date, $dt ) ){
138
  // Remove unwanted Indexes
139
- foreach ( $dt as $k => $v ){
140
- if ( is_int( $k ) ){
141
  unset( $dt[ $k ] );
142
  }
143
  }
144
 
145
  // We need at least Month + Day + Year to work with
146
- if ( ! checkdate( $dt['month'], $dt['day'], $dt['year'] ) ){
147
  return false;
148
  }
149
  } else {
@@ -181,14 +181,15 @@ if ( ! class_exists( 'Tribe__Date_Utils' ) ) {
181
  }
182
 
183
  /**
184
- * Returns the date only.
185
  *
186
  * @param string $date The date.
187
  *
188
  * @return string The time only in DB format.
189
  */
190
  public static function time_only( $date ) {
191
- return date( self::DBTIMEFORMAT, strtotime( $date ) );
 
192
  }
193
 
194
  /**
@@ -199,7 +200,8 @@ if ( ! class_exists( 'Tribe__Date_Utils' ) ) {
199
  * @return string The hour only.
200
  */
201
  public static function hour_only( $date ) {
202
- return date( self::HOURFORMAT, strtotime( $date ) );
 
203
  }
204
 
205
  /**
@@ -210,7 +212,8 @@ if ( ! class_exists( 'Tribe__Date_Utils' ) ) {
210
  * @return string The minute only.
211
  */
212
  public static function minutes_only( $date ) {
213
- return date( self::MINUTEFORMAT, strtotime( $date ) );
 
214
  }
215
 
216
  /**
@@ -221,7 +224,8 @@ if ( ! class_exists( 'Tribe__Date_Utils' ) ) {
221
  * @return string The meridian only in DB format.
222
  */
223
  public static function meridian_only( $date ) {
224
- return date( self::MERIDIANFORMAT, strtotime( $date ) );
 
225
  }
226
 
227
  /**
134
  $dt = array();
135
 
136
  // Now try to match it
137
+ if ( preg_match( '#^' . $regex . '$#', $date, $dt ) ) {
138
  // Remove unwanted Indexes
139
+ foreach ( $dt as $k => $v ) {
140
+ if ( is_int( $k ) ) {
141
  unset( $dt[ $k ] );
142
  }
143
  }
144
 
145
  // We need at least Month + Day + Year to work with
146
+ if ( ! checkdate( $dt['month'], $dt['day'], $dt['year'] ) ) {
147
  return false;
148
  }
149
  } else {
181
  }
182
 
183
  /**
184
+ * Returns the time only.
185
  *
186
  * @param string $date The date.
187
  *
188
  * @return string The time only in DB format.
189
  */
190
  public static function time_only( $date ) {
191
+ $date = is_numeric( $date ) ? $date : strtotime( $date );
192
+ return date( self::DBTIMEFORMAT, $date );
193
  }
194
 
195
  /**
200
  * @return string The hour only.
201
  */
202
  public static function hour_only( $date ) {
203
+ $date = is_numeric( $date ) ? $date : strtotime( $date );
204
+ return date( self::HOURFORMAT, $date );
205
  }
206
 
207
  /**
212
  * @return string The minute only.
213
  */
214
  public static function minutes_only( $date ) {
215
+ $date = is_numeric( $date ) ? $date : strtotime( $date );
216
+ return date( self::MINUTEFORMAT, $date );
217
  }
218
 
219
  /**
224
  * @return string The meridian only in DB format.
225
  */
226
  public static function meridian_only( $date ) {
227
+ $date = is_numeric( $date ) ? $date : strtotime( $date );
228
+ return date( self::MERIDIANFORMAT, $date );
229
  }
230
 
231
  /**
common/src/Tribe/Error.php ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Don't load directly
3
+ defined( 'WPINC' ) or die;
4
+
5
+ class Tribe__Error {
6
+ /**
7
+ * All the Errors Registered
8
+ * @var array
9
+ */
10
+ private $items = array();
11
+
12
+ /**
13
+ * Static Singleton Holder
14
+ *
15
+ * @var self
16
+ */
17
+ private static $instance;
18
+
19
+ /**
20
+ * Static Singleton Factory Method
21
+ *
22
+ * @return self
23
+ */
24
+ public static function instance() {
25
+ if ( ! self::$instance ) {
26
+ self::$instance = new self;
27
+ }
28
+
29
+ return self::$instance;
30
+ }
31
+
32
+ /**
33
+ * Setup all the hooks and filters
34
+ *
35
+ * @return void
36
+ */
37
+ private function __construct() {
38
+ $this->register( 'unknown', esc_html__( 'An Unknown error occurred' ) );
39
+ }
40
+
41
+ /**
42
+ * Make a quickly usable method to transform code/indexes to WP_Errors
43
+ *
44
+ * @see tribe_error()
45
+ *
46
+ * @param string|array $indexes Which Error we are looking for
47
+ * @param array $context Gives the Error context
48
+ * @param array $sprintf Allows variables on the message
49
+ *
50
+ * @return WP_Error
51
+ */
52
+ public function send( $indexes, $context = array(), $sprintf = array() ) {
53
+ if ( ! $this->exists( $indexes ) ) {
54
+ $indexes = array( 'unknown' );
55
+ }
56
+
57
+ // Fetches the Errors
58
+ $messages = (array) $this->get( $indexes );
59
+ $error = new WP_Error;
60
+
61
+ foreach ( $messages as $key => $message ) {
62
+ // Allows variables when sending the message
63
+ if ( ! empty( $sprintf ) ) {
64
+ $message = vsprintf( $message, $sprintf );
65
+ }
66
+ // Add this Message to the WP_Error
67
+ $error->add( $key, $message, $context );
68
+ }
69
+
70
+ return $error;
71
+ }
72
+
73
+ /**
74
+ * Register a new error based on a Namespace
75
+ *
76
+ * @param string|array $indexes A list of the namespaces and last item should be the error name
77
+ * @param string $message What is going to be the message associate with this indexes
78
+ *
79
+ * @return boolean
80
+ */
81
+ public function register( $indexes, $message ) {
82
+ if ( is_string( $indexes ) ) {
83
+ // Each namespace should come with `:`
84
+ $indexes = (array) explode( ':', $indexes );
85
+ }
86
+
87
+ // Couldn't register the error
88
+ if ( empty( $indexes ) ) {
89
+ return false;
90
+ }
91
+
92
+ $variable = &$this->items;
93
+ $count = count( $indexes );
94
+
95
+ // Will create the Indexes based on the $slug
96
+ foreach ( $indexes as $i => $index ) {
97
+ if ( $count === $i + 1 ) {
98
+ $variable[ $index ] = $message;
99
+ } else {
100
+ $variable = &$variable[ $index ];
101
+ }
102
+ }
103
+
104
+ // Allows Chain Reactions
105
+ return true;
106
+ }
107
+
108
+ /**
109
+ * Removes an error from the items
110
+ *
111
+ * @param string|array $indexes A list of the namespaces and last item should be the error name
112
+ *
113
+ * @return boolean
114
+ */
115
+ public function remove( $indexes ) {
116
+ if ( ! $this->exists( $indexes ) ) {
117
+ return false;
118
+ }
119
+
120
+ if ( is_string( $indexes ) ) {
121
+ // Each namespace should come with `:`
122
+ $indexes = (array) explode( ':', $indexes );
123
+ }
124
+
125
+ // Ensures that we don't modify the original
126
+ $variable = &$this->items;
127
+ $count = count( $indexes );
128
+
129
+ foreach ( $indexes as $i => $index ) {
130
+ if ( $count === $i + 1 ) {
131
+ unset( $variable[ $index ] );
132
+ } else {
133
+ $variable = &$variable[ $index ];
134
+ }
135
+ }
136
+
137
+ return true;
138
+ }
139
+
140
+ /**
141
+ * Fetches the error or namespace
142
+ *
143
+ * @param string|array $indexes (optional) A list of the namespaces and last item should be the error name
144
+ *
145
+ * @return null|array|string
146
+ */
147
+ public function get( $indexes = null ) {
148
+ if ( is_null( $indexes ) ) {
149
+ return $this->items;
150
+ }
151
+
152
+ if ( is_string( $indexes ) ) {
153
+ // Each namespace should come with `:`
154
+ $indexes = (array) explode( ':', $indexes );
155
+ }
156
+
157
+ // Ensures that we don't modify the original
158
+ $variable = $this->items;
159
+ $count = count( $indexes );
160
+
161
+ foreach ( $indexes as $i => $index ) {
162
+ if ( ! isset( $variable[ $index ] ) ) {
163
+ // If we are on the last item and we don't have it set make it Null
164
+ if ( $count === $i + 1 ) {
165
+ return null;
166
+ }
167
+ continue;
168
+ }
169
+
170
+ $variable = $variable[ $index ];
171
+ }
172
+
173
+ $return = array();
174
+ $was_namespace = is_array( $variable );
175
+
176
+ /**
177
+ * @todo Allow fetching bigger groups
178
+ * Right now you can only fetch the first group of messages
179
+ * Trying to fetch Namespaces that contain other namespaces will bug
180
+ */
181
+ foreach ( (array) $variable as $key => $value ) {
182
+ $key = implode( ':', $indexes ) . ( $was_namespace ? ':' . $key : '' );
183
+ $return[ $key ] = $value;
184
+ }
185
+
186
+ return $return;
187
+ }
188
+
189
+ /**
190
+ * Checks if a given error or namespace exists
191
+ *
192
+ * @param string|array $indexes A list of the namespaces and last item should be the error name
193
+ *
194
+ * @return boolean
195
+ */
196
+ public function exists( $indexes ) {
197
+ $variable = $this->get( $indexes );
198
+ return ! empty( $variable ) || is_array( $variable ) ? true : false;
199
+ }
200
+ }
common/src/Tribe/Field.php CHANGED
@@ -68,6 +68,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
68
  'attributes' => array(),
69
  'class' => null,
70
  'label' => null,
 
71
  'tooltip' => null,
72
  'size' => 'medium',
73
  'html' => null,
@@ -81,7 +82,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
81
  'clear_after' => true,
82
  );
83
 
84
- // a list of valid field types, to prevent screwy behaviour
85
  $this->valid_field_types = array(
86
  'heading',
87
  'html',
@@ -106,6 +107,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
106
  $id = esc_attr( $id );
107
  $type = esc_attr( $args['type'] );
108
  $name = esc_attr( $args['name'] );
 
109
  $class = sanitize_html_class( $args['class'] );
110
  $label = wp_kses(
111
  $args['label'], array(
@@ -174,8 +176,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
174
  }
175
 
176
  // epicness
177
- $this->doField();
178
-
179
  }
180
 
181
  /**
@@ -185,7 +186,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
185
  *
186
  * @return void
187
  */
188
- public function doField() {
189
 
190
  if ( $this->conditional ) {
191
 
@@ -217,7 +218,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
217
  *
218
  * @return string the field start
219
  */
220
- public function doFieldStart() {
221
  $return = '<fieldset id="tribe-field-' . $this->id . '"';
222
  $return .= ' class="tribe-field tribe-field-' . $this->type;
223
  $return .= ( $this->error ) ? ' tribe-error' : '';
@@ -233,7 +234,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
233
  *
234
  * @return string the field end
235
  */
236
- public function doFieldEnd() {
237
  $return = '</fieldset>';
238
  $return .= ( $this->clear_after ) ? '<div class="clear"></div>' : '';
239
 
@@ -245,7 +246,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
245
  *
246
  * @return string the field label
247
  */
248
- public function doFieldLabel() {
249
  $return = '';
250
  if ( $this->label ) {
251
  $return = '<legend class="tribe-field-label">' . $this->label . '</legend>';
@@ -259,7 +260,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
259
  *
260
  * @return string the field div start
261
  */
262
- public function doFieldDivStart() {
263
  $return = '<div class="tribe-field-wrap">';
264
 
265
  return apply_filters( 'tribe_field_div_start', $return, $this );
@@ -270,8 +271,8 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
270
  *
271
  * @return string the field div end
272
  */
273
- public function doFieldDivEnd() {
274
- $return = $this->doToolTip();
275
  $return .= '</div>';
276
 
277
  return apply_filters( 'tribe_field_div_end', $return, $this );
@@ -282,7 +283,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
282
  *
283
  * @return string the field tooltip
284
  */
285
- public function doToolTip() {
286
  $return = '';
287
  if ( $this->tooltip ) {
288
  $return = '<p class="tooltip description">' . $this->tooltip . '</p>';
@@ -296,7 +297,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
296
  *
297
  * @return string the screen reader label
298
  */
299
- public function doScreenReaderLabel() {
300
  $return = '';
301
  if ( $this->tooltip ) {
302
  $return = '<label class="screen-reader-text">' . $this->tooltip . '</label>';
@@ -310,7 +311,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
310
  *
311
  * @return string the field value
312
  */
313
- public function doFieldValue() {
314
  $return = '';
315
  if ( $this->value ) {
316
  $return = ' value="' . $this->value . '"';
@@ -326,7 +327,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
326
  *
327
  * @return string the field name
328
  */
329
- public function doFieldName( $multi = false ) {
330
  $return = '';
331
  if ( $this->name ) {
332
  if ( $multi ) {
@@ -339,12 +340,26 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
339
  return apply_filters( 'tribe_field_name', $return, $this->name, $this );
340
  }
341
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  /**
343
  * Return a string of attributes for the field
344
  *
345
  * @return string
346
  **/
347
- public function doFieldAttributes() {
348
  $return = '';
349
  if ( ! empty( $this->attributes ) ) {
350
  foreach ( $this->attributes as $key => $value ) {
@@ -372,7 +387,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
372
  * @return string the field
373
  */
374
  public function html() {
375
- $field = $this->doFieldLabel();
376
  $field .= $this->html;
377
 
378
  return $field;
@@ -384,17 +399,18 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
384
  * @return string the field
385
  */
386
  public function text() {
387
- $field = $this->doFieldStart();
388
- $field .= $this->doFieldLabel();
389
- $field .= $this->doFieldDivStart();
390
  $field .= '<input';
391
  $field .= ' type="text"';
392
- $field .= $this->doFieldName();
393
- $field .= $this->doFieldValue();
 
394
  $field .= '/>';
395
- $field .= $this->doScreenReaderLabel();
396
- $field .= $this->doFieldDivEnd();
397
- $field .= $this->doFieldEnd();
398
 
399
  return $field;
400
  }
@@ -405,17 +421,17 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
405
  * @return string the field
406
  */
407
  public function textarea() {
408
- $field = $this->doFieldStart();
409
- $field .= $this->doFieldLabel();
410
- $field .= $this->doFieldDivStart();
411
  $field .= '<textarea';
412
- $field .= $this->doFieldName();
413
  $field .= '>';
414
  $field .= esc_html( stripslashes( $this->value ) );
415
  $field .= '</textarea>';
416
- $field .= $this->doScreenReaderLabel();
417
- $field .= $this->doFieldDivEnd();
418
- $field .= $this->doFieldEnd();
419
 
420
  return $field;
421
  }
@@ -433,13 +449,13 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
433
  ob_start();
434
  wp_editor( html_entity_decode( ( $this->value ) ), $this->name, $settings );
435
  $editor = ob_get_clean();
436
- $field = $this->doFieldStart();
437
- $field .= $this->doFieldLabel();
438
- $field .= $this->doFieldDivStart();
439
  $field .= $editor;
440
- $field .= $this->doScreenReaderLabel();
441
- $field .= $this->doFieldDivEnd();
442
- $field .= $this->doFieldEnd();
443
 
444
  return $field;
445
  }
@@ -450,14 +466,14 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
450
  * @return string the field
451
  */
452
  public function radio() {
453
- $field = $this->doFieldStart();
454
- $field .= $this->doFieldLabel();
455
- $field .= $this->doFieldDivStart();
456
  if ( is_array( $this->options ) ) {
457
  foreach ( $this->options as $option_id => $title ) {
458
  $field .= '<label title="' . esc_attr( strip_tags( $title ) ) . '">';
459
  $field .= '<input type="radio"';
460
- $field .= $this->doFieldName();
461
  $field .= ' value="' . esc_attr( $option_id ) . '" ' . checked( $this->value, $option_id, false ) . '/>';
462
  $field .= $title;
463
  $field .= '</label>';
@@ -465,8 +481,8 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
465
  } else {
466
  $field .= '<span class="tribe-error">' . esc_html__( 'No radio options specified', 'tribe-common' ) . '</span>';
467
  }
468
- $field .= $this->doFieldDivEnd();
469
- $field .= $this->doFieldEnd();
470
 
471
  return $field;
472
  }
@@ -477,9 +493,9 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
477
  * @return string the field
478
  */
479
  public function checkbox_list() {
480
- $field = $this->doFieldStart();
481
- $field .= $this->doFieldLabel();
482
- $field .= $this->doFieldDivStart();
483
 
484
  if ( ! is_array( $this->value ) ) {
485
  if ( ! empty( $this->value ) ) {
@@ -493,7 +509,7 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
493
  foreach ( $this->options as $option_id => $title ) {
494
  $field .= '<label title="' . esc_attr( $title ) . '">';
495
  $field .= '<input type="checkbox"';
496
- $field .= $this->doFieldName( true );
497
  $field .= ' value="' . esc_attr( $option_id ) . '" ' . checked( in_array( $option_id, $this->value ), true, false ) . '/>';
498
  $field .= $title;
499
  $field .= '</label>';
@@ -501,8 +517,8 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
501
  } else {
502
  $field .= '<span class="tribe-error">' . esc_html__( 'No checkbox options specified', 'tribe-common' ) . '</span>';
503
  }
504
- $field .= $this->doFieldDivEnd();
505
- $field .= $this->doFieldEnd();
506
 
507
  return $field;
508
  }
@@ -513,17 +529,17 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
513
  * @return string the field
514
  */
515
  public function checkbox_bool() {
516
- $field = $this->doFieldStart();
517
- $field .= $this->doFieldLabel();
518
- $field .= $this->doFieldDivStart();
519
  $field .= '<input type="checkbox"';
520
- $field .= $this->doFieldName();
521
  $field .= ' value="1" ' . checked( $this->value, true, false );
522
- $field .= $this->doFieldAttributes();
523
  $field .= '/>';
524
- $field .= $this->doScreenReaderLabel();
525
- $field .= $this->doFieldDivEnd();
526
- $field .= $this->doFieldEnd();
527
 
528
  return $field;
529
  }
@@ -534,12 +550,12 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
534
  * @return string the field
535
  */
536
  public function dropdown() {
537
- $field = $this->doFieldStart();
538
- $field .= $this->doFieldLabel();
539
- $field .= $this->doFieldDivStart();
540
  if ( is_array( $this->options ) && ! empty( $this->options ) ) {
541
  $field .= '<select';
542
- $field .= $this->doFieldName();
543
  $field .= '>';
544
  foreach ( $this->options as $option_id => $title ) {
545
  $field .= '<option value="' . esc_attr( $option_id ) . '"';
@@ -551,14 +567,14 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
551
  $field .= '>' . esc_html( $title ) . '</option>';
552
  }
553
  $field .= '</select>';
554
- $field .= $this->doScreenReaderLabel();
555
  } elseif ( $this->if_empty ) {
556
  $field .= '<span class="empty-field">' . (string) $this->if_empty . '</span>';
557
  } else {
558
  $field .= '<span class="tribe-error">' . esc_html__( 'No select options specified', 'tribe-common' ) . '</span>';
559
  }
560
- $field .= $this->doFieldDivEnd();
561
- $field .= $this->doFieldEnd();
562
 
563
  return $field;
564
  }
@@ -595,22 +611,77 @@ if ( ! class_exists( 'Tribe__Field' ) ) {
595
  * @return string the field
596
  */
597
  public function license_key() {
598
- $field = $this->doFieldStart();
599
- $field .= $this->doFieldLabel();
600
- $field .= $this->doFieldDivStart();
601
  $field .= '<input';
602
  $field .= ' type="text"';
603
- $field .= $this->doFieldName();
604
- $field .= $this->doFieldValue();
605
  $field .= '/>';
606
  $field .= '<p class="license-test-results"><img src="' . esc_url( admin_url( 'images/wpspin_light.gif' ) ) . '" class="ajax-loading-license" alt="Loading" style="display: none"/>';
607
  $field .= '<span class="key-validity"></span>';
608
- $field .= $this->doScreenReaderLabel();
609
- $field .= $this->doFieldDivEnd();
610
- $field .= $this->doFieldEnd();
611
 
612
  return $field;
613
  }
614
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615
  } // end class
616
  } // endif class_exists
68
  'attributes' => array(),
69
  'class' => null,
70
  'label' => null,
71
+ 'placeholder' => null,
72
  'tooltip' => null,
73
  'size' => 'medium',
74
  'html' => null,
82
  'clear_after' => true,
83
  );
84
 
85
+ // a list of valid field types, to prevent screwy behavior
86
  $this->valid_field_types = array(
87
  'heading',
88
  'html',
107
  $id = esc_attr( $id );
108
  $type = esc_attr( $args['type'] );
109
  $name = esc_attr( $args['name'] );
110
+ $placeholder = esc_attr( $args['placeholder'] );
111
  $class = sanitize_html_class( $args['class'] );
112
  $label = wp_kses(
113
  $args['label'], array(
176
  }
177
 
178
  // epicness
179
+ $this->do_field();
 
180
  }
181
 
182
  /**
186
  *
187
  * @return void
188
  */
189
+ public function do_field() {
190
 
191
  if ( $this->conditional ) {
192
 
218
  *
219
  * @return string the field start
220
  */
221
+ public function do_field_start() {
222
  $return = '<fieldset id="tribe-field-' . $this->id . '"';
223
  $return .= ' class="tribe-field tribe-field-' . $this->type;
224
  $return .= ( $this->error ) ? ' tribe-error' : '';
234
  *
235
  * @return string the field end
236
  */
237
+ public function do_field_end() {
238
  $return = '</fieldset>';
239
  $return .= ( $this->clear_after ) ? '<div class="clear"></div>' : '';
240
 
246
  *
247
  * @return string the field label
248
  */
249
+ public function do_field_label() {
250
  $return = '';
251
  if ( $this->label ) {
252
  $return = '<legend class="tribe-field-label">' . $this->label . '</legend>';
260
  *
261
  * @return string the field div start
262
  */
263
+ public function do_field_div_start() {
264
  $return = '<div class="tribe-field-wrap">';
265
 
266
  return apply_filters( 'tribe_field_div_start', $return, $this );
271
  *
272
  * @return string the field div end
273
  */
274
+ public function do_field_div_end() {
275
+ $return = $this->do_tool_tip();
276
  $return .= '</div>';
277
 
278
  return apply_filters( 'tribe_field_div_end', $return, $this );
283
  *
284
  * @return string the field tooltip
285
  */
286
+ public function do_tool_tip() {
287
  $return = '';
288
  if ( $this->tooltip ) {
289
  $return = '<p class="tooltip description">' . $this->tooltip . '</p>';
297
  *
298
  * @return string the screen reader label
299
  */
300
+ public function do_screen_reader_label() {
301
  $return = '';
302
  if ( $this->tooltip ) {
303
  $return = '<label class="screen-reader-text">' . $this->tooltip . '</label>';
311
  *
312
  * @return string the field value
313
  */
314
+ public function do_field_value() {
315
  $return = '';
316
  if ( $this->value ) {
317
  $return = ' value="' . $this->value . '"';
327
  *
328
  * @return string the field name
329
  */
330
+ public function do_field_name( $multi = false ) {
331
  $return = '';
332
  if ( $this->name ) {
333
  if ( $multi ) {
340
  return apply_filters( 'tribe_field_name', $return, $this->name, $this );
341
  }
342
 
343
+ /**
344
+ * returns the field's placeholder
345
+ *
346
+ * @return string the field value
347
+ */
348
+ public function do_field_placeholder() {
349
+ $return = '';
350
+ if ( $this->placeholder ) {
351
+ $return = ' placeholder="' . $this->placeholder . '"';
352
+ }
353
+
354
+ return apply_filters( 'tribe_field_placeholder', $return, $this->placeholder, $this );
355
+ }
356
+
357
  /**
358
  * Return a string of attributes for the field
359
  *
360
  * @return string
361
  **/
362
+ public function do_field_attributes() {
363
  $return = '';
364
  if ( ! empty( $this->attributes ) ) {
365
  foreach ( $this->attributes as $key => $value ) {
387
  * @return string the field
388
  */
389
  public function html() {
390
+ $field = $this->do_field_label();
391
  $field .= $this->html;
392
 
393
  return $field;
399
  * @return string the field
400
  */
401
  public function text() {
402
+ $field = $this->do_field_start();
403
+ $field .= $this->do_field_label();
404
+ $field .= $this->do_field_div_start();
405
  $field .= '<input';
406
  $field .= ' type="text"';
407
+ $field .= $this->do_field_name();
408
+ $field .= $this->do_field_value();
409
+ $field .= $this->do_field_placeholder();
410
  $field .= '/>';
411
+ $field .= $this->do_screen_reader_label();
412
+ $field .= $this->do_field_div_end();
413
+ $field .= $this->do_field_end();
414
 
415
  return $field;
416
  }
421
  * @return string the field
422
  */
423
  public function textarea() {
424
+ $field = $this->do_field_start();
425
+ $field .= $this->do_field_label();
426
+ $field .= $this->do_field_div_start();
427
  $field .= '<textarea';
428
+ $field .= $this->do_field_name();
429
  $field .= '>';
430
  $field .= esc_html( stripslashes( $this->value ) );
431
  $field .= '</textarea>';
432
+ $field .= $this->do_screen_reader_label();
433
+ $field .= $this->do_field_div_end();
434
+ $field .= $this->do_field_end();
435
 
436
  return $field;
437
  }
449
  ob_start();
450
  wp_editor( html_entity_decode( ( $this->value ) ), $this->name, $settings );
451
  $editor = ob_get_clean();
452
+ $field = $this->do_field_start();
453
+ $field .= $this->do_field_label();
454
+ $field .= $this->do_field_div_start();
455
  $field .= $editor;
456
+ $field .= $this->do_screen_reader_label();
457
+ $field .= $this->do_field_div_end();
458
+ $field .= $this->do_field_end();
459
 
460
  return $field;
461
  }
466
  * @return string the field
467
  */
468
  public function radio() {
469
+ $field = $this->do_field_start();
470
+ $field .= $this->do_field_label();
471
+ $field .= $this->do_field_div_start();
472
  if ( is_array( $this->options ) ) {
473
  foreach ( $this->options as $option_id => $title ) {
474
  $field .= '<label title="' . esc_attr( strip_tags( $title ) ) . '">';
475
  $field .= '<input type="radio"';
476
+ $field .= $this->do_field_name();
477
  $field .= ' value="' . esc_attr( $option_id ) . '" ' . checked( $this->value, $option_id, false ) . '/>';
478
  $field .= $title;
479
  $field .= '</label>';
481
  } else {
482
  $field .= '<span class="tribe-error">' . esc_html__( 'No radio options specified', 'tribe-common' ) . '</span>';
483
  }
484
+ $field .= $this->do_field_div_end();
485
+ $field .= $this->do_field_end();
486
 
487
  return $field;
488
  }
493
  * @return string the field
494
  */
495
  public function checkbox_list() {
496
+ $field = $this->do_field_start();
497
+ $field .= $this->do_field_label();
498
+ $field .= $this->do_field_div_start();
499
 
500
  if ( ! is_array( $this->value ) ) {
501
  if ( ! empty( $this->value ) ) {
509
  foreach ( $this->options as $option_id => $title ) {
510
  $field .= '<label title="' . esc_attr( $title ) . '">';
511
  $field .= '<input type="checkbox"';
512
+ $field .= $this->do_field_name( true );
513
  $field .= ' value="' . esc_attr( $option_id ) . '" ' . checked( in_array( $option_id, $this->value ), true, false ) . '/>';
514
  $field .= $title;
515
  $field .= '</label>';
517
  } else {
518
  $field .= '<span class="tribe-error">' . esc_html__( 'No checkbox options specified', 'tribe-common' ) . '</span>';
519
  }
520
+ $field .= $this->do_field_div_end();
521
+ $field .= $this->do_field_end();
522
 
523
  return $field;
524
  }
529
  * @return string the field
530
  */
531
  public function checkbox_bool() {
532
+ $field = $this->do_field_start();
533
+ $field .= $this->do_field_label();
534
+ $field .= $this->do_field_div_start();
535
  $field .= '<input type="checkbox"';
536
+ $field .= $this->do_field_name();
537
  $field .= ' value="1" ' . checked( $this->value, true, false );
538
+ $field .= $this->do_field_attributes();
539
  $field .= '/>';
540
+ $field .= $this->do_screen_reader_label();
541
+ $field .= $this->do_field_div_end();
542
+ $field .= $this->do_field_end();
543
 
544
  return $field;
545
  }
550
  * @return string the field
551
  */
552
  public function dropdown() {
553
+ $field = $this->do_field_start();
554
+ $field .= $this->do_field_label();
555
+ $field .= $this->do_field_div_start();
556
  if ( is_array( $this->options ) && ! empty( $this->options ) ) {
557
  $field .= '<select';
558
+ $field .= $this->do_field_name();
559
  $field .= '>';
560
  foreach ( $this->options as $option_id => $title ) {
561
  $field .= '<option value="' . esc_attr( $option_id ) . '"';
567
  $field .= '>' . esc_html( $title ) . '</option>';
568
  }
569
  $field .= '</select>';
570
+ $field .= $this->do_screen_reader_label();
571
  } elseif ( $this->if_empty ) {
572
  $field .= '<span class="empty-field">' . (string) $this->if_empty . '</span>';
573
  } else {
574
  $field .= '<span class="tribe-error">' . esc_html__( 'No select options specified', 'tribe-common' ) . '</span>';
575
  }
576
+ $field .= $this->do_field_div_end();
577
+ $field .= $this->do_field_end();
578
 
579
  return $field;
580
  }
611
  * @return string the field
612
  */
613
  public function license_key() {
614
+ $field = $this->do_field_start();
615
+ $field .= $this->do_field_label();
616
+ $field .= $this->do_field_div_start();
617
  $field .= '<input';
618
  $field .= ' type="text"';
619
+ $field .= $this->do_field_name();
620
+ $field .= $this->do_field_value();
621
  $field .= '/>';
622
  $field .= '<p class="license-test-results"><img src="' . esc_url( admin_url( 'images/wpspin_light.gif' ) ) . '" class="ajax-loading-license" alt="Loading" style="display: none"/>';
623
  $field .= '<span class="key-validity"></span>';
624
+ $field .= $this->do_screen_reader_label();
625
+ $field .= $this->do_field_div_end();
626
+ $field .= $this->do_field_end();
627
 
628
  return $field;
629
  }
630
 
631
+ /* deprecated camelCase methods */
632
+ public function doField() {
633
+ _deprecated_function( __METHOD__, '4.3', __CLASS__ . '::do_field' );
634
+ return $this->do_field();
635
+ }
636
+
637
+ public function doFieldStart() {
638
+ _deprecated_function( __METHOD__, '4.3', __CLASS__ . '::do_field_start' );
639
+ return $this->do_field_start();
640
+ }
641
+
642
+ public function doFieldEnd() {
643
+ _deprecated_function( __METHOD__, '4.3', __CLASS__ . '::do_field_end' );
644
+ return $this->do_field_end();
645
+ }
646
+
647
+ public function doFieldLabel() {
648
+ _deprecated_function( __METHOD__, '4.3', __CLASS__ . '::do_field_label' );
649
+ return $this->do_field_label();
650
+ }
651
+
652
+ public function doFieldDivStart() {
653
+ _deprecated_function( __METHOD__, '4.3', __CLASS__ . '::do_field_div_start' );
654
+ return $this->do_field_div_start();
655
+ }
656
+
657
+ public function doFieldDivEnd() {
658
+ _deprecated_function( __METHOD__, '4.3', __CLASS__ . '::do_field_div_end' );
659
+ return $this->do_field_div_end();
660
+ }
661
+
662
+ public function doToolTip() {
663
+ _deprecated_function( __METHOD__, '4.3', __CLASS__ . '::do_tool_tip' );
664
+ return $this->do_tool_tip();
665
+ }
666
+
667
+ public function doFieldValue() {
668
+ _deprecated_function( __METHOD__, '4.3', __CLASS__ . '::do_field_value' );
669
+ return $this->do_field_value();
670
+ }
671
+
672
+ public function doFieldName( $multi = false ) {
673
+ _deprecated_function( __METHOD__, '4.3', __CLASS__ . '::do_field_name' );
674
+ return $this->do_field_name( $multi );
675
+ }
676
+
677
+ public function doFieldAttributes() {
678
+ _deprecated_function( __METHOD__, '4.3', __CLASS__ . '::do_field_attributes' );
679
+ return $this->do_field_attributes();
680
+ }
681
+
682
+ public function doScreenReaderLabel() {
683
+ _deprecated_function( __METHOD__, '4.3', __CLASS__ . '::do_screen_reader_label' );
684
+ return $this->do_screen_reader_label();
685
+ }
686
  } // end class
687
  } // endif class_exists
common/src/Tribe/JSON_LD/Abstract.php CHANGED
@@ -5,6 +5,7 @@ if ( ! defined( 'ABSPATH' ) ) {
5
  die( '-1' );
6
  }
7
 
 
8
  /**
9
  * An Abstract class that will allow us to have a base to go for all
10
  * the other JSON-LD classes.
@@ -15,10 +16,18 @@ abstract class Tribe__JSON_LD__Abstract {
15
 
16
  /**
17
  * Holder of the Instances
 
18
  * @var array
19
  */
20
  private static $instances = array();
21
 
 
 
 
 
 
 
 
22
  /**
23
  * The class singleton constructor.
24
  *
@@ -42,14 +51,32 @@ abstract class Tribe__JSON_LD__Abstract {
42
 
43
  /**
44
  * Compile the schema.org event data into an array
 
 
 
 
 
 
 
 
 
 
 
45
  */
46
  public function get_data( $post = null, $args = array() ) {
47
- if ( ! $post instanceof WP_Post ) {
48
- $post = Tribe__Main::post_id_helper( $post );
 
 
 
 
 
 
49
  }
50
- $post = get_post( $post );
51
 
52
- if ( ! $post instanceof WP_Post ) {
 
 
53
  return array();
54
  }
55
 
@@ -59,10 +86,10 @@ abstract class Tribe__JSON_LD__Abstract {
59
  if ( ! isset( $args['context'] ) || false !== $args['context'] ) {
60
  $data->{'@context'} = 'http://schema.org';
61
  }
62
- $data->{'@type'} = $this->type;
63
 
64
- $data->name = esc_js( get_the_title( $post ) );
65
- $data->description = esc_js( tribe_events_get_the_excerpt( $post ) );
66
 
67
  if ( has_post_thumbnail( $post ) ) {
68
  $data->image = wp_get_attachment_url( get_post_thumbnail_id( $post ) );
@@ -77,6 +104,7 @@ abstract class Tribe__JSON_LD__Abstract {
77
 
78
  /**
79
  * puts together the actual html/json javascript block for output
 
80
  * @return string
81
  */
82
  public function get_markup( $post = null, $args = array() ) {
@@ -91,11 +119,14 @@ abstract class Tribe__JSON_LD__Abstract {
91
  * @example tribe_json_ld_thing_object
92
  * @example tribe_json_ld_event_object
93
  *
94
- * @param object $data objects representing the Google Markup for each event.
95
- * @param array $args the arguments used to get data
96
  * @param WP_Post $post the arguments used to get data
97
  */
98
  $data[ $post_id ] = apply_filters( "tribe_json_ld_{$type}_object", $_data, $args, get_post( $post_id ) );
 
 
 
99
  }
100
 
101
  /**
@@ -126,18 +157,107 @@ abstract class Tribe__JSON_LD__Abstract {
126
 
127
  /**
128
  * Allows users to filter the end markup of JSON-LD
 
129
  * @deprecated
130
  * @todo Remove on 4.4
 
131
  * @param string The HTML for the JSON LD markup
132
  */
133
  $html = apply_filters( 'tribe_google_data_markup_json', $html );
134
 
135
  /**
136
  * Allows users to filter the end markup of JSON-LD
 
137
  * @param string The HTML for the JSON LD markup
138
  */
139
  $html = apply_filters( 'tribe_json_ld_markup', $html );
140
 
141
  echo $html;
142
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  }
5
  die( '-1' );
6
  }
7
 
8
+
9
  /**
10
  * An Abstract class that will allow us to have a base to go for all
11
  * the other JSON-LD classes.
16
 
17
  /**
18
  * Holder of the Instances
19
+ *
20
  * @var array
21
  */
22
  private static $instances = array();
23
 
24
+ /**
25
+ * Holder for the Already fetched Posts
26
+ *
27
+ * @var array
28
+ */
29
+ protected static $posts = array();
30
+
31
  /**
32
  * The class singleton constructor.
33
  *
51
 
52
  /**
53
  * Compile the schema.org event data into an array
54
+ *
55
+ * @param mixed $post Either a post ID or a WP_post object.
56
+ * @param array $args {
57
+ * Optional. An array of arguments to control the returned data.
58
+ *
59
+ * @type string $context The value of the `@context` tag, defaults to 'https://schema.org'
60
+ * }
61
+ *
62
+ * @return array Either an array containing a post data or an empty array if the post data cannot
63
+ * be generated, the `$post` parameter is not a valid post ID or object or the data
64
+ * for the post has been fetched already.
65
  */
66
  public function get_data( $post = null, $args = array() ) {
67
+ $post_id = Tribe__Main::post_id_helper( $post );
68
+ if ( ! $post_id ) {
69
+ return array();
70
+ }
71
+
72
+ // This prevents a JSON_LD from existing twice one the same page
73
+ if ( $this->exists( $post_id ) ) {
74
+ return array();
75
  }
 
76
 
77
+ $post = get_post( $post_id );
78
+
79
+ if ( empty( $post->ID ) ) {
80
  return array();
81
  }
82
 
86
  if ( ! isset( $args['context'] ) || false !== $args['context'] ) {
87
  $data->{'@context'} = 'http://schema.org';
88
  }
89
+ $data->{'@type'} = $this->type;
90
 
91
+ $data->name = esc_js( get_the_title( $post ) );
92
+ $data->description = esc_js( tribe_events_get_the_excerpt( $post ) );
93
 
94
  if ( has_post_thumbnail( $post ) ) {
95
  $data->image = wp_get_attachment_url( get_post_thumbnail_id( $post ) );
104
 
105
  /**
106
  * puts together the actual html/json javascript block for output
107
+ *
108
  * @return string
109
  */
110
  public function get_markup( $post = null, $args = array() ) {
119
  * @example tribe_json_ld_thing_object
120
  * @example tribe_json_ld_event_object
121
  *
122
+ * @param object $data objects representing the Google Markup for each event.
123
+ * @param array $args the arguments used to get data
124
  * @param WP_Post $post the arguments used to get data
125
  */
126
  $data[ $post_id ] = apply_filters( "tribe_json_ld_{$type}_object", $_data, $args, get_post( $post_id ) );
127
+
128
+ // Register this post as done already
129
+ $this->register( $post_id );
130
  }
131
 
132
  /**
157
 
158
  /**
159
  * Allows users to filter the end markup of JSON-LD
160
+ *
161
  * @deprecated
162
  * @todo Remove on 4.4
163
+ *
164
  * @param string The HTML for the JSON LD markup
165
  */
166
  $html = apply_filters( 'tribe_google_data_markup_json', $html );
167
 
168
  /**
169
  * Allows users to filter the end markup of JSON-LD
170
+ *
171
  * @param string The HTML for the JSON LD markup
172
  */
173
  $html = apply_filters( 'tribe_json_ld_markup', $html );
174
 
175
  echo $html;
176
  }
177
+
178
+ /**
179
+ * Gets from the Posts index a specific post or fetch all of them
180
+ *
181
+ * @param int|WP_Post $post The Post Object or ID
182
+ *
183
+ * @return null|array|WP_Post Returns an Indexed Array of Posts, a found Post or Null if not found
184
+ */
185
+ public function get( $post = null ) {
186
+ if ( is_null( $post ) ) {
187
+ return self::$posts;
188
+ }
189
+ $id = Tribe__Main::post_id_helper( $post );
190
+
191
+ if ( $this->exists( $id ) ) {
192
+ return self::$posts[ $id ];
193
+ }
194
+
195
+ return null;
196
+ }
197
+
198
+ /**
199
+ * Checks if a Post has been registered to the JSON-LD index
200
+ *
201
+ * @param int|WP_Post $post The Post Object or ID
202
+ *
203
+ * @return bool
204
+ */
205
+ public function exists( $post ) {
206
+ return isset( self::$posts[ Tribe__Main::post_id_helper( $post ) ] );
207
+ }
208
+
209
+ /**
210
+ * Register the new Post on the Index of created ones
211
+ *
212
+ * @param int|WP_Post $post The Post Object or ID
213
+ *
214
+ * @return WP_Post The Post Object that was registered
215
+ */
216
+ public function register( $post ) {
217
+ $id = Tribe__Main::post_id_helper( $post );
218
+ if ( $this->exists( $id ) ) {
219
+ return self::$posts[ $id ];
220
+ }
221
+
222
+ self::$posts[ $id ] = get_post( $id );
223
+
224
+ return self::$posts[ $id ];
225
+ }
226
+
227
+ /**
228
+ * Remove an Post from the Indexed list
229
+ *
230
+ * @param int|WP_Post $post The Post Object or ID
231
+ *
232
+ * @return bool
233
+ */
234
+ public function remove( $post ) {
235
+ $id = Tribe__Main::post_id_helper( $post );
236
+
237
+ if ( ! $this->exists( $id ) ) {
238
+ return false;
239
+ }
240
+
241
+ unset( self::$posts[ $id ] );
242
+
243
+ return true;
244
+ }
245
+
246
+ /**
247
+ * Empties the registered posts cache variable.
248
+ *
249
+ * Added for testing purposes.
250
+ */
251
+ public static function unregister_all() {
252
+ self::$posts = array();
253
+ }
254
+
255
+ /**
256
+ * Returns an array of the registered post IDs.
257
+ *
258
+ * @return array
259
+ */
260
+ public static function get_registered_post_ids() {
261
+ return array_keys( self::$posts );
262
+ }
263
  }
common/src/Tribe/Log.php CHANGED
@@ -337,6 +337,11 @@ class Tribe__Log {
337
  $this->build_prioritized_levels();
338
  }
339
 
 
 
 
 
 
340
  return $this->prioritized_levels[ $level_code ] <= $this->prioritized_levels[ $this->current_level ];
341
  }
342
 
@@ -348,7 +353,7 @@ class Tribe__Log {
348
  */
349
  protected function build_prioritized_levels() {
350
  foreach ( $this->get_logging_levels() as $index => $level_data ) {
351
- $this->prioritized_levels[ $level_data[1] ] = $index;
352
  }
353
  }
354
  }
337
  $this->build_prioritized_levels();
338
  }
339
 
340
+ // Protect against the possibility non-existent level codes might be passed in
341
+ if ( ! isset( $this->prioritized_levels[ $level_code ] ) ) {
342
+ return false;
343
+ }
344
+
345
  return $this->prioritized_levels[ $level_code ] <= $this->prioritized_levels[ $this->current_level ];
346
  }
347
 
353
  */
354
  protected function build_prioritized_levels() {
355
  foreach ( $this->get_logging_levels() as $index => $level_data ) {
356
+ $this->prioritized_levels[ $level_data[ 0 ] ] = $index;
357
  }
358
  }
359
  }
common/src/Tribe/Log/Admin.php CHANGED
@@ -103,7 +103,7 @@ class Tribe__Log__Admin {
103
  public function register_script() {
104
  wp_register_script(
105
  'tribe-common-logging-controls',
106
- tribe_resource_url( 'admin-log-controls.js', false, 'common' ),
107
  array( 'jquery' ),
108
  Tribe__Main::VERSION,
109
  true
@@ -126,7 +126,11 @@ class Tribe__Log__Admin {
126
  * @return array
127
  */
128
  protected function get_available_logs() {
129
- $available_logs = $this->current_logger()->list_available_logs();
 
 
 
 
130
 
131
  if ( empty( $available_logs ) ) {
132
  return array( '' => _x( 'None currently available', 'log selector', 'tribe-common' ) );
@@ -165,9 +169,12 @@ class Tribe__Log__Admin {
165
  * @return array
166
  */
167
  protected function get_log_entries( $log = null ) {
168
- $logger = $this->current_logger();
169
- $logger->use_log( $log );
170
- return $logger->retrieve();
 
 
 
171
  }
172
 
173
  /**
103
  public function register_script() {
104
  wp_register_script(
105
  'tribe-common-logging-controls',
106
+ tribe_resource_url( 'admin-log-controls.js', false, null, Tribe__Main::instance() ),
107
  array( 'jquery' ),
108
  Tribe__Main::VERSION,
109
  true
126
  * @return array
127
  */
128
  protected function get_available_logs() {
129
+ $current_logger = $this->current_logger();
130
+
131
+ if ( $current_logger ) {
132
+ $available_logs = $this->current_logger()->list_available_logs();
133
+ }
134
 
135
  if ( empty( $available_logs ) ) {
136
  return array( '' => _x( 'None currently available', 'log selector', 'tribe-common' ) );
169
  * @return array
170
  */
171
  protected function get_log_entries( $log = null ) {
172
+ if ( $logger = $this->current_logger() ) {
173
+ $logger->use_log( $log );
174
+ return (array) $logger->retrieve();
175
+ }
176
+
177
+ return array();
178
  }
179
 
180
  /**
common/src/Tribe/Log/File_Logger.php CHANGED
@@ -59,7 +59,14 @@ class Tribe__Log__File_Logger implements Tribe__Log__Logger {
59
  */
60
  protected function obtain_handle() {
61
  $this->close_handle();
62
- $this->handle = fopen( $this->log_file, $this->context );
 
 
 
 
 
 
 
63
  }
64
 
65
  /**
@@ -138,6 +145,11 @@ class Tribe__Log__File_Logger implements Tribe__Log__Logger {
138
  $this->set_context( 'a' );
139
  }
140
 
 
 
 
 
 
141
  fputcsv( $this->handle, array( date_i18n( 'Y-m-d H:i:s' ), $entry, $type, $src ) );
142
  }
143
 
@@ -160,6 +172,11 @@ class Tribe__Log__File_Logger implements Tribe__Log__Logger {
160
  $this->set_context( 'r' );
161
  }
162
 
 
 
 
 
 
163
  $rows = array();
164
 
165
  while ( $current_row = fgetcsv( $this->handle ) ) {
@@ -262,4 +279,4 @@ class Tribe__Log__File_Logger implements Tribe__Log__Logger {
262
  }
263
  }
264
  }
265
- }
59
  */
60
  protected function obtain_handle() {
61
  $this->close_handle();
62
+
63
+ if ( ! file_exists( $this->log_file ) ) {
64
+ touch( $this->log_file );
65
+ }
66
+
67
+ if ( is_readable( $this->log_file ) ) {
68
+ $this->handle = fopen( $this->log_file, $this->context );
69
+ }
70
  }
71
 
72
  /**
145
  $this->set_context( 'a' );
146
  }
147
 
148
+ // Couldn't obtain the file handle? We'll bail out without causing further disruption
149
+ if ( ! $this->handle ) {
150
+ return;
151
+ }
152
+
153
  fputcsv( $this->handle, array( date_i18n( 'Y-m-d H:i:s' ), $entry, $type, $src ) );
154
  }
155
 
172
  $this->set_context( 'r' );
173
  }
174
 
175
+ // Couldn't obtain the file handle? We'll bail out without causing further disruption
176
+ if ( ! $this->handle ) {
177
+ return array();
178
+ }
179
+
180
  $rows = array();
181
 
182
  while ( $current_row = fgetcsv( $this->handle ) ) {
279
  }
280
  }
281
  }
282
+ }
common/src/Tribe/Main.php CHANGED
@@ -17,7 +17,7 @@ class Tribe__Main {
17
  const OPTIONNAME = 'tribe_events_calendar_options';
18
  const OPTIONNAMENETWORK = 'tribe_events_calendar_network_options';
19
 
20
- const VERSION = '4.2.7';
21
  const FEED_URL = 'https://theeventscalendar.com/feed/';
22
 
23
  protected $plugin_context;
@@ -25,8 +25,21 @@ class Tribe__Main {
25
  protected $doing_ajax = false;
26
  protected $log;
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  public static $tribe_url = 'http://tri.be/';
29
- public static $tec_url = 'http://theeventscalendar.com/';
30
 
31
  public $plugin_dir;
32
  public $plugin_path;
@@ -43,7 +56,10 @@ class Tribe__Main {
43
 
44
  $this->plugin_path = trailingslashit( dirname( dirname( dirname( __FILE__ ) ) ) );
45
  $this->plugin_dir = trailingslashit( basename( $this->plugin_path ) );
46
- $this->plugin_url = plugins_url( $this->plugin_dir );
 
 
 
47
 
48
  $this->load_text_domain( 'tribe-common', basename( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) ) . '/common/lang/' );
49
 
@@ -70,9 +86,16 @@ class Tribe__Main {
70
  require_once dirname( __FILE__ ) . '/Autoloader.php';
71
  }
72
 
73
- $prefixes = array( 'Tribe__' => dirname( __FILE__ ) );
74
  $autoloader = Tribe__Autoloader::instance();
 
 
75
  $autoloader->register_prefixes( $prefixes );
 
 
 
 
 
 
76
  $autoloader->register_autoloader();
77
  }
78
 
@@ -89,6 +112,7 @@ class Tribe__Main {
89
  public function init_libraries() {
90
  Tribe__Debug::instance();
91
  Tribe__Settings_Manager::instance();
 
92
 
93
  require_once $this->plugin_path . 'src/functions/utils.php';
94
  require_once $this->plugin_path . 'src/functions/template-tags/general.php';
@@ -101,65 +125,91 @@ class Tribe__Main {
101
  /**
102
  * Registers resources that can/should be enqueued
103
  */
104
- public function register_resources() {
105
- $resources_url = plugins_url( 'src/resources', dirname( dirname( __FILE__ ) ) );
106
-
107
- wp_register_style(
108
- 'tribe-common-admin',
109
- $resources_url . '/css/tribe-common-admin.css',
110
- array(),
111
- apply_filters( 'tribe_events_css_version', self::VERSION )
112
- );
113
-
114
- wp_register_script(
115
- 'ba-dotimeout',
116
- $resources_url . '/js/jquery.ba-dotimeout.js',
117
  array(
118
- 'jquery',
119
- ),
120
- apply_filters( 'tribe_events_css_version', self::VERSION ),
121
- true
 
 
 
 
 
 
 
 
 
 
 
122
  );
123
 
124
- wp_register_script(
125
- 'tribe-inline-bumpdown',
126
- $resources_url . '/js/inline-bumpdown.js',
127
  array(
128
- 'ba-dotimeout',
 
 
 
 
 
129
  ),
130
- apply_filters( 'tribe_events_css_version', self::VERSION ),
131
- true
132
- );
133
-
134
- wp_register_script(
135
- 'tribe-notice-dismiss',
136
- $resources_url . '/js/notice-dismiss.js',
137
- array( 'jquery' ),
138
- apply_filters( 'tribe_events_css_version', self::VERSION ),
139
- true
140
- );
141
- }
142
-
143
- /**
144
- * Registers vendor assets that can/should be enqueued
145
- */
146
- public function register_vendor() {
147
- $vendor_base = plugins_url( 'vendor', dirname( dirname( __FILE__ ) ) );
148
-
149
- wp_register_style(
150
- 'tribe-jquery-ui-theme',
151
- $vendor_base . '/jquery/ui.theme.css',
152
- array(),
153
- apply_filters( 'tribe_events_css_version', self::VERSION )
154
  );
155
 
156
- wp_register_style(
157
- 'tribe-jquery-ui-datepicker',
158
- $vendor_base . '/jquery/ui.datepicker.css',
159
- array( 'tribe-jquery-ui-theme' ),
160
- apply_filters( 'tribe_events_css_version', self::VERSION )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  );
162
-
163
  }
164
 
165
  /**
@@ -167,13 +217,12 @@ class Tribe__Main {
167
  */
168
  public function add_hooks() {
169
  add_action( 'plugins_loaded', array( 'Tribe__App_Shop', 'instance' ) );
 
170
 
171
- // Register for the assets to be availble everywhere
172
- add_action( 'init', array( $this, 'register_resources' ), 1 );
173
- add_action( 'init', array( $this, 'register_vendor' ), 1 );
174
-
175
- // Enqueue only when needed (admin)
176
- add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
177
  }
178
 
179
  /**
@@ -215,17 +264,6 @@ class Tribe__Main {
215
  return $loaded;
216
  }
217
 
218
- public function admin_enqueue_scripts() {
219
- wp_enqueue_script( 'tribe-inline-bumpdown' );
220
- wp_enqueue_script( 'tribe-notice-dismiss' );
221
- wp_enqueue_style( 'tribe-common-admin' );
222
-
223
- $helper = Tribe__Admin__Helpers::instance();
224
- if ( $helper->is_post_type_screen() ) {
225
- wp_enqueue_style( 'tribe-jquery-ui-datepicker' );
226
- }
227
- }
228
-
229
  /**
230
  * @return Tribe__Log
231
  */
@@ -233,6 +271,17 @@ class Tribe__Main {
233
  return $this->log;
234
  }
235
 
 
 
 
 
 
 
 
 
 
 
 
236
  /**
237
  * Returns the post types registered by Tribe plugins
238
  */
@@ -336,4 +385,16 @@ class Tribe__Main {
336
 
337
  return $instance;
338
  }
 
 
 
 
 
 
 
 
 
 
 
 
339
  }
17
  const OPTIONNAME = 'tribe_events_calendar_options';
18
  const OPTIONNAMENETWORK = 'tribe_events_calendar_network_options';
19
 
20
+ const VERSION = '4.3';
21
  const FEED_URL = 'https://theeventscalendar.com/feed/';
22
 
23
  protected $plugin_context;
25
  protected $doing_ajax = false;
26
  protected $log;
27
 
28
+ /**
29
+ * Manages PUE license key notifications.
30
+ *
31
+ * It's important for the sanity of our users that only one instance of this object
32
+ * be created. However, multiple Tribe__Main objects can and will be instantiated, hence
33
+ * why for the time being we need to make this field static.
34
+ *
35
+ * @see https://central.tri.be/issues/65755
36
+ *
37
+ * @var Tribe__PUE__Notices
38
+ */
39
+ protected static $pue_notices;
40
+
41
  public static $tribe_url = 'http://tri.be/';
42
+ public static $tec_url = 'https://theeventscalendar.com/';
43
 
44
  public $plugin_dir;
45
  public $plugin_path;
56
 
57
  $this->plugin_path = trailingslashit( dirname( dirname( dirname( __FILE__ ) ) ) );
58
  $this->plugin_dir = trailingslashit( basename( $this->plugin_path ) );
59
+
60
+ $parent_plugin_dir = trailingslashit( plugin_basename( $this->plugin_path ) );
61
+
62
+ $this->plugin_url = plugins_url( $parent_plugin_dir === $this->plugin_dir ? $this->plugin_dir : $parent_plugin_dir );
63
 
64
  $this->load_text_domain( 'tribe-common', basename( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) ) . '/common/lang/' );
65
 
86
  require_once dirname( __FILE__ ) . '/Autoloader.php';
87
  }
88
 
 
89
  $autoloader = Tribe__Autoloader::instance();
90
+
91
+ $prefixes = array( 'Tribe__' => dirname( __FILE__ ) );
92
  $autoloader->register_prefixes( $prefixes );
93
+
94
+ foreach ( glob( $this->plugin_path . 'src/deprecated/*.php' ) as $file ) {
95
+ $class_name = str_replace( '.php', '', basename( $file ) );
96
+ $autoloader->register_class( $class_name, $file );
97
+ }
98
+
99
  $autoloader->register_autoloader();
100
  }
101
 
112
  public function init_libraries() {
113
  Tribe__Debug::instance();
114
  Tribe__Settings_Manager::instance();
115
+ $this->pue_notices();
116
 
117
  require_once $this->plugin_path . 'src/functions/utils.php';
118
  require_once $this->plugin_path . 'src/functions/template-tags/general.php';
125
  /**
126
  * Registers resources that can/should be enqueued
127
  */
128
+ public function load_assets() {
129
+ // These ones are only registred
130
+ tribe_assets(
131
+ $this,
 
 
 
 
 
 
 
 
 
132
  array(
133
+ array( 'tribe-clipboard', 'vendor/clipboard/clipboard.js' ),
134
+ array( 'datatables', 'vendor/datatables/media/js/jquery.dataTables.js', array( 'jquery' ) ),
135
+ array( 'datatables-css', 'datatables.css' ),
136
+ array( 'datatables-responsive', 'vendor/datatables/extensions/Responsive/js/dataTables.responsive.js', array( 'jquery', 'datatables' ) ),
137
+ array( 'datatables-responsive-css', 'vendor/datatables/extensions/Responsive/css/responsive.dataTables.css' ),
138
+ array( 'datatables-select', 'vendor/datatables/extensions/Select/js/dataTables.select.js', array( 'jquery', 'datatables' ) ),
139
+ array( 'datatables-select-css', 'vendor/datatables/extensions/Select/css/select.dataTables.css' ),
140
+ array( 'datatables-scroller', 'vendor/datatables/extensions/Scroller/js/dataTables.scroller.js', array( 'jquery', 'datatables' ) ),
141
+ array( 'datatables-scroller-css', 'vendor/datatables/extensions/Scroller/css/scroller.dataTables.css' ),
142
+ array( 'datatables-fixedheader', 'vendor/datatables/extensions/FixedHeader/js/dataTables.fixedHeader.js', array( 'jquery', 'datatables' ) ),
143
+ array( 'datatables-fixedheader-css', 'vendor/datatables/extensions/FixedHeader/css/fixedHeader.dataTables.css' ),
144
+ array( 'tribe-datatables', 'tribe-datatables.js', array( 'datatables', 'datatables-select' ) ),
145
+ array( 'tribe-bumpdown', 'bumpdown.js', array( 'jquery', 'underscore', 'hoverIntent' ) ),
146
+ array( 'tribe-bumpdown-css', 'bumpdown.css' ),
147
+ )
148
  );
149
 
150
+ // These ones will be enqueued on `admin_enqueue_scripts` if the conditional method on filter is met
151
+ tribe_assets(
152
+ $this,
153
  array(
154
+ array( 'tribe-common-admin', 'tribe-common-admin.css', array( 'tribe-dependency-style', 'tribe-bumpdown-css' ) ),
155
+ array( 'tribe-dependency', 'dependency.js', array( 'jquery', 'underscore' ) ),
156
+ array( 'tribe-dependency-style', 'dependency.css' ),
157
+ array( 'tribe-pue-notices', 'pue-notices.js', array( 'jquery' ) ),
158
+ array( 'tribe-jquery-ui-theme', 'vendor/jquery/ui.theme.css' ),
159
+ array( 'tribe-jquery-ui-datepicker', 'vendor/jquery/ui.datepicker.css' ),
160
  ),
161
+ 'admin_enqueue_scripts',
162
+ array(
163
+ 'filter' => array( Tribe__Admin__Helpers::instance(), 'is_post_type_screen' ),
164
+ 'localize' => (object) array(
165
+ 'name' => 'tribe_system_info',
166
+ 'data' => array(
167
+ 'sysinfo_optin_nonce' => wp_create_nonce( 'sysinfo_optin_nonce' ),
168
+ 'clipboard_btn_text' => __( 'Copy to clipboard', 'tribe-common' ),
169
+ 'clipboard_copied_text' => __( 'System info copied', 'tribe-common' ),
170
+ 'clipboard_fail_text' => __( 'Press "Cmd + C" to copy', 'tribe-common' ),
171
+ ),
172
+ ),
173
+ )
 
 
 
 
 
 
 
 
 
 
 
174
  );
175
 
176
+ tribe_asset(
177
+ $this,
178
+ 'tribe-common',
179
+ 'tribe-common.js',
180
+ array( 'tribe-clipboard' ),
181
+ 'admin_enqueue_scripts',
182
+ array(
183
+ 'localize' => array(
184
+ 'name' => 'tribe_l10n_datatables',
185
+ 'data' => array(
186
+ 'aria' => array(
187
+ 'sort_ascending' => __( ': activate to sort column ascending', 'tribe-common' ),
188
+ 'sort_descending' => __( ': activate to sort column descending', 'tribe-common' ),
189
+ ),
190
+ 'length_menu' => __( 'Show _MENU_ entries', 'tribe-common' ),
191
+ 'empty_table' => __( 'No data available in table', 'tribe-common' ),
192
+ 'info' => __( 'Showing _START_ to _END_ of _TOTAL_ entries', 'tribe-common' ),
193
+ 'info_empty' => __( 'Showing 0 to 0 of 0 entries', 'tribe-common' ),
194
+ 'info_filtered' => __( '(filtered from _MAX_ total entries)', 'tribe-common' ),
195
+ 'zero_records' => __( 'No matching records found', 'tribe-common' ),
196
+ 'search' => __( 'Search:', 'tribe-common' ),
197
+ 'pagination' => array(
198
+ 'all' => __( 'All', 'tribe-common' ),
199
+ 'next' => __( 'Next', 'tribe-common' ),
200
+ 'previous' => __( 'Previous', 'tribe-common' ),
201
+ ),
202
+ 'select' => array(
203
+ 'rows' => array(
204
+ 0 => '',
205
+ '_' => __( ': Selected %d rows', 'tribe-common' ),
206
+ 1 => __( ': Selected 1 row', 'tribe-common' ),
207
+ ),
208
+ ),
209
+ ),
210
+ ),
211
+ )
212
  );
 
213
  }
214
 
215
  /**
217
  */
218
  public function add_hooks() {
219
  add_action( 'plugins_loaded', array( 'Tribe__App_Shop', 'instance' ) );
220
+ add_action( 'plugins_loaded', array( 'Tribe__Assets', 'instance' ), 1 );
221
 
222
+ // Register for the assets to be available everywhere
223
+ add_action( 'init', array( $this, 'load_assets' ), 1 );
224
+ add_action( 'plugins_loaded', array( 'Tribe__Admin__Notices', 'instance' ), 1 );
225
+ add_action( 'admin_enqueue_scripts', array( $this, 'store_admin_notices' ) );
 
 
226
  }
227
 
228
  /**
264
  return $loaded;
265
  }
266
 
 
 
 
 
 
 
 
 
 
 
 
267
  /**
268
  * @return Tribe__Log
269
  */
271
  return $this->log;
272
  }
273
 
274
+ /**
275
+ * @return Tribe__PUE__Notices
276
+ */
277
+ public function pue_notices() {
278
+ if ( empty( self::$pue_notices ) ) {
279
+ self::$pue_notices = new Tribe__PUE__Notices;
280
+ }
281
+
282
+ return self::$pue_notices;
283
+ }
284
+
285
  /**
286
  * Returns the post types registered by Tribe plugins
287
  */
385
 
386
  return $instance;
387
  }
388
+
389
+ /**
390
+ * Adds a hook
391
+ *
392
+ */
393
+ public function store_admin_notices( $page ) {
394
+ if ( 'plugins.php' !== $page ) {
395
+ return;
396
+ }
397
+ $notices = apply_filters( 'tribe_plugin_notices', array() );
398
+ wp_localize_script( 'tribe-pue-notices', 'tribe_plugin_notices', $notices );
399
+ }
400
  }
common/src/Tribe/PUE/Checker.php CHANGED
@@ -17,52 +17,153 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
17
  /**
18
  * A custom plugin update checker.
19
  *
20
- * @original author (c) Janis Elsts
21
- * @heavily modified by Darren Ethier
22
- * @slighty modified by Nick Ciske
23
- * @slighty modified by Joachim Kudish
24
- * @heavily modified by Peter Chester
25
- * @license GPL2 or greater.
26
- * @version 1.7
27
- * @access public
28
  */
29
  class Tribe__PUE__Checker {
30
 
31
- private $pue_update_url = ''; //The URL of the plugin's metadata file.
32
- private $plugin_file = ''; //Plugin filename relative to the plugins directory.
33
- private $plugin_name = ''; //variable used to hold the plugin_name as set by the constructor.
34
- private $slug = ''; //Plugin slug. (with .php extension)
35
- private $download_query = array(); //used to hold the query variables for download checks;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
- public $check_period = 12; //How often to check for updates (in hours).
38
- public $pue_option_name = ''; //Where to store the update info.
39
- public $json_error = ''; //for storing any json_error data that get's returned so we can display an admin notice.
40
- public $api_secret_key = ''; //used to hold the user API. If not set then nothing will work!
41
- public $install_key = false; //used to hold the install_key if set (included here for addons that will extend PUE to use install key checks)
42
- public $dismiss_upgrade; //for setting the dismiss upgrade option (per plugin).
43
- public $pue_install_key; //we'll customize this later so each plugin can have it's own install key!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
  /**
46
  * Class constructor.
47
  *
48
  * @param string $pue_update_url The URL of the plugin's metadata file.
49
  * @param string $slug The plugin's 'slug'.
50
- * @param array $options Contains any options that need to be set in the class initialization for construct. These are the keys:
51
- *
52
- * @key integer $check_period How often to check for updates (in hours). Defaults to checking every 12 hours. Set to 0 to disable automatic update checks.
53
- * @key string $pue_option_name Where to store book-keeping info about update checks. Defaults to 'external_updates-$slug'.
54
- * @key string $apikey used to authorize download updates from developer server
55
  *
 
 
 
 
 
 
 
 
56
  * @param string $plugin_file fully qualified path to the main plugin file.
57
  */
58
  public function __construct( $pue_update_url, $slug = '', $options = array(), $plugin_file = '' ) {
59
-
60
  $this->set_slug( $slug );
61
  $this->set_pue_update_url( $pue_update_url );
62
  $this->set_plugin_file( $plugin_file );
63
  $this->set_options( $options );
64
  $this->hooks();
65
-
66
  }
67
 
68
  /**
@@ -72,11 +173,12 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
72
  */
73
  public function hooks() {
74
  // Override requests for plugin information
75
- add_filter( 'plugins_api', array( &$this, 'inject_info' ), 10, 3 );
76
 
77
  // Check for updates when the WP updates are checked and inject our update if needed.
78
- // Only add filter if the TRIBE_DISABLE_PUE constant is not set as true.
79
- if ( ! defined( 'TRIBE_DISABLE_PUE' ) || TRIBE_DISABLE_PUE !== true ) {
 
80
  add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_for_updates' ) );
81
  }
82
 
@@ -84,14 +186,15 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
84
  add_action( 'tribe_license_fields', array( $this, 'do_license_key_fields' ) );
85
  add_action( 'tribe_settings_after_content_tab_licenses', array( $this, 'do_license_key_javascript' ) );
86
  add_action( 'tribe_settings_success_message', array( $this, 'do_license_key_success_message' ), 10, 2 );
 
 
 
87
 
88
  // Key validation
89
  add_action( 'wp_ajax_pue-validate-key_' . $this->get_slug(), array( $this, 'ajax_validate_key' ) );
90
-
91
- // Dashboard message "dismiss upgrade" link
92
- add_action( 'wp_ajax_' . $this->dismiss_upgrade, array( $this, 'dashboard_dismiss_upgrade' ) );
93
-
94
  add_filter( 'tribe-pue-install-keys', array( $this, 'return_install_key' ) );
 
 
95
  }
96
 
97
  /********************** Getter / Setter Functions **********************/
@@ -181,7 +284,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
181
 
182
  $plugin_details = explode( '/', $this->get_plugin_file() );
183
  $plugin_folder = get_plugins( '/' . $plugin_details[0] );
184
- $this->plugin_name = $plugin_folder[ $plugin_details[1] ]['Name'];
185
  }
186
  }
187
 
@@ -211,12 +314,15 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
211
  'apikey' => '',
212
  'installkey' => false,
213
  'check_period' => 12,
 
214
  )
215
  );
216
 
217
  $this->pue_option_name = $options['pue_option_name'];
218
  $this->check_period = (int) $options['check_period'];
219
  $this->api_secret_key = $options['apikey'];
 
 
220
  if ( isset( $options['installkey'] ) && $options['installkey'] ) {
221
  $this->install_key = trim( $options['installkey'] );
222
  } else {
@@ -325,16 +431,27 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
325
  ?>
326
  <script>
327
  jQuery(document).ready(function ($) {
 
 
 
 
 
 
 
 
 
328
  $('#tribe-field-<?php echo $this->pue_install_key ?>').change(function () {
329
  <?php echo $this->pue_install_key ?>_validateKey();
330
  });
331
  <?php echo $this->pue_install_key ?>_validateKey();
332
  });
 
333
  function <?php echo $this->pue_install_key ?>_validateKey() {
334
  var this_id = '#tribe-field-<?php echo $this->pue_install_key ?>';
335
  var $validity_msg = jQuery(this_id + ' .key-validity');
336
 
337
  if (jQuery(this_id + ' input').val() != '') {
 
338
  jQuery(this_id + ' .tooltip').hide();
339
  jQuery(this_id + ' .ajax-loading-license').show();
340
  $validity_msg.hide();
@@ -360,7 +477,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
360
  }
361
  }
362
  </script>
363
- <?php
364
  }
365
 
366
  /**
@@ -381,147 +498,276 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
381
 
382
  }
383
 
384
- /**
385
- * Echo JSON results for key validation
386
- */
387
- public function ajax_validate_key() {
388
  $response = array();
389
  $response['status'] = 0;
390
- if ( isset( $_POST['key'] ) ) {
391
 
392
- $queryArgs = array(
393
- 'pu_install_key' => trim( $_POST['key'] ),
394
- 'pu_checking_for_updates' => '1',
395
- );
396
 
397
- //include version info
398
- $queryArgs['pue_active_version'] = $this->get_installed_version();
 
 
399
 
400
- global $wp_version;
401
- $queryArgs['wp_version'] = $wp_version;
402
 
403
- // For multisite, return the network-level siteurl ... in
404
- // all other cases return the actual URL being serviced
405
- $queryArgs['domain'] = is_multisite() ? $this->get_network_domain() : $_SERVER['SERVER_NAME'];
406
 
407
- if ( is_multisite() ) {
408
- $queryArgs['multisite'] = 1;
409
- $queryArgs['network_activated'] = is_plugin_active_for_network( $this->get_plugin_file() );
410
- global $wpdb;
411
- $queryArgs['active_sites'] = $wpdb->get_var( "SELECT count(blog_id) FROM $wpdb->blogs WHERE public = '1' AND archived = '0' AND spam = '0' AND deleted = '0'" );
412
- } else {
413
- $queryArgs['multisite'] = 0;
414
- $queryArgs['network_activated'] = 0;
415
- $queryArgs['active_sites'] = 1;
416
- }
 
 
 
 
417
 
418
- $pluginInfo = $this->request_info( $queryArgs );
419
- $expiration = isset( $pluginInfo->expiration ) ? $pluginInfo->expiration : esc_html__( 'unknown date', 'tribe-common' );
420
-
421
- if ( empty( $pluginInfo ) ) {
422
- $response['message'] = esc_html__( 'Sorry, key validation server is not available.', 'tribe-common' );
423
- } elseif ( isset( $pluginInfo->api_expired ) && $pluginInfo->api_expired == 1 ) {
424
- $response['message'] = $this->get_api_message( $pluginInfo );
425
- } elseif ( isset( $pluginInfo->api_upgrade ) && $pluginInfo->api_upgrade == 1 ) {
426
- $response['message'] = $this->get_api_message( $pluginInfo );
427
- } elseif ( isset( $pluginInfo->api_invalid ) && $pluginInfo->api_invalid == 1 ) {
428
- $response['message'] = $this->get_api_message( $pluginInfo );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
429
  } else {
430
- $api_secret_key = get_option( $this->pue_install_key );
431
- if ( $api_secret_key && $api_secret_key === $queryArgs['pu_install_key'] ){
432
- $default_success_msg = sprintf( esc_html__( 'Valid Key! Expires on %s', 'tribe-common' ), $expiration );
433
- } else {
434
- // Set the key
435
- update_option( $this->pue_install_key, $queryArgs['pu_install_key'] );
436
-
437
- $default_success_msg = sprintf( esc_html__( 'Thanks for setting up a valid key, it will expire on %s', 'tribe-common' ), $expiration );
438
- }
439
 
440
- $response['status'] = isset( $pluginInfo->api_message ) ? 2 : 1;
441
- $response['message'] = isset( $pluginInfo->api_message ) ? wp_kses( $pluginInfo->api_message, 'data' ) : $default_success_msg;
442
- $response['expiration'] = $expiration;
 
 
443
  }
444
- } else {
445
- $response['message'] = sprintf( esc_html__( 'Hmmm... something\'s wrong with this validator. Please contact %ssupport%s.', 'tribe-common' ), '<a href="http://m.tri.be/1u">', '</a>' );
 
 
 
 
446
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
  echo json_encode( $response );
448
  exit;
449
  }
450
 
451
  /**
452
- * processes variable substitutions for server-side API message
 
 
 
 
453
  */
454
  private function get_api_message( $info ) {
455
  // this default message should never show, but is here as a fallback just in case.
456
  $message = sprintf(
457
- esc_html__( 'Sorry, there is a problem with your license key. You\'ll need to %scheck your license%s to have access to updates, downloads, and support.', 'tribe-common' ),
 
458
  '<a href="https://theeventscalendar.com/license-keys/">',
459
  '</a>'
460
  );
461
 
462
- if ( ! empty( $info->api_invalid_message ) ) {
463
- $message = wp_kses( $info->api_invalid_message, 'post' );
464
  }
465
 
466
- $message = str_replace( '%plugin_name%', '<b>' . $this->get_plugin_name() . '</b>', $message );
467
  $message = str_replace( '%plugin_slug%', $this->get_slug(), $message );
468
  $message = str_replace( '%update_url%', $this->get_pue_update_url(), $message );
469
  $message = str_replace( '%version%', $info->version, $message );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
470
 
471
  return $message;
472
  }
473
 
474
  /**
475
- * Echo JSON formatted errors
 
 
476
  */
477
- public function display_json_error() {
478
- $pluginInfo = $this->json_error;
479
- $update_dismissed = $this->get_option( $this->dismiss_upgrade );
 
480
 
481
- $is_dismissed = ! empty( $update_dismissed ) && in_array( $pluginInfo->version, $update_dismissed ) ? true : false;
482
 
483
- if ( $is_dismissed ) {
484
  return;
485
  }
486
 
487
- if ( ! current_user_can( 'administrator' ) ) {
488
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  }
490
 
491
- //only display messages if there is a new version of the plugin.
492
- if ( version_compare( $pluginInfo->version, $this->get_installed_version(), '>' ) ) {
493
- if ( empty( $pluginInfo->api_invalid ) || $pluginInfo->api_invalid != 1 ) {
494
- return;
495
- }
496
 
497
- $msg = $this->get_api_message( $pluginInfo );
498
-
499
- //Dismiss code idea below is obtained from the Gravity Forms Plugin by rocketgenius.com
500
- ?>
501
- <div class="updated" style="padding:5px; position:relative;" id="pu_dashboard_message"><?php echo wp_kses( $msg, 'post' ); ?>
502
- <a href="javascript:void(0);" onclick="PUDismissUpgrade();" style="float:right;">[X]</a>
503
- </div>
504
- <script type="text/javascript">
505
- function PUDismissUpgrade() {
506
- jQuery("#pu_dashboard_message").slideUp();
507
- jQuery.post( ajaxurl, {
508
- action: "<?php echo esc_attr( $this->dismiss_upgrade ); ?>",
509
- version: "<?php echo esc_attr( $pluginInfo->version ); ?>",
510
- cookie: encodeURIComponent(document.cookie)
511
- } );
512
- }
513
- </script>
514
- <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515
  }
516
  }
517
 
518
  /**
519
  * Retrieve plugin info from the configured API endpoint.
520
  *
521
- * @param array $queryArgs Additional query arguments to append to the request. Optional.
 
 
 
522
  *
523
  * @uses wp_remote_get()
524
- * @return string $pluginInfo
 
 
 
 
525
  */
526
  public function request_info( $queryArgs = array() ) {
527
  //Query args to append to the URL. Plugins can add their own by using a filter callback (see add_query_arg_filter()).
@@ -543,7 +789,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
543
  $queryArgs['wp_version'] = $wp_version;
544
 
545
  //include domain and multisite stats
546
- $queryArgs['domain'] = is_multisite() ? $this->get_network_domain() : $_SERVER['SERVER_NAME'];
547
 
548
  if ( is_multisite() ) {
549
  $queryArgs['multisite'] = 1;
@@ -586,15 +832,15 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
586
  );
587
 
588
  //Try to parse the response
589
- $pluginInfo = null;
590
  if ( ! is_wp_error( $result ) && isset( $result['response']['code'] ) && ( $result['response']['code'] == 200 ) && ! empty( $result['body'] ) ) {
591
- $pluginInfo = Tribe__PUE__Plugin_Info::from_json( $result['body'] );
592
  }
593
- $pluginInfo = apply_filters( 'tribe_puc_request_info_result-' . $this->get_slug(), $pluginInfo, $result );
594
 
595
- $plugin_info_cache[ $key ] = $pluginInfo;
596
 
597
- return $pluginInfo;
598
  }
599
 
600
  /**
@@ -619,59 +865,48 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
619
  * @return Tribe__PUE__Utility An instance of Tribe__PUE__Utility, or NULL when no updates are available.
620
  */
621
  public function request_update() {
622
- //For the sake of simplicity, this function just calls request_info()
623
- //and transforms the result accordingly.
624
- $pluginInfo = $this->request_info( array( 'pu_checking_for_updates' => '1' ) );
625
- if ( $pluginInfo == null ) {
626
- return null;
 
 
 
 
 
627
  }
628
- //admin display for if the update check reveals that there is a new version but the API key isn't valid.
629
- if ( isset( $pluginInfo->api_invalid ) ) { //we have json_error returned let's display a message
630
- $this->json_error = $pluginInfo;
631
- add_action( 'admin_notices', array( &$this, 'display_json_error' ) );
632
 
 
 
 
633
  return null;
634
  }
635
 
636
- if ( isset( $pluginInfo->new_install_key ) ) {
637
- $this->update_option( $this->pue_install_key, $pluginInfo->new_install_key );
 
 
 
 
 
 
 
638
  }
639
 
640
  //need to correct the download url so it contains the custom user data (i.e. api and any other paramaters)
641
 
642
  $download_query = $this->get_download_query();
643
  if ( ! empty( $download_query ) ) {
644
- $pluginInfo->download_url = esc_url_raw( add_query_arg( $download_query, $pluginInfo->download_url ) );
645
  }
646
 
647
  // Add plugin dirname/file (this will be expected by WordPress when it builds the plugin list table)
648
- $pluginInfo->plugin = $this->get_plugin_file();
649
-
650
- return Tribe__PUE__Utility::from_plugin_info( $pluginInfo );
651
- }
652
-
653
 
654
- /**
655
- * Display the upgrade message in the plugin list under the plugin.
656
- *
657
- * @param $plugin_data
658
- */
659
- public function in_plugin_update_message( $plugin_data ) {
660
- $plugininfo = $this->json_error;
661
- //only display messages if there is a new version of the plugin.
662
- if ( is_object( $plugininfo ) && version_compare( $plugininfo->version, $this->get_installed_version(), '>' ) ) {
663
- if ( $plugininfo->api_invalid ) {
664
- $msg = str_replace( '%plugin_name%', '<strong>' . $this->get_plugin_name() . '</strong>', $plugininfo->api_inline_invalid_message );
665
- $msg = str_replace( '%plugin_slug%', $this->get_slug(), $msg );
666
- $msg = str_replace( '%update_url%', $this->get_pue_update_url(), $msg );
667
- $msg = str_replace( '%version%', $plugininfo->version, $msg );
668
- $msg = str_replace( '%changelog%', '<a class="thickbox" title="' . $this->get_plugin_name() . '" href="plugin-install.php?tab=plugin-information&plugin=' . $this->get_slug() . '&TB_iframe=true&width=640&height=808">what\'s new</a>', $msg );
669
- echo '</tr><tr class="plugin-update-tr"><td colspan="3" class="plugin-update"><div class="update-message">' . $msg . '</div></td>';
670
- }
671
- }
672
  }
673
 
674
-
675
  /**
676
  * Display a changelog when the api key is missing.
677
  */
@@ -679,19 +914,6 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
679
  //contents of changelog display page when api-key is invalid or missing. It will ONLY show the changelog (hook into existing thickbox?)
680
  }
681
 
682
- /**
683
- * Update option to dismiss the upgrade notice.
684
- */
685
- public function dashboard_dismiss_upgrade() {
686
- $os_ary = $this->get_option( $this->dismiss_upgrade );
687
- if ( ! is_array( $os_ary ) ) {
688
- $os_ary = array();
689
- }
690
-
691
- $os_ary[] = $_POST['version'];
692
- $this->update_option( $this->dismiss_upgrade, $os_ary );
693
- }
694
-
695
  /**
696
  * Get the currently installed version of the plugin.
697
  *
@@ -711,11 +933,12 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
711
  *
712
  * @param string $option_key
713
  * @param bool|mixed $default
 
714
  *
715
  * @return null|mixed
716
  */
717
- public function get_option( $option_key, $default = false ) {
718
- return get_site_option( $option_key, $default );
719
  }
720
 
721
  /**
@@ -737,7 +960,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
737
  *
738
  */
739
  public function check_for_updates( $updates = array() ) {
740
- $state = $this->get_option( $this->pue_option_name );
741
  if ( empty( $state ) ) {
742
  $state = new StdClass;
743
  $state->lastCheck = 0;
@@ -753,20 +976,53 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
753
 
754
  // If a null update was returned, skip the end of the function.
755
  if ( $state->update == null ) {
 
756
  return $updates;
757
  }
758
 
759
  //Is there an update to insert?
760
  if ( version_compare( $state->update->version, $this->get_installed_version(), '>' ) ) {
 
 
 
761
  $updates->response[ $this->get_plugin_file() ] = $state->update->to_wp_format();
 
 
 
 
 
762
  }
763
 
764
  $this->update_option( $this->pue_option_name, $state );
765
- add_action( 'after_plugin_row_' . $this->get_plugin_file(), array( &$this, 'in_plugin_update_message' ) );
766
 
767
  return $updates;
768
  }
769
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
770
  /**
771
  * Intercept plugins_api() calls that request information about our plugin and
772
  * use the configured API endpoint to satisfy them.
@@ -785,9 +1041,9 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
785
  return $result;
786
  }
787
 
788
- $pluginInfo = $this->request_info( array( 'pu_checking_for_updates' => '1' ) );
789
- if ( $pluginInfo ) {
790
- return $pluginInfo->to_wp_format();
791
  }
792
 
793
  return $result;
@@ -880,5 +1136,35 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
880
 
881
  return $keys;
882
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
883
  }
884
  }
17
  /**
18
  * A custom plugin update checker.
19
  *
20
+ * @since 1.7
 
 
 
 
 
 
 
21
  */
22
  class Tribe__PUE__Checker {
23
 
24
+ /**
25
+ * The URL of the plugin's metadata file.
26
+ *
27
+ * @var string
28
+ */
29
+ private $pue_update_url = '';
30
+
31
+ /**
32
+ * Plugin filename relative to the plugins directory.
33
+ *
34
+ * @var string
35
+ */
36
+ private $plugin_file = '';
37
+
38
+ /**
39
+ * Used to hold the plugin_name as set by the constructor.
40
+ *
41
+ * @var string
42
+ */
43
+ private $plugin_name = '';
44
+
45
+ /**
46
+ * The plugin slug (without the .php extension)
47
+ *
48
+ * @var string
49
+ */
50
+ protected $plugin_slug;
51
+
52
+ /**
53
+ * Plugin slug. (with .php extension)
54
+ *
55
+ * @var string
56
+ */
57
+ private $slug = '';
58
+
59
+ /**
60
+ * Used to hold the query variables for download checks
61
+ *
62
+ * @var array
63
+ */
64
+ private $download_query = array();
65
+
66
+ /**
67
+ * The context in which this license key is used. May be 'component'
68
+ * in the case of a downloadable set of files such as a plugin or
69
+ * theme or else 'service' if the license key is used to utilize a
70
+ * remote SaaS platform.
71
+ *
72
+ * @var string
73
+ */
74
+ private $context = 'component';
75
+
76
+ /**
77
+ * How often to check for updates (in hours).
78
+ *
79
+ * @var int
80
+ */
81
+ public $check_period = 12;
82
+
83
+ /**
84
+ * Where to store the update info.
85
+ *
86
+ * @var string
87
+ */
88
+ public $pue_option_name = '';
89
+
90
+ /**
91
+ * used to hold the user API. If not set then nothing will work!
92
+ *
93
+ * @var string
94
+ */
95
+ public $api_secret_key = '';
96
+
97
+ /**
98
+ * used to hold the install_key if set (included here for addons that will extend PUE to use install key checks)
99
+ *
100
+ * @var bool
101
+ */
102
+ public $install_key = false;
103
+
104
+ /**
105
+ * For setting the dismiss upgrade option (per plugin).
106
+ *
107
+ * @var
108
+ */
109
+ public $dismiss_upgrade;
110
+
111
+ /**
112
+ * We'll customize this later so each plugin can have it's own install key!
113
+ *
114
+ * @var string
115
+ */
116
+ public $pue_install_key;
117
 
118
+ /**
119
+ * Storing any `json_error` data that get's returned so we can display an admin notice.
120
+ * For backwards compatibility this will be kept in the code for 2 versions
121
+ *
122
+ * @var array|null
123
+ *
124
+ * @deprecated
125
+ * @todo remove on 4.5
126
+ */
127
+ public $json_error;
128
+
129
+ /**
130
+ * Storing any `plugin_info` data that get's returned so we can display an admin notice.
131
+ *
132
+ * @var array|null
133
+ */
134
+ public $plugin_info;
135
+
136
+ /**
137
+ * Storing the `plugin_notice` message.
138
+ *
139
+ * @var string
140
+ */
141
+ public $plugin_notice;
142
 
143
  /**
144
  * Class constructor.
145
  *
146
  * @param string $pue_update_url The URL of the plugin's metadata file.
147
  * @param string $slug The plugin's 'slug'.
148
+ * @param array $options {
149
+ * Contains any options that need to be set in the class initialization for construct.
 
 
 
150
  *
151
+ * @type integer $check_period How often to check for updates (in hours). Defaults to checking every
152
+ * 12 hours. Set to 0 to disable automatic update checks.
153
+ * @type string $pue_option_name Where to store book-keeping info about update checks. Defaults to
154
+ * 'external_updates-$slug'.
155
+ * @type string $apikey Used to authorize download updates from developer server
156
+ * @type string $context Defaults to 'component' which is expected for plugins (or themes).
157
+ * If set to 'service' it will not hook into WP update checks.
158
+ * }
159
  * @param string $plugin_file fully qualified path to the main plugin file.
160
  */
161
  public function __construct( $pue_update_url, $slug = '', $options = array(), $plugin_file = '' ) {
 
162
  $this->set_slug( $slug );
163
  $this->set_pue_update_url( $pue_update_url );
164
  $this->set_plugin_file( $plugin_file );
165
  $this->set_options( $options );
166
  $this->hooks();
 
167
  }
168
 
169
  /**
173
  */
174
  public function hooks() {
175
  // Override requests for plugin information
176
+ add_filter( 'plugins_api', array( $this, 'inject_info' ), 10, 3 );
177
 
178
  // Check for updates when the WP updates are checked and inject our update if needed.
179
+ // Only add filter if the TRIBE_DISABLE_PUE constant is not set as true and where
180
+ // the context is not 'service'
181
+ if ( ( ! defined( 'TRIBE_DISABLE_PUE' ) || TRIBE_DISABLE_PUE !== true ) && 'service' !== $this->context ) {
182
  add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_for_updates' ) );
183
  }
184
 
186
  add_action( 'tribe_license_fields', array( $this, 'do_license_key_fields' ) );
187
  add_action( 'tribe_settings_after_content_tab_licenses', array( $this, 'do_license_key_javascript' ) );
188
  add_action( 'tribe_settings_success_message', array( $this, 'do_license_key_success_message' ), 10, 2 );
189
+ add_action( 'load-plugins.php', array( $this, 'remove_default_inline_update_msg' ), 50 );
190
+ add_action( 'update_option_' . $this->pue_install_key, array( $this, 'check_for_api_key_error' ), 10, 2 );
191
+ add_action( 'update_site_option_' . $this->pue_install_key, array( $this, 'check_for_api_key_error' ), 10, 2 );
192
 
193
  // Key validation
194
  add_action( 'wp_ajax_pue-validate-key_' . $this->get_slug(), array( $this, 'ajax_validate_key' ) );
 
 
 
 
195
  add_filter( 'tribe-pue-install-keys', array( $this, 'return_install_key' ) );
196
+ add_action( 'admin_enqueue_scripts', array( $this, 'maybe_display_json_error_on_plugins_page' ), 1 );
197
+ add_action( 'admin_init', array( $this, 'general_notifications' ) );
198
  }
199
 
200
  /********************** Getter / Setter Functions **********************/
284
 
285
  $plugin_details = explode( '/', $this->get_plugin_file() );
286
  $plugin_folder = get_plugins( '/' . $plugin_details[0] );
287
+ $this->plugin_name = isset( $plugin_details[1] ) && isset( $plugin_folder[ $plugin_details[1] ] ) ? $plugin_folder[ $plugin_details[1] ]['Name'] : null;
288
  }
289
  }
290
 
314
  'apikey' => '',
315
  'installkey' => false,
316
  'check_period' => 12,
317
+ 'context' => 'component',
318
  )
319
  );
320
 
321
  $this->pue_option_name = $options['pue_option_name'];
322
  $this->check_period = (int) $options['check_period'];
323
  $this->api_secret_key = $options['apikey'];
324
+ $this->context = $options['context'];
325
+
326
  if ( isset( $options['installkey'] ) && $options['installkey'] ) {
327
  $this->install_key = trim( $options['installkey'] );
328
  } else {
431
  ?>
432
  <script>
433
  jQuery(document).ready(function ($) {
434
+ $( '.tribe-field-license_key' ).each( function() {
435
+ var $el = $( this );
436
+ var $field = $el.find( 'input' );
437
+
438
+ if ( '' === $.trim( $field.val() ) ) {
439
+ $el.find( '.license-test-results' ).hide();
440
+ }
441
+ } );
442
+
443
  $('#tribe-field-<?php echo $this->pue_install_key ?>').change(function () {
444
  <?php echo $this->pue_install_key ?>_validateKey();
445
  });
446
  <?php echo $this->pue_install_key ?>_validateKey();
447
  });
448
+
449
  function <?php echo $this->pue_install_key ?>_validateKey() {
450
  var this_id = '#tribe-field-<?php echo $this->pue_install_key ?>';
451
  var $validity_msg = jQuery(this_id + ' .key-validity');
452
 
453
  if (jQuery(this_id + ' input').val() != '') {
454
+ jQuery( this_id + ' .license-test-results' ).show();
455
  jQuery(this_id + ' .tooltip').hide();
456
  jQuery(this_id + ' .ajax-loading-license').show();
457
  $validity_msg.hide();
477
  }
478
  }
479
  </script>
480
+ <?php
481
  }
482
 
483
  /**
498
 
499
  }
500
 
501
+ public function validate_key( $key ) {
 
 
 
502
  $response = array();
503
  $response['status'] = 0;
 
504
 
505
+ if ( ! $key ) {
506
+ $response['message'] = sprintf( esc_html__( 'Hmmm... something\'s wrong with this validator. Please contact %ssupport%s.', 'tribe-common' ), '<a href="http://m.tri.be/1u">', '</a>' );
507
+ return $response;
508
+ }
509
 
510
+ $queryArgs = array(
511
+ 'pu_install_key' => trim( $key ),
512
+ 'pu_checking_for_updates' => '1',
513
+ );
514
 
515
+ //include version info
516
+ $queryArgs['pue_active_version'] = $this->get_installed_version();
517
 
518
+ global $wp_version;
519
+ $queryArgs['wp_version'] = $wp_version;
 
520
 
521
+ // For multisite, return the network-level siteurl ... in
522
+ // all other cases return the actual URL being serviced
523
+ $queryArgs['domain'] = is_multisite() ? $this->get_network_domain() : $_SERVER['SERVER_NAME'];
524
+
525
+ if ( is_multisite() ) {
526
+ $queryArgs['multisite'] = 1;
527
+ $queryArgs['network_activated'] = is_plugin_active_for_network( $this->get_plugin_file() );
528
+ global $wpdb;
529
+ $queryArgs['active_sites'] = $wpdb->get_var( "SELECT count(blog_id) FROM $wpdb->blogs WHERE public = '1' AND archived = '0' AND spam = '0' AND deleted = '0'" );
530
+ } else {
531
+ $queryArgs['multisite'] = 0;
532
+ $queryArgs['network_activated'] = 0;
533
+ $queryArgs['active_sites'] = 1;
534
+ }
535
 
536
+ // This method is primarily used during when validating keys by ajax, before they are
537
+ // formally committed or saved by the user: for that reason we call request_info()
538
+ // rather than license_key_status() as at this stage invalid or missing keys should
539
+ // not result in admin notices being generated
540
+ $plugin_info = $this->request_info( $queryArgs );
541
+ $expiration = isset( $plugin_info->expiration ) ? $plugin_info->expiration : esc_html__( 'unknown date', 'tribe-common' );
542
+
543
+ $pue_notices = Tribe__Main::instance()->pue_notices();
544
+ $plugin_name = $this->get_plugin_name();
545
+
546
+ if ( empty( $plugin_info ) ) {
547
+ $response['message'] = esc_html__( 'Sorry, key validation server is not available.', 'tribe-common' );
548
+ } elseif ( isset( $plugin_info->api_expired ) && $plugin_info->api_expired == 1 ) {
549
+ $response['message'] = $this->get_license_expired_message();
550
+ $response['api_expired'] = true;
551
+ } elseif ( isset( $plugin_info->api_upgrade ) && $plugin_info->api_upgrade == 1 ) {
552
+ $response['message'] = $this->get_api_message( $plugin_info );
553
+ $response['api_upgrade'] = true;
554
+ } elseif ( isset( $plugin_info->api_invalid ) && $plugin_info->api_invalid == 1 ) {
555
+ $response['message'] = $this->get_api_message( $plugin_info );
556
+ $response['api_invalid'] = true;
557
+ } else {
558
+ $api_secret_key = get_option( $this->pue_install_key );
559
+ if ( $api_secret_key && $api_secret_key === $queryArgs['pu_install_key'] ){
560
+ $default_success_msg = sprintf( esc_html__( 'Valid Key! Expires on %s', 'tribe-common' ), $expiration );
561
  } else {
562
+ // Set the key
563
+ update_option( $this->pue_install_key, $queryArgs['pu_install_key'] );
564
+
565
+ $default_success_msg = sprintf( esc_html__( 'Thanks for setting up a valid key. It will expire on %s', 'tribe-common' ), $expiration );
 
 
 
 
 
566
 
567
+ //Set SysInfo Key on Tec.com After Successful Validation of License
568
+ $optin_key = get_option( 'tribe_systeminfo_optin' );
569
+ if ( $optin_key ) {
570
+ Tribe__Support::send_sysinfo_key( $optin_key, $queryArgs['domain'], false, true );
571
+ }
572
  }
573
+
574
+ $pue_notices->clear_notices( $plugin_name );
575
+
576
+ $response['status'] = isset( $plugin_info->api_message ) ? 2 : 1;
577
+ $response['message'] = isset( $plugin_info->api_message ) ? wp_kses( $plugin_info->api_message, 'data' ) : $default_success_msg;
578
+ $response['expiration'] = $expiration;
579
  }
580
+
581
+ return $response;
582
+ }
583
+
584
+ public function get_license_expired_message() {
585
+ return '<a href="http://m.tri.be/195y" target="_blank" class="button button-primary">' .
586
+ __( 'Renew Your License Now', 'tribe-common' ) .
587
+ '<span class="screen-reader-text">' .
588
+ __( ' (opens in a new window)', 'tribe-common' ) .
589
+ '</span></a>';
590
+ }
591
+
592
+ /**
593
+ * Echo JSON results for key validation
594
+ */
595
+ public function ajax_validate_key() {
596
+ $key = isset( $_POST['key'] ) ? $_POST['key'] : null;
597
+
598
+ $response = $this->validate_key( $key );
599
+
600
  echo json_encode( $response );
601
  exit;
602
  }
603
 
604
  /**
605
+ * Processes variable substitutions for server-side API message.
606
+ *
607
+ * @param Tribe__PUE__Plugin_Info $info
608
+ *
609
+ * @return string
610
  */
611
  private function get_api_message( $info ) {
612
  // this default message should never show, but is here as a fallback just in case.
613
  $message = sprintf(
614
+ esc_html__( 'There is an update for %s. You\'ll need to %scheck your license%s to have access to updates, downloads, and support.', 'tribe-common' ),
615
+ $this->get_plugin_name(),
616
  '<a href="https://theeventscalendar.com/license-keys/">',
617
  '</a>'
618
  );
619
 
620
+ if ( ! empty( $info->api_inline_invalid_message ) ) {
621
+ $message = wp_kses( $info->api_inline_invalid_message, 'post' );
622
  }
623
 
624
+ $message = str_replace( '%plugin_name%', $this->get_plugin_name(), $message );
625
  $message = str_replace( '%plugin_slug%', $this->get_slug(), $message );
626
  $message = str_replace( '%update_url%', $this->get_pue_update_url(), $message );
627
  $message = str_replace( '%version%', $info->version, $message );
628
+ $message = str_replace( '%changelog%', '<a class="thickbox" title="' . $this->get_plugin_name() . '" href="plugin-install.php?tab=plugin-information&plugin=' . $this->get_slug() . '&TB_iframe=true&width=640&height=808">what\'s new</a>', $message );
629
+
630
+ return $message;
631
+ }
632
+
633
+ private function get_api_update_message() {
634
+ $plugin_info = $this->plugin_info;
635
+
636
+ if ( ! isset( $plugin_info->api_invalid_message ) ) {
637
+ return false;
638
+ }
639
+
640
+ $message = sprintf(
641
+ esc_html__( 'There is an update for %s. %sRenew your license%s to get access to bug fixes, security updates, and new features.', 'tribe-common' ),
642
+ $this->get_plugin_name(),
643
+ '<a href="https://theeventscalendar.com/license-keys/">',
644
+ '</a>'
645
+ );
646
 
647
  return $message;
648
  }
649
 
650
  /**
651
+ * Displays a PUE message on the page if it is relevant
652
+ *
653
+ * @param string $page
654
  */
655
+ public function maybe_display_json_error_on_plugins_page( $page ) {
656
+ if ( 'plugins.php' !== $page ) {
657
+ return;
658
+ }
659
 
660
+ $state = $this->get_option( $this->pue_option_name, false, false );
661
 
662
+ if ( empty( $state->update->license_error ) ) {
663
  return;
664
  }
665
 
666
+ $this->plugin_notice = array(
667
+ 'slug' => $this->get_slug(),
668
+ 'message_row_html' => "
669
+ <tr class='plugin-update-tr active'> <td colspan='3' class='plugin-update'>
670
+ <div class='update-message notice inline notice-warning notice-alt'>
671
+ {$state->update->license_error}
672
+ </div>
673
+ </td> </tr>
674
+ ",
675
+ );
676
+
677
+ add_filter( 'tribe_plugin_notices', array( $this, 'add_notice_to_plugin_notices' ) );
678
+ }
679
+
680
+ public function add_notice_to_plugin_notices( $notices ) {
681
+ if ( ! $this->plugin_notice ) {
682
+ return $notices;
683
  }
684
 
685
+ $notices[ $this->plugin_notice['slug'] ] = $this->plugin_notice;
 
 
 
 
686
 
687
+ return $notices;
688
+ }
689
+
690
+ /**
691
+ * Returns plugin/license key data based on the provided query arguments.
692
+ *
693
+ * Calling this method will also take care of setting up admin notices for any
694
+ * keys that are invalid or have expired, etc.
695
+ *
696
+ * @see Tribe__PUE__Checker::request_info()
697
+ *
698
+ * @param $query_args
699
+ *
700
+ * @return Tribe__PUE__Plugin_Info|null
701
+ */
702
+ public function license_key_status( $query_args ) {
703
+ $pue_notices = Tribe__Main::instance()->pue_notices();
704
+ $plugin_info = $this->request_info( $query_args );
705
+ $plugin_name = empty( $this->plugin_name ) ? $this->get_plugin_name() : $this->plugin_name;
706
+
707
+ if ( empty( $plugin_name ) ) {
708
+ return $plugin_info;
709
+ }
710
+
711
+ // Check for expired keys
712
+ if ( ! empty( $plugin_info->api_expired ) ) {
713
+ $pue_notices->add_notice( Tribe__PUE__Notices::EXPIRED_KEY, $plugin_name );
714
+ }
715
+ // Check for keys that are out of installs (*must* happen before the api_invalid test)
716
+ elseif ( ! empty( $plugin_info->api_upgrade ) ) {
717
+ $pue_notices->add_notice( Tribe__PUE__Notices::UPGRADE_KEY, $plugin_name );
718
+ }
719
+ // Check for invalid keys last of all (upgrades/empty keys will be flagged as invalid)
720
+ elseif (
721
+ ! empty( $plugin_info->api_invalid )
722
+ && (
723
+ 'component' === $this->context
724
+ || (
725
+ 'service' === $this->context
726
+ && $this->install_key
727
+ )
728
+ )
729
+ ) {
730
+ $pue_notices->add_notice( Tribe__PUE__Notices::INVALID_KEY, $plugin_name );
731
+ }
732
+ // If none of the above were satisfied we can assume the key is valid
733
+ else {
734
+ $pue_notices->clear_notices( $plugin_name );
735
+ }
736
+
737
+ return $plugin_info;
738
+ }
739
+
740
+ /**
741
+ * Sets up and manages those license key notifications which don't depend on communicating with a remote
742
+ * PUE server, etc.
743
+ */
744
+ public function general_notifications() {
745
+ $plugin_name = empty( $this->plugin_name ) ? $this->get_plugin_name() : $this->plugin_name;
746
+
747
+ // Register our plugin name for use in messages (thus if we're deactivated, any previously
748
+ // added persistent messaging can be cleared)
749
+ Tribe__Main::instance()->pue_notices()->register_name( $plugin_name );
750
+
751
+ // Detect and setup notices for missing keys
752
+ if ( empty( $this->install_key ) && 'service' !== $this->context ) {
753
+ Tribe__Main::instance()->pue_notices()->add_notice( Tribe__PUE__Notices::INVALID_KEY, $plugin_name );
754
  }
755
  }
756
 
757
  /**
758
  * Retrieve plugin info from the configured API endpoint.
759
  *
760
+ * In general, this method should not be called directly and it is preferable to call
761
+ * the license_key_status() method instead. That method returns the same result, but
762
+ * also analyses each response to set up appropriate license key notifications in the
763
+ * admin environment.
764
  *
765
  * @uses wp_remote_get()
766
+ * @see Tribe__PUE__Checker::license_key_status()
767
+ *
768
+ * @param array $queryArgs Additional query arguments to append to the request. Optional.
769
+ *
770
+ * @return string $plugin_info
771
  */
772
  public function request_info( $queryArgs = array() ) {
773
  //Query args to append to the URL. Plugins can add their own by using a filter callback (see add_query_arg_filter()).
789
  $queryArgs['wp_version'] = $wp_version;
790
 
791
  //include domain and multisite stats
792
+ $queryArgs['domain'] = is_multisite() ? $this->get_network_domain() : $this->get_site_domain();
793
 
794
  if ( is_multisite() ) {
795
  $queryArgs['multisite'] = 1;
832
  );
833
 
834
  //Try to parse the response
835
+ $plugin_info = null;
836
  if ( ! is_wp_error( $result ) && isset( $result['response']['code'] ) && ( $result['response']['code'] == 200 ) && ! empty( $result['body'] ) ) {
837
+ $plugin_info = Tribe__PUE__Plugin_Info::from_json( $result['body'] );
838
  }
839
+ $plugin_info = apply_filters( 'tribe_puc_request_info_result-' . $this->get_slug(), $plugin_info, $result );
840
 
841
+ $plugin_info_cache[ $key ] = $plugin_info;
842
 
843
+ return $plugin_info;
844
  }
845
 
846
  /**
865
  * @return Tribe__PUE__Utility An instance of Tribe__PUE__Utility, or NULL when no updates are available.
866
  */
867
  public function request_update() {
868
+ // For the sake of simplicity, this function just calls request_info()
869
+ // and transforms the result accordingly.
870
+ $args = array(
871
+ 'pu_checking_for_updates' => 1,
872
+ );
873
+
874
+ if ( ! empty( $_POST['key'] ) ) {
875
+ $args['pu_install_key'] = $_POST['key'];
876
+ } elseif ( ! empty( $_POST[ $this->pue_install_key ] ) ) {
877
+ $args['pu_install_key'] = $_POST[ $this->pue_install_key ];
878
  }
 
 
 
 
879
 
880
+ $this->plugin_info = $plugin_info = $this->license_key_status( $args );
881
+
882
+ if ( null === $plugin_info ) {
883
  return null;
884
  }
885
 
886
+ // admin display for if the update check reveals that there is a new version but the API key isn't valid.
887
+ if ( isset( $plugin_info->api_invalid ) ) {
888
+ $plugin_info = Tribe__PUE__Utility::from_plugin_info( $plugin_info );
889
+ $plugin_info->license_error = $this->get_api_message( $plugin_info );
890
+ return $plugin_info;
891
+ }
892
+
893
+ if ( isset( $plugin_info->new_install_key ) ) {
894
+ $this->update_option( $this->pue_install_key, $plugin_info->new_install_key );
895
  }
896
 
897
  //need to correct the download url so it contains the custom user data (i.e. api and any other paramaters)
898
 
899
  $download_query = $this->get_download_query();
900
  if ( ! empty( $download_query ) ) {
901
+ $plugin_info->download_url = esc_url_raw( add_query_arg( $download_query, $plugin_info->download_url ) );
902
  }
903
 
904
  // Add plugin dirname/file (this will be expected by WordPress when it builds the plugin list table)
905
+ $plugin_info->plugin = $this->get_plugin_file();
 
 
 
 
906
 
907
+ return Tribe__PUE__Utility::from_plugin_info( $plugin_info );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
908
  }
909
 
 
910
  /**
911
  * Display a changelog when the api key is missing.
912
  */
914
  //contents of changelog display page when api-key is invalid or missing. It will ONLY show the changelog (hook into existing thickbox?)
915
  }
916
 
 
 
 
 
 
 
 
 
 
 
 
 
 
917
  /**
918
  * Get the currently installed version of the plugin.
919
  *
933
  *
934
  * @param string $option_key
935
  * @param bool|mixed $default
936
+ * @param bool $use_cache
937
  *
938
  * @return null|mixed
939
  */
940
+ public function get_option( $option_key, $default = false, $use_cache = true ) {
941
+ return get_site_option( $option_key, $default, $use_cache );
942
  }
943
 
944
  /**
960
  *
961
  */
962
  public function check_for_updates( $updates = array() ) {
963
+ $state = $this->get_option( $this->pue_option_name, false, false );
964
  if ( empty( $state ) ) {
965
  $state = new StdClass;
966
  $state->lastCheck = 0;
976
 
977
  // If a null update was returned, skip the end of the function.
978
  if ( $state->update == null ) {
979
+ $this->update_option( $this->pue_option_name, $state );
980
  return $updates;
981
  }
982
 
983
  //Is there an update to insert?
984
  if ( version_compare( $state->update->version, $this->get_installed_version(), '>' ) ) {
985
+ if ( empty( $updates ) ) {
986
+ $updates = (object) array( 'response' => array() );
987
+ }
988
  $updates->response[ $this->get_plugin_file() ] = $state->update->to_wp_format();
989
+
990
+ // If the key has expired we should register an appropriate admin notice
991
+ if ( $this->plugin_info->api_expired ) {
992
+ Tribe__Main::instance()->pue_notices()->add_notice( Tribe__PUE__Notices::EXPIRED_KEY, $this->plugin_name );
993
+ }
994
  }
995
 
996
  $this->update_option( $this->pue_option_name, $state );
 
997
 
998
  return $updates;
999
  }
1000
 
1001
+ /**
1002
+ * Clears out the site external site option and re-checks the license key
1003
+ */
1004
+ public function check_for_api_key_error( $old_value, $value ) {
1005
+ if ( 'service' !== $this->context ) {
1006
+ delete_site_option( $this->pue_option_name );
1007
+ $this->check_for_updates();
1008
+ }
1009
+
1010
+ // are we saving THIS PUE key to the options table?
1011
+ if ( empty( $_POST[ $this->pue_install_key ] ) || $value !== $_POST[ $this->pue_install_key ] ) {
1012
+ return;
1013
+ }
1014
+
1015
+ // if we are saving this PUE key, we need to make sure we update the license key notices
1016
+ // appropriately. Otherwise, we could have an invalid license key in place but the notices
1017
+ // aren't being thrown globally
1018
+ $args = array(
1019
+ 'pu_checking_for_updates' => 1,
1020
+ 'pu_install_key' => $_POST[ $this->pue_install_key ],
1021
+ );
1022
+
1023
+ $this->license_key_status( $args );
1024
+ }
1025
+
1026
  /**
1027
  * Intercept plugins_api() calls that request information about our plugin and
1028
  * use the configured API endpoint to satisfy them.
1041
  return $result;
1042
  }
1043
 
1044
+ $plugin_info = $this->license_key_status( array( 'pu_checking_for_updates' => '1' ) );
1045
+ if ( $plugin_info ) {
1046
+ return $plugin_info->to_wp_format();
1047
  }
1048
 
1049
  return $result;
1136
 
1137
  return $keys;
1138
  }
1139
+
1140
+ /**
1141
+ * Prevent the default inline update-available messages from appearing, as we
1142
+ * have implemented our own.
1143
+ *
1144
+ * @see resources/js/pue-notices.js
1145
+ */
1146
+ public function remove_default_inline_update_msg() {
1147
+ remove_action( "after_plugin_row_{$this->plugin_file}", 'wp_plugin_update_row' );
1148
+ }
1149
+
1150
+ /**
1151
+ * Returns the domain of the single site installation
1152
+ *
1153
+ * Will try to read it from the $_SERVER['SERVER_NAME'] variable
1154
+ * and fall back on the one contained in the siteurl option.
1155
+ *
1156
+ * @return string
1157
+ */
1158
+ protected function get_site_domain() {
1159
+ if ( isset( $_SERVER['SERVER_NAME'] ) ) {
1160
+ return $_SERVER['SERVER_NAME'];
1161
+ }
1162
+ $site_url = parse_url( get_option( 'siteurl' ) );
1163
+ if ( ! $site_url || ! isset( $site_url['host'] ) ) {
1164
+ return '';
1165
+ } else {
1166
+ return strtolower( $site_url['host'] );
1167
+ }
1168
+ }
1169
  }
1170
  }
common/src/Tribe/PUE/Notices.php ADDED
@@ -0,0 +1,373 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facilitates storage and display of license key warning notices.
4
+ *
5
+ * @internal
6
+ */
7
+ class Tribe__PUE__Notices {
8
+ const INVALID_KEY = 'invalid_key';
9
+ const UPGRADE_KEY = 'upgrade_key';
10
+ const EXPIRED_KEY = 'expired_key';
11
+ const STORE_KEY = 'tribe_pue_key_notices';
12
+
13
+ protected $registered = array();
14
+ protected $saved_notices = array();
15
+ protected $notices = array();
16
+
17
+ /**
18
+ * Sets up license key related admin notices.
19
+ */
20
+ public function __construct() {
21
+ $this->populate();
22
+ add_action( 'current_screen', array( $this, 'setup_notices' ) );
23
+ add_action( 'tribe_pue_notices_save_notices', array( $this, 'maybe_undismiss_notices' ) );
24
+ }
25
+
26
+ /**
27
+ * Registers a plugin name that should be used in license key notifications.
28
+ *
29
+ * If, on a given request, the name is not registered then the plugin name will not
30
+ * feature in any notifications. The benefit is that if a plugin is suddenly removed,
31
+ * it's name can be automatically dropped from any pre-registered persistent
32
+ * notifications.
33
+ *
34
+ * @param string $plugin_name
35
+ */
36
+ public function register_name( $plugin_name ) {
37
+ $this->registered[] = $plugin_name;
38
+ }
39
+
40
+ /**
41
+ * Restores plugins added on previous requests to the relevant notification
42
+ * groups.
43
+ */
44
+ protected function populate() {
45
+ $this->saved_notices = (array) get_option( self::STORE_KEY, array() );
46
+
47
+ if ( empty( $this->saved_notices ) ) {
48
+ return;
49
+ }
50
+
51
+ $this->notices = array_merge_recursive( $this->notices, $this->saved_notices );
52
+
53
+ // Cleanup
54
+ foreach ( $this->notices as $key => &$plugin_lists ) {
55
+ // Purge any elements that are not arrays
56
+ if ( ! is_array( $plugin_lists ) ) {
57
+ unset( $this->notices[ $key ] );
58
+ continue;
59
+ }
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Saves any license key notices already added.
65
+ */
66
+ public function save_notices() {
67
+ update_option( self::STORE_KEY, $this->notices );
68
+
69
+ /**
70
+ * Fires after PUE license key notices have been saved.
71
+ *
72
+ * @param array $current_notices
73
+ * @param array $previously_saved_notices
74
+ */
75
+ do_action( 'tribe_pue_notices_save_notices', $this->notices, $this->saved_notices );
76
+ }
77
+
78
+ /**
79
+ * Undismisses license key notifications where appropriate.
80
+ *
81
+ * The idea is that if an invalid key is detected for one or more plugins, we show a notification
82
+ * until a user dismisses it. That user will not then see the notification again unless or until
83
+ * an additional plugin name is added to the invalid key list.
84
+ *
85
+ * Example:
86
+ *
87
+ * - Notification listing "Eventbrite" and "Pro" keys as invalid shows
88
+ * - User X dismisses the notification
89
+ * - The "Pro" license is fixed/corrected - notification remains in a "dismissed" status for User X
90
+ * - "Filter Bar" is added to the list of invalid keys
91
+ * - The invalid key notification is undismissed, to make all users (including User X) aware of
92
+ * the problem re Filter Bar
93
+ */
94
+ public function maybe_undismiss_notices() {
95
+ foreach ( $this->notices as $notice_type => $plugin_list ) {
96
+ if ( is_array( $this->saved_notices ) && ! empty( $this->saved_notices[ $notice_type ] ) ) {
97
+ $new_plugins = array_diff_key( $this->notices[ $notice_type ], $this->saved_notices[ $notice_type ] );
98
+ } else {
99
+ $new_plugins = $this->notices[ $notice_type ];
100
+ }
101
+
102
+ if ( ! empty( $new_plugins ) ) {
103
+ Tribe__Admin__Notices::instance()->undismiss_for_all( 'pue_key-' . $notice_type );
104
+ }
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Used to include a plugin in a notification.
110
+ *
111
+ * For example, this could be used to add "My Plugin" to the expired license key
112
+ * notification by passing Tribe__PUE__Notices::EXPIRED_KEY as the second param.
113
+ *
114
+ * Plugins can only be added to one notification group at a time, so if a plugin
115
+ * was already added to the MISSING_KEY group and is subsequently added to the
116
+ * INVALID_KEY group, the previous entry (under MISSING_KEY) will be cleared.
117
+ *
118
+ * @param string $notice_type
119
+ * @param string $plugin_name
120
+ */
121
+ public function add_notice( $notice_type, $plugin_name ) {
122
+ $this->clear_notices( $plugin_name, true );
123
+ $this->notices[ $notice_type ][ $plugin_name ] = true;
124
+ $this->save_notices();
125
+ }
126
+
127
+ /**
128
+ * Returns whether or not a given plugin name has a specific notice
129
+ *
130
+ * @param string $plugin_name
131
+ * @param string|null $notice_type
132
+ *
133
+ * @return boolean
134
+ */
135
+ public function has_notice( $plugin_name, $notice_type = null ) {
136
+ if ( $notice_type ) {
137
+ return ! empty( $this->notices[ $notice_type ][ $plugin_name ] );
138
+ }
139
+
140
+ foreach ( $this->notices as $notice_type => $plugins ) {
141
+ if ( ! empty( $plugins[ $plugin_name ] ) ) {
142
+ return true;
143
+ }
144
+ }
145
+
146
+ return false;
147
+ }
148
+
149
+ /**
150
+ * Removes any notifications for the specified plugin.
151
+ *
152
+ * Useful when a valid license key is detected for a plugin, where previously
153
+ * it might have been included under a warning notification.
154
+ *
155
+ * If the optional second param is set to true then this change will not
156
+ * immediately be committed to storage (useful if we know this will happen in
157
+ * any case later on in the same request).
158
+ *
159
+ * @param string $plugin_name
160
+ * @param bool $defer_saving_change = false
161
+ */
162
+ public function clear_notices( $plugin_name, $defer_saving_change = false ) {
163
+ foreach ( $this->notices as $notice_type => &$list_of_plugins ) {
164
+ unset( $list_of_plugins[ $plugin_name ] );
165
+ }
166
+
167
+ if ( ! $defer_saving_change ) {
168
+ $this->save_notices();
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Tests to see if there are any extant notifications and renders them if so.
174
+ *
175
+ * This must run prior to Tribe__Admin__Notices::hook() (which currently runs during
176
+ * "current_screen" priority 20).
177
+ */
178
+ public function setup_notices() {
179
+ // Don't allow this to run multiple times
180
+ remove_action( 'current_screen', array( $this, 'setup_notices' ) );
181
+
182
+ // No need to display license key notices to users without appropriate capabilities
183
+ if ( ! current_user_can( 'install_plugins' ) ) {
184
+ return;
185
+ }
186
+
187
+ foreach ( $this->notices as $notice_type => $plugin_names ) {
188
+ if ( empty( $plugin_names ) ) {
189
+ continue;
190
+ }
191
+
192
+ $callback = array( $this, 'render_' . $notice_type );
193
+
194
+ if ( is_callable( $callback ) ) {
195
+ tribe_notice( 'pue_key-' . $notice_type, $callback, 'dismiss=1&type=warning' );
196
+ }
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Generate a notice listing any plugins for which license keys have been entered but
202
+ * are invalid (in the sense of not matching PUE server records or having been revoked
203
+ * rather than having expired which is handled separately).
204
+ *
205
+ * In the context of the plugin admin screen, will not render if the key-has-expired
206
+ * notice is also scheduled to display.
207
+ */
208
+ public function render_invalid_key() {
209
+ global $pagenow;
210
+
211
+ if ( 'plugins.php' === $pagenow && ! empty( $this->notices[ self::EXPIRED_KEY ] ) ) {
212
+ return;
213
+ }
214
+
215
+ $plugin_names = $this->get_formatted_plugin_names( self::INVALID_KEY );
216
+
217
+ if ( empty( $plugin_names ) ) {
218
+ return;
219
+ }
220
+
221
+ $prompt = sprintf( _n(
222
+ "It looks like you're using %s, but the license key you supplied does not appear to be valid or is missing. Please review and fix so that you can always have access to our latest versions!",
223
+ "It looks like you're using %s, but the license keys you supplied do not appear to be valid or are missing. Please review and fix so that you can always have access to our latest versions!",
224
+ count( $this->notices[ self::INVALID_KEY ] ),
225
+ 'tribe-common'
226
+ ),
227
+ $plugin_names
228
+ );
229
+
230
+ $action_steps = $this->find_your_key_text();
231
+
232
+ $this->render_notice( 'pue_key-' . self::INVALID_KEY, "<p>$prompt</p> <p>$action_steps</p>" );
233
+ }
234
+
235
+ /**
236
+ * Generate a notice listing any plugins for which license keys have expired.
237
+ *
238
+ * This notice should only appear at the top of the plugin admin screen and "trumps"
239
+ * the missing/invalid key notice on that screen only.
240
+ */
241
+ public function render_expired_key() {
242
+ global $pagenow;
243
+
244
+ if ( 'plugins.php' !== $pagenow ) {
245
+ return;
246
+ }
247
+
248
+ $plugin_names = $this->get_formatted_plugin_names( self::EXPIRED_KEY );
249
+
250
+ if ( empty( $plugin_names ) ) {
251
+ return;
252
+ }
253
+
254
+ $prompt = sprintf( _n(
255
+ 'There is an update available for %1$s but your license has expired. %2$sVisit the Events Calendar website to renew your license.%3$s',
256
+ 'Updates are available for %1$s but your license keys have expired. %2$sVisit the Events Calendar website to renew your licenses.%3$s',
257
+ count( $this->notices[ self::EXPIRED_KEY ] ),
258
+ 'tribe-common'
259
+ ),
260
+ $plugin_names,
261
+ '<a href="http://m.tri.be/195d" target="_blank">',
262
+ '</a>'
263
+ );
264
+
265
+ $renew_action =
266
+ '<a href="http://m.tri.be/195y" target="_blank" class="button button-primary">' .
267
+ __( 'Renew Your License Now', 'tribe-common' ) .
268
+ '<span class="screen-reader-text">' .
269
+ __( ' (opens in a new window)', 'tribe-common' ) .
270
+ '</span></a>';
271
+
272
+ $this->render_notice( 'pue_key-' . self::EXPIRED_KEY, "<p>$prompt</p> <p>$renew_action</p>" );
273
+ }
274
+
275
+ /**
276
+ * Generate a notice listing any plugins which have valid license keys, but those keys
277
+ * have met or exceeded the permitted number of installations they can be applied to.
278
+ */
279
+ public function render_upgrade_key() {
280
+ $plugin_names = $this->get_formatted_plugin_names( self::UPGRADE_KEY );
281
+
282
+ if ( empty( $plugin_names ) ) {
283
+ return;
284
+ }
285
+
286
+ $prompt = sprintf( _n(
287
+ 'You have entered a license key for %1$s but the key is out of installs. %2$sVisit the Events Calendar website%3$s to to manage your installs, upgrade your license, or purchase a new one.',
288
+ 'You have entered license keys for %1$s but your keys are out of installs. %2$sVisit the Events Calendar website%3$s to to manage your installs, upgrade your licenses, or purchase new ones.', count( $this->notices[ self::UPGRADE_KEY ] ),
289
+ 'tribe-common'
290
+ ),
291
+ $plugin_names,
292
+ '<a href="http://m.tri.be/195d" target="_blank">',
293
+ '</a>'
294
+ );
295
+
296
+ $this->render_notice( 'pue_key-' . self::UPGRADE_KEY, "<p>$prompt</p>" );
297
+ }
298
+
299
+ /**
300
+ * Renders the notice itself (the provided HTML will be wrapped in a suitable container div).
301
+ *
302
+ * @param string $slug
303
+ * @param string $inner_html
304
+ */
305
+ protected function render_notice( $slug, $inner_html ) {
306
+ $spirit_animal = esc_url( Tribe__Main::instance()->plugin_url . 'src/resources/images/spirit-animal.png' );
307
+
308
+ $html =
309
+ '<div class="api-check">
310
+ <div class="tribe-spirit-animal">
311
+ <img src="' . $spirit_animal . '"/>
312
+ </div>
313
+ <div class="notice-content">' . $inner_html . '</div>
314
+ </div>';
315
+
316
+ Tribe__Admin__Notices::instance()->render( $slug, $html );
317
+ }
318
+
319
+ /**
320
+ * @return string
321
+ */
322
+ protected function find_your_key_text() {
323
+ return sprintf(
324
+ __( 'You can find your license keys by logging in to %1$syour account on theeventscalendar.com%2$s and you can enter them over on the %3$ssettings page%2$s.', 'tribe-common' ),
325
+ '<a href="http://m.tri.be/195d" target="_blank">',
326
+ '</a>',
327
+ '<a href="' . admin_url( 'edit.php?page=tribe-common&tab=licenses&post_type=tribe_events' ) . '">'
328
+ );
329
+ }
330
+
331
+ /**
332
+ * Transforms the array referenced by group into a human readable,
333
+ * comma delimited list.
334
+ *
335
+ * Examples of output:
336
+ *
337
+ * # One name
338
+ * "Ticket Pro"
339
+ *
340
+ * # Two names
341
+ * "Ticket Pro and Calendar Legend"
342
+ *
343
+ * # Three names
344
+ * "Ticket Pro, Calendar Legend and Date Stars"
345
+ *
346
+ * # Fallback
347
+ * "Unknown Plugin(s)"
348
+ *
349
+ * @param string $group
350
+ *
351
+ * @return string
352
+ */
353
+ protected function get_formatted_plugin_names( $group ) {
354
+ if ( ! count( $this->notices[ $group ] ) ) {
355
+ return '';
356
+ }
357
+
358
+ $plugin_list = array_intersect( $this->registered, array_keys( $this->notices[ $group ] ) );
359
+ $num_plugins = count( $plugin_list );
360
+
361
+ if ( 0 === $num_plugins ) {
362
+ return '';
363
+ } elseif ( 1 === $num_plugins ) {
364
+ $html = current( $plugin_list );
365
+ } elseif ( 1 < $num_plugins ) {
366
+ $all_but_last = join( ', ', array_slice( $plugin_list, 0, count( $plugin_list ) - 1 ) );
367
+ $last = current( array_slice( $plugin_list, count( $plugin_list ) - 1, 1 ) );
368
+ $html = sprintf( _x( '%1$s and %2$s', 'formatted plugin list', 'tribe-common' ), $all_but_last, $last );
369
+ }
370
+
371
+ return '<span class="plugin-list">' . $html . '</span>';
372
+ }
373
+ }
common/src/Tribe/PUE/Plugin_Info.php CHANGED
@@ -41,6 +41,10 @@ if ( ! class_exists( 'Tribe__PUE__Plugin_Info' ) ) {
41
  public $downloaded;
42
  public $last_updated;
43
 
 
 
 
 
44
  public $id = 0; // The native WP.org API returns numeric plugin IDs, but they're not used for anything.
45
 
46
  /**
@@ -96,6 +100,9 @@ if ( ! class_exists( 'Tribe__PUE__Plugin_Info' ) ) {
96
  'downloaded',
97
  'homepage',
98
  'last_updated',
 
 
 
99
  );
100
  foreach ( $sameFormat as $field ) {
101
  if ( isset( $this->$field ) ) {
41
  public $downloaded;
42
  public $last_updated;
43
 
44
+ public $api_expired;
45
+ public $api_invalid;
46
+ public $api_upgrade;
47
+
48
  public $id = 0; // The native WP.org API returns numeric plugin IDs, but they're not used for anything.
49
 
50
  /**
100
  'downloaded',
101
  'homepage',
102
  'last_updated',
103
+ 'api_expired',
104
+ 'api_upgrade',
105
+ 'api_invalid',
106
  );
107
  foreach ( $sameFormat as $field ) {
108
  if ( isset( $this->$field ) ) {
common/src/Tribe/PUE/Utility.php CHANGED
@@ -60,8 +60,26 @@ if ( ! class_exists( 'Tribe__PUE__Utility' ) ) {
60
  */
61
  public static function from_plugin_info( $info ) {
62
  $update = new Tribe__PUE__Utility();
63
- $copyFields = array( 'id', 'slug', 'version', 'homepage', 'download_url', 'upgrade_notice', 'sections', 'plugin' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  foreach ( $copyFields as $field ) {
 
 
 
65
  $update->$field = $info->$field;
66
  }
67
 
60
  */
61
  public static function from_plugin_info( $info ) {
62
  $update = new Tribe__PUE__Utility();
63
+ $copyFields = array(
64
+ 'id',
65
+ 'slug',
66
+ 'version',
67
+ 'homepage',
68
+ 'download_url',
69
+ 'upgrade_notice',
70
+ 'sections',
71
+ 'plugin',
72
+ 'api_expired',
73
+ 'api_upgrade',
74
+ 'api_invalid',
75
+ 'api_invalid_message',
76
+ 'api_inline_invalid_message',
77
+ );
78
+
79
  foreach ( $copyFields as $field ) {
80
+ if ( ! isset( $info->$field ) ) {
81
+ continue;
82
+ }
83
  $update->$field = $info->$field;
84
  }
85
 
common/src/Tribe/Post_History.php ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Used for maintaining post-level histories/audit trails.
4
+ *
5
+ * @internal
6
+ * @since 4.3
7
+ */
8
+ class Tribe__Post_History {
9
+ /**
10
+ * Used to identify history/audit trail post meta records.
11
+ */
12
+ const HISTORY_KEY = '_tribe_post_history';
13
+
14
+ /**
15
+ * The post this history object is concerned with.
16
+ *
17
+ * @var int
18
+ */
19
+ protected $post_id;
20
+
21
+
22
+ /**
23
+ * Returns a Tribe__Post_History object for the specified post.
24
+ *
25
+ * @param int $post_id
26
+ *
27
+ * @return Tribe__Post_History
28
+ */
29
+ public static function load( $post_id ) {
30
+ return new self( $post_id );
31
+ }
32
+
33
+ /**
34
+ * Returns a Tribe__Post_History object for the specified post.
35
+ *
36
+ * @param int $post_id
37
+ *
38
+ * @return Tribe__Post_History
39
+ */
40
+ public function __construct( $post_id ) {
41
+ $this->post_id = $post_id;
42
+ }
43
+
44
+ /**
45
+ * Records a new history entry for the current post.
46
+ *
47
+ * @param string $message
48
+ * @param array $data
49
+ */
50
+ public function add_entry( $message, array $data = array() ) {
51
+ $datetime = current_time( 'mysql' );
52
+ $checksum = uniqid( substr( hash( 'md5', $datetime . $message . serialize( $data ) ), 0, 8 ) . '_' );
53
+
54
+ $log_entry = wp_slash( json_encode( array(
55
+ 'datetime' => $datetime,
56
+ 'message' => $message,
57
+ 'data' => $data,
58
+ 'checksum' => $checksum,
59
+ ) ) );
60
+
61
+ add_post_meta( $this->post_id, self::HISTORY_KEY, $log_entry );
62
+ }
63
+
64
+ /**
65
+ * Indicates if any history exists for the current post.
66
+ *
67
+ * @return bool
68
+ */
69
+ public function has_entries() {
70
+ $first_available_entry = get_post_meta( $this->post_id, self::HISTORY_KEY, true );
71
+ return ! empty( $first_available_entry );
72
+ }
73
+
74
+ /**
75
+ * Returns all historical records for the current post as an array
76
+ * of objects, each object taking the form:
77
+ *
78
+ * {
79
+ * "datetime": "yyyy-mm-dd hh:ii:ss",
80
+ * "message": "...",
81
+ * "data": []
82
+ * }
83
+ *
84
+ * @return array
85
+ */
86
+ public function get_entries() {
87
+ $entries = array();
88
+
89
+ foreach ( get_post_meta( $this->post_id, self::HISTORY_KEY ) as $log_entry ) {
90
+ $log_entry = json_decode( $log_entry );
91
+
92
+ if ( ! $log_entry ) {
93
+ continue;
94
+ }
95
+
96
+ $entries[] = $log_entry;
97
+ }
98
+
99
+ return $entries;
100
+ }
101
+
102
+ /**
103
+ * Deletes all entries for the current post that match the provided datetime
104
+ * string and (optionally) also match the provided checksum.
105
+ *
106
+ * Returns the total number of deleted entries, which may be zero if none were matched;
107
+ * can also be more than one if multiple entries were logged at the same time and no
108
+ * checksum is provided.
109
+ *
110
+ * @param string $datetime
111
+ * @param string $checksum optional value to more precisely specify the entry to be deleted
112
+ *
113
+ * @return int
114
+ */
115
+ public function delete_entry( $datetime, $checksum = null ) {
116
+ $deleted = 0;
117
+
118
+ foreach ( $this->get_entries() as $entry ) {
119
+ if ( $entry->datetime !== $datetime ) {
120
+ continue;
121
+ }
122
+
123
+ if ( null !== $checksum && $entry->checksum !== $checksum ) {
124
+ continue;
125
+ }
126
+
127
+ if ( delete_post_meta( $this->post_id, self::HISTORY_KEY, json_encode( $entry ) ) ) {
128
+ $deleted++;
129
+ }
130
+ }
131
+
132
+ return $deleted;
133
+ }
134
+ }
common/src/Tribe/Settings_Manager.php CHANGED
@@ -28,7 +28,6 @@ class Tribe__Settings_Manager {
28
  add_action( 'admin_menu', array( $this, 'add_help_admin_menu_item' ), 50 );
29
  add_action( 'tribe_settings_do_tabs', array( $this, 'do_setting_tabs' ) );
30
  add_action( 'tribe_settings_do_tabs', array( $this, 'do_network_settings_tab' ), 400 );
31
- add_action( 'tribe_settings_content_tab_help', array( $this, 'do_help_tab' ) );
32
  add_action( 'tribe_settings_validate_tab_network', array( $this, 'save_all_tabs_hidden' ) );
33
  }
34
 
@@ -56,15 +55,6 @@ class Tribe__Settings_Manager {
56
  new Tribe__Settings_Tab( 'display', esc_html__( 'Display', 'tribe-common' ), $displayTab );
57
 
58
  $this->do_licenses_tab();
59
-
60
- new Tribe__Settings_Tab(
61
- 'help',
62
- esc_html__( 'Help', 'tribe-common' ),
63
- array(
64
- 'priority' => 60,
65
- 'show_save' => false,
66
- )
67
- );
68
  }
69
 
70
  /**
@@ -285,21 +275,11 @@ class Tribe__Settings_Manager {
285
  return;
286
  }
287
 
288
- $parent = Tribe__Settings::$parent_slug;
289
  $title = esc_html__( 'Help', 'tribe-common' );
290
- $slug = esc_url(
291
- apply_filters( 'tribe_settings_url',
292
- add_query_arg(
293
- array(
294
- 'page' => 'tribe-common',
295
- 'tab' => 'help',
296
- ),
297
- Tribe__Settings::$parent_page
298
- )
299
- )
300
- );
301
 
302
- add_submenu_page( $parent, $title, $title, 'manage_options', $slug, '' );
303
  }
304
 
305
  /**
28
  add_action( 'admin_menu', array( $this, 'add_help_admin_menu_item' ), 50 );
29
  add_action( 'tribe_settings_do_tabs', array( $this, 'do_setting_tabs' ) );
30
  add_action( 'tribe_settings_do_tabs', array( $this, 'do_network_settings_tab' ), 400 );
 
31
  add_action( 'tribe_settings_validate_tab_network', array( $this, 'save_all_tabs_hidden' ) );
32
  }
33
 
55
  new Tribe__Settings_Tab( 'display', esc_html__( 'Display', 'tribe-common' ), $displayTab );
56
 
57
  $this->do_licenses_tab();
 
 
 
 
 
 
 
 
 
58
  }
59
 
60
  /**
275
  return;
276
  }
277
 
278
+ $parent = Tribe__Settings::$parent_page;
279
  $title = esc_html__( 'Help', 'tribe-common' );
280
+ $slug = 'tribe-help';
 
 
 
 
 
 
 
 
 
 
281
 
282
+ add_submenu_page( $parent, $title, $title, 'manage_options', $slug, array( $this, 'do_help_tab' ) );
283
  }
284
 
285
  /**
common/src/Tribe/Support.php CHANGED
@@ -13,7 +13,7 @@ if ( ! class_exists( 'Tribe__Support' ) ) {
13
  class Tribe__Support {
14
 
15
  public static $support;
16
- public $rewrite_rules_purged = false;
17
 
18
  /**
19
  * @var Tribe__Support__Obfuscator
@@ -44,10 +44,14 @@ if ( ! class_exists( 'Tribe__Support' ) ) {
44
  $this->must_escape = (array) apply_filters( 'tribe_help_must_escape_fields', $this->must_escape );
45
  add_action( 'tribe_help_pre_get_sections', array( $this, 'append_system_info' ), 10 );
46
  add_action( 'delete_option_rewrite_rules', array( $this, 'log_rewrite_rule_purge' ) );
 
 
 
47
  }
48
 
49
  /**
50
  * Display help tab info in events settings
 
51
  * @param Tribe__Admin__Help_Page $help The Help Page Instance
52
  */
53
  public function append_system_info( Tribe__Admin__Help_Page $help ) {
@@ -158,7 +162,6 @@ if ( ! class_exists( 'Tribe__Support' ) ) {
158
  'Site URL' => get_site_url(),
159
  'Site Language' => get_option( 'WPLANG' ) ? get_option( 'WPLANG' ) : esc_html__( 'English', 'tribe-common' ),
160
  'Character Set' => get_option( 'blog_charset' ),
161
- 'WP Permalinks' => get_option( 'permalink_structure' ),
162
  'Name' => $user->display_name,
163
  'Email' => $user->user_email,
164
  'Install keys' => $keys,
@@ -186,6 +189,7 @@ if ( ! class_exists( 'Tribe__Support' ) ) {
186
  if ( $this->rewrite_rules_purged ) {
187
  $systeminfo['rewrite rules purged'] = esc_html__( 'Rewrite rules were purged on load of this help page. Chances are there is a rewrite rule flush occurring in a plugin or theme!', 'tribe-common' );
188
  }
 
189
  $systeminfo = apply_filters( 'tribe-events-pro-support', $systeminfo );
190
 
191
  return $systeminfo;
@@ -269,6 +273,137 @@ if ( ! class_exists( 'Tribe__Support' ) ) {
269
  $this->obfuscator = $obfuscator;
270
  }
271
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  /****************** SINGLETON GUTS ******************/
273
 
274
  /**
@@ -279,7 +414,7 @@ if ( ! class_exists( 'Tribe__Support' ) ) {
279
 
280
  public static function getInstance() {
281
  if ( null == self::$instance ) {
282
- $instance = new self;
283
  $instance->set_obfuscator( new Tribe__Support__Obfuscator( $instance->must_obfuscate_prefixes ) );
284
  self::$instance = $instance;
285
  }
13
  class Tribe__Support {
14
 
15
  public static $support;
16
+ public $rewrite_rules_purged = false;
17
 
18
  /**
19
  * @var Tribe__Support__Obfuscator
44
  $this->must_escape = (array) apply_filters( 'tribe_help_must_escape_fields', $this->must_escape );
45
  add_action( 'tribe_help_pre_get_sections', array( $this, 'append_system_info' ), 10 );
46
  add_action( 'delete_option_rewrite_rules', array( $this, 'log_rewrite_rule_purge' ) );
47
+
48
+ add_action( 'rest_api_init', array( __CLASS__, 'create_sysinfo_endpoint' ) );
49
+ add_action( 'wp_ajax_tribe_toggle_sysinfo_optin', array( __CLASS__, 'ajax_sysinfo_optin' ) );
50
  }
51
 
52
  /**
53
  * Display help tab info in events settings
54
+ *
55
  * @param Tribe__Admin__Help_Page $help The Help Page Instance
56
  */
57
  public function append_system_info( Tribe__Admin__Help_Page $help ) {
162
  'Site URL' => get_site_url(),
163
  'Site Language' => get_option( 'WPLANG' ) ? get_option( 'WPLANG' ) : esc_html__( 'English', 'tribe-common' ),
164
  'Character Set' => get_option( 'blog_charset' ),
 
165
  'Name' => $user->display_name,
166
  'Email' => $user->user_email,
167
  'Install keys' => $keys,
189
  if ( $this->rewrite_rules_purged ) {
190
  $systeminfo['rewrite rules purged'] = esc_html__( 'Rewrite rules were purged on load of this help page. Chances are there is a rewrite rule flush occurring in a plugin or theme!', 'tribe-common' );
191
  }
192
+
193
  $systeminfo = apply_filters( 'tribe-events-pro-support', $systeminfo );
194
 
195
  return $systeminfo;
273
  $this->obfuscator = $obfuscator;
274
  }
275
 
276
+ /**
277
+ * Creates Fields in Help Tab to Opt In to System Info
278
+ *
279
+ * @return string
280
+ */
281
+ public static function opt_in() {
282
+
283
+ $checked = '';
284
+ $optin_key = get_option( 'tribe_systeminfo_optin' );
285
+ if ( $optin_key ) {
286
+ $checked = 'checked';
287
+ }
288
+
289
+ $opt_in = '<p class="system-info"><input name="tribe_auto_sysinfo_opt_in" id="tribe_auto_sysinfo_opt_in" type="checkbox" value="optin" ' . esc_attr( $checked ) . '/><label for="tribe_auto_sysinfo_opt_in">' . esc_html__( 'Yes, automatically share my system information with the Modern Tribe support team', 'tribe-common' ) . '</label></p>';
290
+ $opt_in .= '<p class="tooltip description">' . esc_html__( 'Your system information will only be used by the Modern Tribe support team. All information is stored securely. We do not share this information with any third parties.', 'tribe-common' ) . '</p>';
291
+ $opt_in .= '<p class="tribe-sysinfo-optin-msg"></p>';
292
+
293
+ return $opt_in;
294
+ }
295
+
296
+ /**
297
+ * Method to send back sysinfo
298
+ *
299
+ * @param $query
300
+ *
301
+ * @return string|void
302
+ *
303
+ */
304
+ public static function sysinfo_query( $query ) {
305
+
306
+ $optin_key = get_option( 'tribe_systeminfo_optin' );
307
+
308
+ if ( ! $optin_key ) {
309
+ wp_send_json_error( __( 'Invalid Key', 'tribe-common' ) );
310
+ }
311
+
312
+ $key = $query['key'];
313
+ if ( $key != $optin_key ) {
314
+ wp_send_json_error( __( 'Invalid Key', 'tribe-common' ) );
315
+ }
316
+
317
+ $support = Tribe__Support::getInstance();
318
+ $systeminfo = $support->formattedSupportStats();
319
+
320
+ return $systeminfo;
321
+ }
322
+
323
+ /*
324
+ * Create Unique Enpoint Per Site
325
+ */
326
+ public static function create_sysinfo_endpoint() {
327
+ $optin_key = get_option( 'tribe_systeminfo_optin' );
328
+ if ( $optin_key ) {
329
+ register_rest_route( 'tribe_events/v2', '/(?P<key>[a-z0-9\-]+)/sysinfo/', array(
330
+ 'methods' => 'GET',
331
+ 'callback' => array( 'Tribe__Support', 'sysinfo_query' ),
332
+ ) );
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Ajax Method to Create Unique Key and send to tec.com
338
+ */
339
+ public static function ajax_sysinfo_optin() {
340
+
341
+ if ( ! isset( $_POST['confirm'] ) || ! wp_verify_nonce( $_POST['confirm'], 'sysinfo_optin_nonce' ) ) {
342
+ wp_send_json_error( __( 'Permission Error', 'tribe-common' ) );
343
+ }
344
+
345
+ if ( 'generate' == $_POST['generate_key'] ) {
346
+
347
+ $random = base_convert( rand( 0, getrandmax() ), 10, 36 );
348
+ $optin_key = hash( 'sha1', $random );
349
+ update_option( 'tribe_systeminfo_optin', $optin_key );
350
+
351
+ //Only Connect If a License Exists
352
+ $keys = apply_filters( 'tribe-pue-install-keys', array() );
353
+ if ( is_array( $keys ) && ! empty( $keys ) ) {
354
+ Tribe__Support::send_sysinfo_key( $optin_key );
355
+ } else {
356
+ wp_send_json_success( __( 'Unique System Info Key Generated', 'tribe-common' ) );
357
+ }
358
+
359
+ } elseif ( 'remove' == $_POST['generate_key'] ) {
360
+ $optin_key = get_option( 'tribe_systeminfo_optin' );
361
+
362
+ delete_option( 'tribe_systeminfo_optin' );
363
+
364
+ Tribe__Support::send_sysinfo_key( $optin_key, null, 'remove' );
365
+
366
+ }
367
+
368
+ wp_send_json_error( __( 'Permission Error', 'tribe-common' ) );
369
+
370
+ }
371
+
372
+ /**
373
+ * Contact Tribe Website to Add SysInfo Key
374
+ *
375
+ * @param null $optin_key provide key for system info
376
+ * @param null $url domain of current site
377
+ * @param null $remove string used if removing $optin_key from tec.com
378
+ * @param null $pueadd boolean to disable messaging when coming from pue script
379
+ */
380
+ public static function send_sysinfo_key( $optin_key = null, $url = null, $remove = null, $pueadd = false ) {
381
+
382
+ $url = $url ? $url : urlencode( str_replace( array( 'http://', 'https://' ), '', get_site_url() ) );
383
+ $pue = new Tribe__PUE__Checker( 'https://theeventscalendar.com/', 'events-calendar' );
384
+ $query = $pue->get_pue_update_url() . 'wp-json/tribe_system/v2/customer-info/' . $optin_key . '/' . $url . $remove;
385
+ if ( $remove ) {
386
+ $query = $pue->get_pue_update_url() . 'wp-json/tribe_system/v2/customer-info/' . $optin_key . '/' . $url . '?status=remove';
387
+ }
388
+ $response = wp_remote_get( esc_url( $query ) );
389
+
390
+ $response = json_decode( wp_remote_retrieve_body( $response ) );
391
+
392
+ if ( ! $pueadd ) {
393
+ // make sure the response came back okay
394
+ if ( ! isset( $response->success ) ) {
395
+ //on error delete the key
396
+ delete_option( 'tribe_systeminfo_optin' );
397
+
398
+ //send error response
399
+ wp_send_json_error( $response );
400
+ }
401
+
402
+ wp_send_json_success( $response->data );
403
+ }
404
+ }
405
+
406
+
407
  /****************** SINGLETON GUTS ******************/
408
 
409
  /**
414
 
415
  public static function getInstance() {
416
  if ( null == self::$instance ) {
417
+ $instance = new self;
418
  $instance->set_obfuscator( new Tribe__Support__Obfuscator( $instance->must_obfuscate_prefixes ) );
419
  self::$instance = $instance;
420
  }
common/src/Tribe/Templates.php CHANGED
@@ -43,11 +43,11 @@ class Tribe__Templates {
43
  return $fallback;
44
  }
45
  foreach ( $stylesheets as $filename ) {
46
- if ( file_exists( STYLESHEETPATH . '/' . $filename ) ) {
47
  $located = trailingslashit( get_stylesheet_directory_uri() ) . $filename;
48
  break;
49
  } else {
50
- if ( file_exists( TEMPLATEPATH . '/' . $filename ) ) {
51
  $located = trailingslashit( get_template_directory_uri() ) . $filename;
52
  break;
53
  }
43
  return $fallback;
44
  }
45
  foreach ( $stylesheets as $filename ) {
46
+ if ( file_exists( get_stylesheet_directory() . '/' . $filename ) ) {
47
  $located = trailingslashit( get_stylesheet_directory_uri() ) . $filename;
48
  break;
49
  } else {
50
+ if ( file_exists( get_template_directory() . '/' . $filename ) ) {
51
  $located = trailingslashit( get_template_directory_uri() ) . $filename;
52
  break;
53
  }
common/src/Tribe/Utils/JSON.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ /**
5
+ * Class Tribe__Utils__JSON
6
+ *
7
+ * Provides JSON related utility functions.
8
+ */
9
+ class Tribe__Utils__JSON {
10
+
11
+ /**
12
+ * Recursively escapes quotes and JSON relevant chars in a string to avoid json operation errors.
13
+ *
14
+ * The method will recursively escape any string found.
15
+ *
16
+ * @param array|string $value Either a string to escape or an array of strings to escape.
17
+ *
18
+ * @return array|string Either an array of escaped strings or the escaped string.
19
+ */
20
+ public static function escape_string( $value ) {
21
+ if ( ! ( is_string( $value ) || is_array( $value ) ) ) {
22
+ return $value;
23
+ }
24
+ if ( is_array( $value ) ) {
25
+ $escaped = array();
26
+ foreach ( $value as $key => $subvalue ) {
27
+ $escaped[ $key ] = self::escape_string( $subvalue );
28
+ }
29
+
30
+ return $escaped;
31
+ }
32
+
33
+ $escapers = array( "\\", "/", "\"", "\n", "\r", "\t", "\x08", "\x0c" );
34
+ $replacements = array( "\\\\", "\\/", "\\\"", "\\n", "\\r", "\\t", "\\f", "\\b" );
35
+
36
+ return str_replace( $escapers, $replacements, $value );
37
+ }
38
+ }
common/src/Tribe/View_Helpers.php CHANGED
@@ -96,7 +96,7 @@ if ( ! class_exists( 'Tribe__View_Helpers' ) ) {
96
  'CD' => esc_html__( 'Congo, The Democratic Republic Of The', 'tribe-common' ),
97
  'CK' => esc_html__( 'Cook Islands', 'tribe-common' ),
98
  'CR' => esc_html__( 'Costa Rica', 'tribe-common' ),
99
- 'CI' => esc_html__( "Cote D'Ivoire", 'tribe-common' ),
100
  'HR' => esc_html__( 'Croatia (Local Name: Hrvatska)', 'tribe-common' ),
101
  'CU' => esc_html__( 'Cuba', 'tribe-common' ),
102
  'CY' => esc_html__( 'Cyprus', 'tribe-common' ),
96
  'CD' => esc_html__( 'Congo, The Democratic Republic Of The', 'tribe-common' ),
97
  'CK' => esc_html__( 'Cook Islands', 'tribe-common' ),
98
  'CR' => esc_html__( 'Costa Rica', 'tribe-common' ),
99
+ 'CI' => esc_html__( "C&ocirc;te d'Ivoire", 'tribe-common' ),
100
  'HR' => esc_html__( 'Croatia (Local Name: Hrvatska)', 'tribe-common' ),
101
  'CU' => esc_html__( 'Cuba', 'tribe-common' ),
102
  'CY' => esc_html__( 'Cyprus', 'tribe-common' ),
common/src/admin-views/app-shop.php CHANGED
@@ -1,8 +1,8 @@
1
  <div id="tribe-app-shop" class="wrap">
2
 
3
  <div class="header">
4
- <h1><?php esc_html_e( 'Tribe Event Add-Ons', 'tribe-common' ); ?></h1>
5
- <a href="https://theeventscalendar.com/?utm_campaign=in-app&utm_source=addonspage&utm_medium=top-banner" target="_blank"><img src="<?php echo esc_url( tribe_resource_url( 'images/app-shop-banner.jpg', false, 'common' ) ); ?>" /></a>
6
  </div>
7
 
8
  <div class="content-wrapper">
@@ -11,9 +11,9 @@
11
  $i = 0;
12
  foreach ( $products as $product ) {
13
  ?>
14
- <div class="tribe-addon<?php echo ( $i % 4 == 0 ) ? ' first tribe-clearfix' : '';?>">
15
  <div class="thumb">
16
- <a href="<?php echo esc_url( $product->link ); ?>"><img src="<?php echo esc_url( tribe_resource_url( $product->image, false, 'common' ) ); ?>" /></a>
17
  </div>
18
  <div class="caption">
19
  <h4><a href="<?php echo esc_url( $product->link ); ?>"><?php echo esc_html( $product->title ); ?></a></h4>
@@ -29,7 +29,7 @@
29
  ?>
30
  </div>
31
 
32
- <a class="button button-primary" href="<?php echo esc_url( $product->link ); ?>">Get This Add-on</a>
33
  </div>
34
  </div>
35
 
1
  <div id="tribe-app-shop" class="wrap">
2
 
3
  <div class="header">
4
+ <h1><?php esc_html_e( 'Events Add-Ons', 'tribe-common' ); ?></h1>
5
+ <a class="button" href="https://theeventscalendar.com/?utm_campaign=in-app&utm_source=addonspage&utm_medium=top-banner" target="_blank"><?php esc_html_e( 'Browse All Add-Ons', 'tribe-common' ); ?></a>
6
  </div>
7
 
8
  <div class="content-wrapper">
11
  $i = 0;
12
  foreach ( $products as $product ) {
13
  ?>
14
+ <div class="tribe-addon<?php echo ( 0 === $i ) ? ' first' : '';?>">
15
  <div class="thumb">
16
+ <a href="<?php echo esc_url( $product->link ); ?>"><img src="<?php echo esc_url( tribe_resource_url( $product->image, false, null, $main ) ); ?>" /></a>
17
  </div>
18
  <div class="caption">
19
  <h4><a href="<?php echo esc_url( $product->link ); ?>"><?php echo esc_html( $product->title ); ?></a></h4>
29
  ?>
30
  </div>
31
 
32
+ <a class="button button-primary" href="<?php echo esc_url( $product->link ); ?>"><?php esc_html_e( 'Buy This Add-On', 'tribe-common' ); ?></a>
33
  </div>
34
  </div>
35
 
common/src/admin-views/event-log.php CHANGED
@@ -84,6 +84,9 @@
84
  </div>
85
 
86
  <div id="tribe-log-viewer">
 
 
 
87
 
88
  <table>
89
  <?php foreach ( $log_entries as $data ): ?>
@@ -95,8 +98,6 @@
95
  <?php endforeach; ?>
96
  </table>
97
 
98
- <?php if ( empty( $log_entries ) ): ?>
99
- <p><?php esc_html_e( 'The selected log file is empty or has not been generated yet.', 'tribe-common' ); ?></p>
100
  <?php endif; ?>
101
 
102
  </div>
84
  </div>
85
 
86
  <div id="tribe-log-viewer">
87
+ <?php if ( empty( $log_entries ) ): ?>
88
+ <p><?php esc_html_e( 'The selected log file is empty or has not been generated yet.', 'tribe-common' ); ?></p>
89
+ <?php else: ?>
90
 
91
  <table>
92
  <?php foreach ( $log_entries as $data ): ?>
98
  <?php endforeach; ?>
99
  </table>
100
 
 
 
101
  <?php endif; ?>
102
 
103
  </div>
common/src/admin-views/tribe-options-help.php CHANGED
@@ -32,17 +32,27 @@ $help->add_section_content( 'extra-help', '<div style="text-align: right;"><a hr
32
 
33
  // Creates the System Info section
34
  $help->add_section( 'system-info', __( 'System Information', 'tribe-common' ), 30 );
35
- $help->add_section_content( 'system-info', __( 'The details of your calendar plugin and settings is often needed for you or our staff to help troubleshoot an issue. We may ask you to share this information if you ask for support. If you post in one of our premium forums, please copy and paste this information into the System Information field and it will help us help you faster!', 'tribe-common' ), 0 );
 
 
 
 
 
 
 
 
36
 
37
  $help->add_section( 'template-changes', __( 'Recent Template Changes', 'tribe-common' ), 40 );
38
  $help->add_section_content( 'template-changes', Tribe__Support__Template_Checker_Report::generate() );
 
 
 
39
  ?>
40
 
41
  <div id="tribe-help-general">
42
  <?php $help->get_sections(); ?>
43
  </div>
44
 
45
-
46
  <div id="tribe-help-sidebar">
47
  <?php
48
  /**
@@ -70,4 +80,4 @@ $help->add_section_content( 'template-changes', Tribe__Support__Template_Checker
70
  */
71
  do_action( 'tribe_help_sidebar_after' ); ?>
72
 
73
- </div>
32
 
33
  // Creates the System Info section
34
  $help->add_section( 'system-info', __( 'System Information', 'tribe-common' ), 30 );
35
+ $help->add_section_content( 'system-info', __( 'The details of your calendar plugin and settings is often needed for you or our staff to help troubleshoot an issue. Please opt-in below to automatically share your system information with our support team. This will allow us to assist you faster if you post in our forums.', 'tribe-common' ), 0 );
36
+
37
+ $help->add_section_content(
38
+ 'system-info',
39
+ Tribe__Support::opt_in(),
40
+ 10
41
+ );
42
+
43
+ $help->add_section_content( 'system-info', '<div class="system-info-copy"><button data-clipboard-action="copy" class="system-info-copy-btn" data-clipboard-target=".support-stats" ><span class="dashicons dashicons-clipboard license-btn"></span>' . __( 'Copy to clipboard', 'tribe-common' ) . '</button></div>', 10 );
44
 
45
  $help->add_section( 'template-changes', __( 'Recent Template Changes', 'tribe-common' ), 40 );
46
  $help->add_section_content( 'template-changes', Tribe__Support__Template_Checker_Report::generate() );
47
+
48
+ $help->add_section( 'event-log', __( 'Event Log', 'tribe-common' ), 50 );
49
+ $help->add_section_content( 'event-log', Tribe__Main::instance()->log()->admin()->display_log() );
50
  ?>
51
 
52
  <div id="tribe-help-general">
53
  <?php $help->get_sections(); ?>
54
  </div>
55
 
 
56
  <div id="tribe-help-sidebar">
57
  <?php
58
  /**
80
  */
81
  do_action( 'tribe_help_sidebar_after' ); ?>
82
 
83
+ </div>
common/src/admin-views/tribe-options-licenses.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- $link = add_query_arg(
3
  array(
4
  'utm_campaign' => 'in-app',
5
  'utm_medium' => 'plugin-tec',
@@ -7,10 +7,30 @@ $link = add_query_arg(
7
  ), Tribe__Main::$tec_url . 'license-keys/'
8
  );
9
 
10
- $link = esc_url( $link );
 
11
 
12
  // Explanatory text about license settings for the tab information box
13
- $html = __( '<p>The license key you received when completing your purchase from %1$s will grant you access to support and updates until it expires. You do not need to enter the key below for the plugins to work, but you will need to enter it to get automatic updates. <strong>Find your license keys at <a href="%2$s" target="_blank">%3$s</a></strong>.</p> <p>Each paid add-on has its own unique license key. Simply paste the key into its appropriate field on below, and give it a moment to validate. You know you\'re set when a green expiration date appears alongside a "valid" message.</p> <p>If you\'re seeing a red message telling you that your key isn\'t valid or is out of installs, visit <a href="%4$s" target="_blank">%5$s</a> to manage your installs or renew / upgrade your license.</p><p>Not seeing an update but expecting one? In WordPress, go to <a href="%6$s">Dashboard > Updates</a> and click "Check Again".</p>', 'tribe-common' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  // Expand with extra information for mu network users
16
  if ( is_multisite() ) {
@@ -47,27 +67,10 @@ $licenses_tab = array(
47
  ),
48
  'info-box-description' => array(
49
  'type' => 'html',
50
- 'html' => sprintf(
51
- $html,
52
- Tribe__Main::$tec_url,
53
- $link,
54
- Tribe__Main::$tec_url . 'license-keys/',
55
- $link,
56
- Tribe__Main::$tec_url . 'license-keys/',
57
- admin_url( '/update-core.php' )
58
- ),
59
  ),
60
  'info-end' => array(
61
  'type' => 'html',
62
  'html' => '</div>',
63
  ),
64
- 'tribe-form-content-start' => array(
65
- 'type' => 'html',
66
- 'html' => '<div class="tribe-settings-form-wrap">',
67
- ),
68
- // TODO: Figure out how properly close this wrapper after the license content
69
- 'tribe-form-content-end' => array(
70
- 'type' => 'html',
71
- 'html' => '</div>',
72
- ),
73
  );
1
  <?php
2
+ $utm_link = add_query_arg(
3
  array(
4
  'utm_campaign' => 'in-app',
5
  'utm_medium' => 'plugin-tec',
7
  ), Tribe__Main::$tec_url . 'license-keys/'
8
  );
9
 
10
+ $utm_link = esc_url( $utm_link );
11
+ $license_link = '<a href="' . $utm_link . '" target="_blank">' . Tribe__Main::$tec_url . '<span class="screen-reader-text">' . __( ' (opens in new window)', 'tribe-common' ) . '</span></a>';
12
 
13
  // Explanatory text about license settings for the tab information box
14
+ $html = '<p>' . sprintf(
15
+ esc_html__( 'The license key you received when completing your purchase from %1$s will grant you access to support and updates until it expires. You do not need to enter the key below for the plugins to work, but you will need to enter it to get automatic updates. %3$sFind your license keys at %2$s%4$s.', 'tribe-common' ),
16
+ '<a href="' . Tribe__Main::$tec_url . '" target="_blank">' . Tribe__Main::$tec_url . '<span class="screen-reader-text">' . __( ' (opens in new window)', 'tribe-common' ) . '</span></a>',
17
+ $license_link,
18
+ '<strong>',
19
+ '</strong>'
20
+ ) . '</p>';
21
+
22
+ $html .= '<p>' . esc_html__( 'Each paid add-on has its own unique license key. Simply paste the key into its appropriate field below, and give it a moment to validate. You know you\'re set when a green expiration date appears alongside a "valid" message.', 'tribe-common' ) . '</p>';
23
+
24
+ $html .= '<p>' . sprintf(
25
+ esc_html__( 'If you\'re seeing a red message telling you that your key isn\'t valid or is out of installs, visit %1$s to manage your installs or renew / upgrade your license.', 'tribe-common' ),
26
+ $license_link
27
+ ) . '</p>';
28
+
29
+ $html .= '<p>' . sprintf(
30
+ esc_html__( 'Not seeing an update but expecting one? In WordPress, go to %1$sDashboard > Updates%2$s and click "Check Again".', 'tribe-common' ),
31
+ '<a href="' . admin_url( '/update-core.php' ) . '">',
32
+ '</a>'
33
+ ) . '</p>';
34
 
35
  // Expand with extra information for mu network users
36
  if ( is_multisite() ) {
67
  ),
68
  'info-box-description' => array(
69
  'type' => 'html',
70
+ 'html' => $html,
 
 
 
 
 
 
 
 
71
  ),
72
  'info-end' => array(
73
  'type' => 'html',
74
  'html' => '</div>',
75
  ),
 
 
 
 
 
 
 
 
 
76
  );
common/src/deprecated/Tribe__Admin__Notice__Archive_Slug_Conflict.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ _deprecated_file( __FILE__, '4.3', 'Tribe__Admin__Notices' );
3
+
4
+ class Tribe__Admin__Notice__Archive_Slug_Conflict {
5
+ protected static $instance;
6
+
7
+ public static function instance() {
8
+ _deprecated_function( __METHOD__, '4.3', 'Tribe__Admin__Notices' );
9
+
10
+ if ( empty( self::$instance ) ) {
11
+ self::$instance = new self();
12
+ }
13
+
14
+ return self::$instance;
15
+ }
16
+
17
+ public function maybe_add_admin_notice(){
18
+ _deprecated_function( __METHOD__, '4.3', 'Tribe__Admin__Notices' );
19
+ }
20
+
21
+ public function maybe_dismiss(){
22
+ _deprecated_function( __METHOD__, '4.3', 'Tribe__Admin__Notices' );
23
+ }
24
+
25
+ public function notice(){
26
+ _deprecated_function( __METHOD__, '4.3', 'Tribe__Admin__Notices' );
27
+ }
28
+ }
common/src/functions/template-tags/general.php CHANGED
@@ -70,40 +70,53 @@ if ( ! function_exists( 'tribe_resource_url' ) ) {
70
  * Returns or echoes a url to a file in the Events Calendar plugin resources directory
71
  *
72
  * @category Events
 
73
  * @param string $resource the filename of the resource
74
  * @param bool $echo whether or not to echo the url
75
- * @param string $root_dir directory to hunt for resource files (src or common)
 
76
  *
77
  * @return string
78
  **/
79
- function tribe_resource_url( $resource, $echo = false, $root_dir = 'src' ) {
80
  $extension = pathinfo( $resource, PATHINFO_EXTENSION );
81
-
82
- if ( 'src' !== $root_dir ) {
83
- $root_dir .= '/src';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  }
85
 
86
- $resources_path = $root_dir . '/resources/';
87
- switch ( $extension ) {
88
- case 'css':
89
- $resource_path = $resources_path .'css/';
90
- break;
91
- case 'js':
92
- $resource_path = $resources_path .'js/';
93
- break;
94
- case 'scss':
95
- $resource_path = $resources_path .'scss/';
96
- break;
97
- default:
98
- $resource_path = $resources_path;
99
- break;
100
  }
101
 
102
- $path = $resource_path . $resource;
103
 
104
- $plugin_path = trailingslashit( dirname( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) ) );
105
- $plugin_dir = trailingslashit( basename( $plugin_path ) );
106
- $url = plugins_url( $plugin_dir );
 
 
107
 
108
  /**
109
  * Filters the resource URL
@@ -111,7 +124,7 @@ if ( ! function_exists( 'tribe_resource_url' ) ) {
111
  * @param $url
112
  * @param $resource
113
  */
114
- $url = apply_filters( 'tribe_resource_url', $url . $path, $resource );
115
 
116
  /**
117
  * Deprected the tribe_events_resource_url filter in 4.0 in favor of tribe_resource_url. Remove in 5.0
@@ -454,3 +467,96 @@ if ( ! function_exists( 'tribe_get_date_option' ) ) {
454
  return Tribe__Date_Utils::unescape_date_format($value);
455
  }
456
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  * Returns or echoes a url to a file in the Events Calendar plugin resources directory
71
  *
72
  * @category Events
73
+ *
74
  * @param string $resource the filename of the resource
75
  * @param bool $echo whether or not to echo the url
76
+ * @param string $root_dir directory to hunt for resource files (null or the actual path)
77
+ * @param object $origin Which plugin we are dealing with
78
  *
79
  * @return string
80
  **/
81
+ function tribe_resource_url( $resource, $echo = false, $root_dir = null, $origin = null ) {
82
  $extension = pathinfo( $resource, PATHINFO_EXTENSION );
83
+ $resource_path = '';
84
+
85
+ if ( is_null( $root_dir ) ) {
86
+ $resources_path = 'src/resources/';
87
+ switch ( $extension ) {
88
+ case 'css':
89
+ $resource_path = $resources_path .'css/';
90
+ break;
91
+ case 'js':
92
+ $resource_path = $resources_path .'js/';
93
+ break;
94
+ case 'scss':
95
+ $resource_path = $resources_path .'scss/';
96
+ break;
97
+ default:
98
+ $resource_path = $resources_path;
99
+ break;
100
+ }
101
+ } else {
102
+ $resource_path = $root_dir;
103
  }
104
 
105
+ $path = $resource_path . $resource;
106
+
107
+ if ( is_object( $origin ) ) {
108
+ $plugin_path = trailingslashit( ! empty( $origin->plugin_path ) ? $origin->plugin_path : $origin->pluginPath );
109
+ } else {
110
+ $plugin_path = trailingslashit( dirname( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) ) );
 
 
 
 
 
 
 
 
111
  }
112
 
113
+ $file = $plugin_path . $path;
114
 
115
+ // Turn the Path into a URL
116
+ $url = str_replace( wp_normalize_path( WP_CONTENT_DIR ), WP_CONTENT_URL, $file );
117
+
118
+ // Make it compatible with Windows and other OS
119
+ $url = str_replace( DIRECTORY_SEPARATOR, '/', $url );
120
 
121
  /**
122
  * Filters the resource URL
124
  * @param $url
125
  * @param $resource
126
  */
127
+ $url = apply_filters( 'tribe_resource_url', $url, $resource );
128
 
129
  /**
130
  * Deprected the tribe_events_resource_url filter in 4.0 in favor of tribe_resource_url. Remove in 5.0
467
  return Tribe__Date_Utils::unescape_date_format($value);
468
  }
469
  }
470
+
471
+ /**
472
+ * Shortcut for Tribe__Admin__Notices::register(), create a Admin Notice easily
473
+ *
474
+ * @param string $slug Slug to save the notice
475
+ * @param callable|string $callback A callable Method/Fuction to actually display the notice
476
+ * @param array $arguments Arguments to Setup a notice
477
+ *
478
+ * @return stdClass Which notice was registered
479
+ */
480
+ function tribe_notice( $slug, $callback, $arguments = array() ) {
481
+ return Tribe__Admin__Notices::instance()->register( $slug, $callback, $arguments );
482
+ }
483
+
484
+ /**
485
+ * A quick internal way of sending errors using WP_Error
486
+ *
487
+ * @param string|array $indexes Which Error we are looking for
488
+ * @param array $context Gives the Error context
489
+ * @param array $sprintf Allows variables on the message
490
+ *
491
+ * @return WP_Error
492
+ */
493
+ function tribe_error( $indexes, $context = array(), $sprintf = array() ) {
494
+ return Tribe__Error::instance()->send( $indexes, $context, $sprintf );
495
+ }
496
+
497
+ /**
498
+ * Register a new error based on a Namespace
499
+ *
500
+ * @param string|array $indexes A list of the namespaces and last item should be the error name
501
+ * @param string $message What is going to be the message associate with this indexes
502
+ *
503
+ * @return boolean
504
+ */
505
+ function tribe_register_error( $indexes, $message ) {
506
+ return Tribe__Error::instance()->register( $indexes, $message );
507
+ }
508
+
509
+ /**
510
+ * Shortcut for Tribe__Assets::register(), include a single asset
511
+ *
512
+ * @param object $origin The main Object for the plugin you are enqueueing the script/style for
513
+ * @param string $slug Slug to save the asset
514
+ * @param string $file Which file will be loaded, either CSS or JS
515
+ * @param array $deps Dependencies
516
+ * @param string $action A WordPress Action, needs to happen after: `wp_enqueue_scripts`, `admin_enqueue_scripts`, or `login_enqueue_scripts`
517
+ * @param array $arguments Look at `Tribe__Assets::register()` for more info
518
+ *
519
+ * @return array Which Assets was registered
520
+ */
521
+ function tribe_asset( $origin, $slug, $file, $deps = array(), $action = null, $arguments = array() ) {
522
+ return Tribe__Assets::instance()->register( $origin, $slug, $file, $deps, $action, $arguments );
523
+ }
524
+
525
+ /**
526
+ * Function to include more the one asset, based on `tribe_asset`
527
+ *
528
+ * @param object $origin The main Object for the plugin you are enqueueing the script/style for
529
+ * @param array $assets {
530
+ * Indexed array, don't use any associative key.
531
+ * E.g.: array( 'slug-my-script', 'my/own/path.js', array( 'jquery' ) )
532
+ *
533
+ * @type string $slug Slug to save the asset
534
+ * @type string $file Which file will be loaded, either CSS or JS
535
+ * @type array $deps (optional) Dependencies
536
+ * }
537
+ * @param string $action A WordPress Action, needs to happen after: `wp_enqueue_scripts`, `admin_enqueue_scripts`, or `login_enqueue_scripts`
538
+ * @param array $arguments Look at `Tribe__Assets::register()` for more info
539
+ *
540
+ * @return array Which Assets were registered
541
+ */
542
+ function tribe_assets( $origin, $assets, $action = null, $arguments = array() ) {
543
+ $registered = array();
544
+
545
+ foreach ( $assets as $asset ) {
546
+ if ( ! is_array( $asset ) ) {
547
+ continue;
548
+ }
549
+
550
+ $slug = reset( $asset );
551
+ if ( empty( $asset[1] ) ) {
552
+ continue;
553
+ }
554
+
555
+ $file = $asset[1];
556
+ $deps = ! empty( $asset[2] ) ? $asset[2] : array();
557
+
558
+ $registered[] = tribe_asset( $origin, $slug, $file, $deps, $action, $arguments );
559
+ }
560
+
561
+ return $registered;
562
+ }
common/src/functions/utils.php CHANGED
@@ -57,3 +57,65 @@ if ( ! function_exists( 'tribe_register_plugin' ) ) {
57
  return false;
58
  }
59
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  return false;
58
  }
59
  }
60
+
61
+ if ( ! function_exists( 'tribe_append_path' ) ) {
62
+ /**
63
+ * Append a path fragment to a URL preserving query arguments
64
+ * and fragments.
65
+ *
66
+ * @param string $url A full URL in the `http://example.com/?query=var#frag` format.
67
+ * @param string $path The path to append to the existing, if any, one., e.g. `/some/path`
68
+ *
69
+ * @return mixed|string
70
+ *
71
+ * @since 4.3
72
+ */
73
+ function tribe_append_path( $url, $path ) {
74
+ $path = trim( $path, '/' );
75
+
76
+ $query = @parse_url( $url, PHP_URL_QUERY );
77
+ $frag = @parse_url( $url, PHP_URL_FRAGMENT );
78
+
79
+ if ( ! ( empty( $query ) && empty( $frag ) ) ) {
80
+ $url = str_replace( '?' . $query, '', $url );
81
+ $url = str_replace( '#' . $frag, '', $url );
82
+ $query = $query ? '?' . $query : '';
83
+ $frag = $frag ? '#' . $frag : '';
84
+ }
85
+
86
+ $url = trailingslashit( esc_url_raw( trailingslashit( $url ) . $path ) );
87
+ $url .= $query . $frag;
88
+
89
+ return $url;
90
+ }
91
+ }
92
+
93
+ if ( ! function_exists( 'tribe_exit' ) ) {
94
+ /**
95
+ * Filterable `die` wrapper.
96
+ *
97
+ * @param string $status
98
+ *
99
+ * @return void|mixed Depending on the handler this function might return
100
+ * a value or `die` before anything is returned.
101
+ */
102
+ function tribe_exit( $status = '' ) {
103
+ $handler = 'die';
104
+
105
+ /**
106
+ * Filters the callback to call in place of `die()`.
107
+ *
108
+ * @param callable $handler The `die` replacement callback.
109
+ * @param string $status The exit/die status.
110
+ */
111
+ $handler = apply_filters( 'tribe_exit', $handler, $status );
112
+
113
+ // Die and exit are language constructs that cannot be used as callbacks on all PHP runtimes
114
+ if ( 'die' === $handler || 'exit' === $handler ) {
115
+ exit;
116
+ }
117
+
118
+ return call_user_func( $handler, $status );
119
+ }
120
+ }
121
+
common/src/resources/css/app-shop.css CHANGED
@@ -8,59 +8,70 @@
8
  * @see: http://moderntribe.github.io/products-engineering/css/
9
  */
10
 
11
- .tribe-addon {
12
- box-sizing: border-box;
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  display: inline-block;
14
- margin-right: 10px;
15
  overflow: hidden;
16
- padding: 20px 20px 20px 0;
 
17
  vertical-align: top;
18
  width: 300px;
 
 
19
  }
20
-
21
- .tribe-addon .thumb img {
22
- max-width: 100%;
23
- width: 100%;
24
- }
25
-
26
- .tribe-addon h4 {
27
  font-size: 1.17em;
 
28
  }
29
-
30
- .tribe-addon h4 a {
31
  text-decoration: none;
32
  }
33
-
34
- .addon-grid {
35
- width: 100%;
36
- }
37
-
38
- .category-title {
39
- clear: both;
40
  display: block;
41
- margin-bottom: 10px;
 
42
  }
43
-
44
- .tribe-addon.first {
45
- border-bottom: 1px solid #dfdfdf;
46
- margin: 20px 0 10px;
 
 
 
 
 
 
47
  overflow: hidden;
48
- padding: 0 0 20px 330px;
49
- width: 100%;
50
  }
51
-
52
- .tribe-addon.first h4 {
53
  font-size: 20px;
54
  line-height: 1.4;
55
- margin: 0;
56
  }
57
-
58
- .tribe-addon.first .thumb {
 
 
 
 
59
  float: left;
60
- margin-left: -330px;
61
  width: 300px;
62
  }
63
-
64
- .tribe-addon.first .description {
65
- max-width: 600px;
66
- }
8
  * @see: http://moderntribe.github.io/products-engineering/css/
9
  */
10
 
11
+ #tribe-app-shop {
12
+ max-width: 960px;
13
+ }
14
+ #tribe-app-shop .addon-grid {
15
+ width: 100%;
16
+ }
17
+ #tribe-app-shop .header h1 {
18
+ display: inline-block;
19
+ }
20
+ #tribe-app-shop .header .button {
21
+ margin-top: 10px;
22
+ }
23
+ #tribe-app-shop .tribe-addon {
24
+ background-color: #fff;
25
+ border-bottom: 1px solid #dfdfdf;
26
  display: inline-block;
27
+ margin: 0 15px 15px 0;
28
  overflow: hidden;
29
+ padding: 0;
30
+ position: relative;
31
  vertical-align: top;
32
  width: 300px;
33
+ border-radius: 3px 3px 3px 3px;
34
+ box-sizing: border-box;
35
  }
36
+ #tribe-app-shop .tribe-addon h4 {
 
 
 
 
 
 
37
  font-size: 1.17em;
38
+ margin: 15px 0;
39
  }
40
+ #tribe-app-shop .tribe-addon h4 a {
 
41
  text-decoration: none;
42
  }
43
+ #tribe-app-shop .tribe-addon .button {
44
+ bottom: 15px;
 
 
 
 
 
45
  display: block;
46
+ position: absolute;
47
+ width: 123px;
48
  }
49
+ #tribe-app-shop .tribe-addon .caption {
50
+ padding: 0 15px 45px 15px;
51
+ }
52
+ #tribe-app-shop .tribe-addon .thumb img {
53
+ height: 228px;
54
+ max-width: 100%;
55
+ width: 300px;
56
+ }
57
+ #tribe-app-shop .tribe-addon.first {
58
+ margin: 20px 0;
59
  overflow: hidden;
60
+ padding: 0;
61
+ width: 937px;
62
  }
63
+ #tribe-app-shop .tribe-addon.first h4 {
 
64
  font-size: 20px;
65
  line-height: 1.4;
66
+ margin: 15px 0 0 0;
67
  }
68
+ #tribe-app-shop .tribe-addon.first .caption {
69
+ display: inline-block;
70
+ padding-left: 20px;
71
+ width: 600px;
72
+ }
73
+ #tribe-app-shop .tribe-addon.first .thumb {
74
  float: left;
75
+ height: 228px;
76
  width: 300px;
77
  }
 
 
 
 
common/src/resources/css/app-shop.min.css CHANGED
@@ -1 +1 @@
1
- .tribe-addon{box-sizing:border-box;display:inline-block;margin-right:10px;overflow:hidden;padding:20px 20px 20px 0;vertical-align:top;width:300px}.tribe-addon .thumb img{max-width:100%;width:100%}.tribe-addon h4{font-size:1.17em}.tribe-addon h4 a{text-decoration:none}.addon-grid{width:100%}.category-title{clear:both;display:block;margin-bottom:10px}.tribe-addon.first{border-bottom:1px solid #dfdfdf;margin:20px 0 10px;overflow:hidden;padding:0 0 20px 330px;width:100%}.tribe-addon.first h4{font-size:20px;line-height:1.4;margin:0}.tribe-addon.first .thumb{float:left;margin-left:-330px;width:300px}.tribe-addon.first .description{max-width:600px}
1
+ #tribe-app-shop{max-width:960px}#tribe-app-shop .addon-grid{width:100%}#tribe-app-shop .header h1{display:inline-block}#tribe-app-shop .header .button{margin-top:10px}#tribe-app-shop .tribe-addon{background-color:#fff;border-bottom:1px solid #dfdfdf;display:inline-block;margin:0 15px 15px 0;overflow:hidden;padding:0;position:relative;vertical-align:top;width:300px;border-radius:3px 3px 3px 3px;box-sizing:border-box}#tribe-app-shop .tribe-addon h4{font-size:1.17em;margin:15px 0}#tribe-app-shop .tribe-addon h4 a{text-decoration:none}#tribe-app-shop .tribe-addon .button{bottom:15px;display:block;position:absolute;width:123px}#tribe-app-shop .tribe-addon .caption{padding:0 15px 45px}#tribe-app-shop .tribe-addon .thumb img{height:228px;max-width:100%;width:300px}#tribe-app-shop .tribe-addon.first{margin:20px 0;overflow:hidden;padding:0;width:937px}#tribe-app-shop .tribe-addon.first h4{font-size:20px;line-height:1.4;margin:15px 0 0}#tribe-app-shop .tribe-addon.first .caption{display:inline-block;padding-left:20px;width:600px}#tribe-app-shop .tribe-addon.first .thumb{float:left;height:228px;width:300px}
common/src/resources/css/bumpdown.css ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This CSS file was auto-generated via PostCSS
3
+ *
4
+ * Contributors should avoid editing this file, but instead edit the associated
5
+ * src/resources/postcss/ file. For more information, check out our engineering
6
+ * docs on how we handle CSS in our engineering docs.
7
+ *
8
+ * @see: http://moderntribe.github.io/products-engineering/css/
9
+ */
10
+
11
+ /* = Bumpdown CSS
12
+ =============================================*/
13
+ .wrap .tribe-bumpdown,
14
+ .tribe-bumpdown,
15
+ .tribe-bumpdown-cell.tribe-bumpdown {
16
+ display: none;
17
+ margin: 16px 0;
18
+ margin: 1rem 0;
19
+ }
20
+ .wrap .tribe-bumpdown .tribe-bumpdown-content, .tribe-bumpdown .tribe-bumpdown-content, .tribe-bumpdown-cell.tribe-bumpdown .tribe-bumpdown-content {
21
+ background: #f1f1f1;
22
+ padding: 10px 35px 10px 10px;
23
+ font-size: 12px;
24
+ position: relative;
25
+ }
26
+ .tribe-bumpdown-cell.tribe-bumpdown {
27
+ padding: 0;
28
+ }
29
+ #poststuff .tribe-bumpdown h1,
30
+ #poststuff .tribe-bumpdown h2,
31
+ #poststuff .tribe-bumpdown h3,
32
+ #poststuff .tribe-bumpdown h4,
33
+ .tribe-bumpdown h1,
34
+ .tribe-bumpdown h2,
35
+ .tribe-bumpdown h3,
36
+ .tribe-bumpdown h4 {
37
+ padding-left: 0;
38
+ padding-top: 0;
39
+ }
40
+ .tribe-bumpdown-arrow {
41
+ position: absolute;
42
+ width: 0;
43
+ height: 0;
44
+ top: -11px;
45
+ margin-left: -18px;
46
+ border-left: 8px solid transparent;
47
+ border-right: 8px solid transparent;
48
+
49
+ border-bottom: 11px solid #f1f1f1;
50
+ }
51
+ .tribe-bumpdown-close {
52
+ color: #686868;
53
+ cursor: pointer;
54
+ position: absolute;
55
+ right: 8px;
56
+ right: .5rem;
57
+ top: 8px;
58
+ top: .5rem;
59
+ z-index: 2;
60
+ }
61
+ .tribe-bumpdown-trigger .target {
62
+ color: #0074a2;
63
+ }
64
+ @media screen and (max-width: 782px) {
65
+ .wrap td.tribe-bumpdown {
66
+ padding-right: 35px;
67
+ }
68
+ .tribe-bumpdown-arrow {
69
+ margin-left: -15px;
70
+ }
71
+ }
common/src/resources/css/bumpdown.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .tribe-bumpdown,.tribe-bumpdown-cell.tribe-bumpdown,.wrap .tribe-bumpdown{display:none;margin:16px 0;margin:1rem 0}.tribe-bumpdown-cell.tribe-bumpdown .tribe-bumpdown-content,.tribe-bumpdown .tribe-bumpdown-content,.wrap .tribe-bumpdown .tribe-bumpdown-content{background:#f1f1f1;padding:10px 35px 10px 10px;font-size:12px;position:relative}.tribe-bumpdown-cell.tribe-bumpdown{padding:0}#poststuff .tribe-bumpdown h1,#poststuff .tribe-bumpdown h2,#poststuff .tribe-bumpdown h3,#poststuff .tribe-bumpdown h4,.tribe-bumpdown h1,.tribe-bumpdown h2,.tribe-bumpdown h3,.tribe-bumpdown h4{padding-left:0;padding-top:0}.tribe-bumpdown-arrow{position:absolute;width:0;height:0;top:-11px;margin-left:-18px;border-left:8px solid transparent;border-right:8px solid transparent;border-bottom:11px solid #f1f1f1}.tribe-bumpdown-close{color:#686868;cursor:pointer;position:absolute;right:8px;right:.5rem;top:8px;top:.5rem;z-index:1}.tribe-bumpdown-trigger .target{color:#0074a2}@media screen and (max-width:782px){.wrap td.tribe-bumpdown{padding-right:35px}.tribe-bumpdown-arrow{margin-left:-15px}}
common/src/resources/css/datatables.css ADDED
@@ -0,0 +1,395 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This CSS file was auto-generated via PostCSS
3
+ *
4
+ * Contributors should avoid editing this file, but instead edit the associated
5
+ * src/resources/postcss/ file. For more information, check out our engineering
6
+ * docs on how we handle CSS in our engineering docs.
7
+ *
8
+ * @see: http://moderntribe.github.io/products-engineering/css/
9
+ */
10
+
11
+ /*
12
+ * Table styles
13
+ */
14
+ table.dataTable {
15
+ border-collapse: separate;
16
+ border-spacing: 0;
17
+ clear: both;
18
+ margin: 0 auto;
19
+ width: 100%;
20
+ }
21
+ table.dataTable thead th:active,
22
+ table.dataTable thead td:active {
23
+ outline: none;
24
+ }
25
+ table.dataTable thead .sorting,
26
+ table.dataTable thead .sorting_asc,
27
+ table.dataTable thead .sorting_desc {
28
+ cursor: pointer;
29
+ cursor: hand;
30
+ }
31
+ table.dataTable thead .sorting,
32
+ table.dataTable thead .sorting_asc,
33
+ table.dataTable thead .sorting_desc,
34
+ table.dataTable thead .sorting_asc_disabled,
35
+ table.dataTable thead .sorting_desc_disabled {
36
+ background-position: center right;
37
+ background-repeat: no-repeat;
38
+ }
39
+ table.dataTable thead .sorting {
40
+ background-image: url("../../../vendor/datatables/media/images/sort_both.png");
41
+ }
42
+ table.dataTable thead .sorting_asc {
43
+ background-image: url("../../../vendor/datatables/media/images/sort_asc.png");
44
+ }
45
+ table.dataTable thead .sorting_desc {
46
+ background-image: url("../../../vendor/datatables/media/images/sort_desc.png");
47
+ }
48
+ table.dataTable thead .sorting_asc_disabled {
49
+ background-image: url("../../../vendor/datatables/media/images/sort_asc_disabled.png");
50
+ }
51
+ table.dataTable thead .sorting_desc_disabled {
52
+ background-image: url("../../../vendor/datatables/media/images/sort_desc_disabled.png");
53
+ }
54
+ table.dataTable.widefat thead th input, table.dataTable.widefat thead td input, table.dataTable.widefat tfoot th input, table.dataTable.widefat tfoot td input {
55
+ margin: 0 0 0 8px;
56
+ vertical-align: text-top;
57
+ }
58
+ table.dataTable.widefat thead td.check-column, table.dataTable.widefat tfoot td.check-column {
59
+ padding-top: 4px;
60
+ vertical-align: middle;
61
+ }
62
+ table.dataTable.widefat thead th.check-column, table.dataTable.widefat tfoot th.check-column, table.dataTable.widefat tbody th.check-column {
63
+ padding: 11px 0 0 3px;
64
+ }
65
+ table.dataTable .check-column {
66
+ width: 2.2em;
67
+ }
68
+ table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, table.dataTable.display tbody th, table.dataTable.display tbody td {
69
+ border-top: 1px solid #ddd;
70
+ }
71
+ table.dataTable.row-border tbody tr:first-child th,
72
+ table.dataTable.row-border tbody tr:first-child td,
73
+ table.dataTable.display tbody tr:first-child th,
74
+ table.dataTable.display tbody tr:first-child td {
75
+ border-top: none;
76
+ }
77
+ table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody td {
78
+ border-top: 1px solid #ddd;
79
+ border-right: 1px solid #ddd;
80
+ }
81
+ table.dataTable.cell-border tbody tr th:first-child,
82
+ table.dataTable.cell-border tbody tr td:first-child {
83
+ border-left: 1px solid #ddd;
84
+ }
85
+ table.dataTable.cell-border tbody tr:first-child th,
86
+ table.dataTable.cell-border tbody tr:first-child td {
87
+ border-top: none;
88
+ }
89
+ table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd {
90
+ background-color: #f9f9f9;
91
+ }
92
+ table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover {
93
+ background-color: #f6f6f6;
94
+ }
95
+ table.dataTable.order-column tbody tr > .sorting_1,
96
+ table.dataTable.order-column tbody tr > .sorting_2,
97
+ table.dataTable.order-column tbody tr > .sorting_3,
98
+ table.dataTable.display tbody tr > .sorting_1,
99
+ table.dataTable.display tbody tr > .sorting_2,
100
+ table.dataTable.display tbody tr > .sorting_3 {
101
+ background-color: #fafafa;
102
+ }
103
+ table.dataTable.display tbody tr.odd > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 {
104
+ background-color: #f1f1f1;
105
+ }
106
+ table.dataTable.display tbody tr.odd > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 {
107
+ background-color: #f3f3f3;
108
+ }
109
+ table.dataTable.display tbody tr.odd > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 {
110
+ background-color: whitesmoke;
111
+ }
112
+ table.dataTable.display tbody tr.even > .sorting_1, table.dataTable.order-column.stripe tbody tr.even > .sorting_1 {
113
+ background-color: #fafafa;
114
+ }
115
+ table.dataTable.display tbody tr.even > .sorting_2, table.dataTable.order-column.stripe tbody tr.even > .sorting_2 {
116
+ background-color: #fcfcfc;
117
+ }
118
+ table.dataTable.display tbody tr.even > .sorting_3, table.dataTable.order-column.stripe tbody tr.even > .sorting_3 {
119
+ background-color: #fefefe;
120
+ }
121
+ table.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 {
122
+ background-color: #eaeaea;
123
+ }
124
+ table.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 {
125
+ background-color: #ececec;
126
+ }
127
+ table.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 {
128
+ background-color: #efefef;
129
+ }
130
+ table.dataTable.no-footer {
131
+ border-bottom: 1px solid #111;
132
+ }
133
+ table.dataTable.nowrap th, table.dataTable.nowrap td {
134
+ white-space: nowrap;
135
+ }
136
+ table.dataTable.compact thead th,
137
+ table.dataTable.compact thead td {
138
+ padding: 4px 17px 4px 4px;
139
+ }
140
+ table.dataTable.compact tfoot th,
141
+ table.dataTable.compact tfoot td {
142
+ padding: 4px;
143
+ }
144
+ table.dataTable.compact tbody th,
145
+ table.dataTable.compact tbody td {
146
+ padding: 4px;
147
+ }
148
+ table.dataTable th.dt-left,
149
+ table.dataTable td.dt-left {
150
+ text-align: left;
151
+ }
152
+ table.dataTable th.dt-center,
153
+ table.dataTable td.dt-center,
154
+ table.dataTable td.dataTables_empty {
155
+ text-align: center;
156
+ }
157
+ table.dataTable th.dt-right,
158
+ table.dataTable td.dt-right {
159
+ text-align: right;
160
+ }
161
+ table.dataTable th.dt-justify,
162
+ table.dataTable td.dt-justify {
163
+ text-align: justify;
164
+ }
165
+ table.dataTable th.dt-nowrap,
166
+ table.dataTable td.dt-nowrap {
167
+ white-space: nowrap;
168
+ }
169
+ table.dataTable thead th.dt-head-left,
170
+ table.dataTable thead td.dt-head-left,
171
+ table.dataTable tfoot th.dt-head-left,
172
+ table.dataTable tfoot td.dt-head-left {
173
+ text-align: left;
174
+ }
175
+ table.dataTable thead th.dt-head-center,
176
+ table.dataTable thead td.dt-head-center,
177
+ table.dataTable tfoot th.dt-head-center,
178
+ table.dataTable tfoot td.dt-head-center {
179
+ text-align: center;
180
+ }
181
+ table.dataTable thead th.dt-head-right,
182
+ table.dataTable thead td.dt-head-right,
183
+ table.dataTable tfoot th.dt-head-right,
184
+ table.dataTable tfoot td.dt-head-right {
185
+ text-align: right;
186
+ }
187
+ table.dataTable thead th.dt-head-justify,
188
+ table.dataTable thead td.dt-head-justify,
189
+ table.dataTable tfoot th.dt-head-justify,
190
+ table.dataTable tfoot td.dt-head-justify {
191
+ text-align: justify;
192
+ }
193
+ table.dataTable thead th.dt-head-nowrap,
194
+ table.dataTable thead td.dt-head-nowrap,
195
+ table.dataTable tfoot th.dt-head-nowrap,
196
+ table.dataTable tfoot td.dt-head-nowrap {
197
+ white-space: nowrap;
198
+ }
199
+ table.dataTable tbody th.dt-body-left,
200
+ table.dataTable tbody td.dt-body-left {
201
+ text-align: left;
202
+ }
203
+ table.dataTable tbody th.dt-body-center,
204
+ table.dataTable tbody td.dt-body-center {
205
+ text-align: center;
206
+ }
207
+ table.dataTable tbody th.dt-body-right,
208
+ table.dataTable tbody td.dt-body-right {
209
+ text-align: right;
210
+ }
211
+ table.dataTable tbody th.dt-body-justify,
212
+ table.dataTable tbody td.dt-body-justify {
213
+ text-align: justify;
214
+ }
215
+ table.dataTable tbody th.dt-body-nowrap,
216
+ table.dataTable tbody td.dt-body-nowrap {
217
+ white-space: nowrap;
218
+ }
219
+ table.dataTable,
220
+ table.dataTable th,
221
+ table.dataTable td {
222
+ box-sizing: content-box;
223
+ }
224
+ /*
225
+ * Control feature layout
226
+ */
227
+ .dataTables_wrapper {
228
+ clear: both;
229
+ position: relative;
230
+ zoom: 1;
231
+ }
232
+ .dataTables_wrapper .dataTables_length {
233
+ float: left;
234
+ }
235
+ .dataTables_wrapper .dataTables_filter {
236
+ float: right;
237
+ text-align: right;
238
+ }
239
+ .dataTables_wrapper .dataTables_filter input {
240
+ margin-left: 0.5em;
241
+ }
242
+ .dataTables_wrapper .dataTables_info {
243
+ clear: both;
244
+ float: left;
245
+ padding-top: 0.755em;
246
+ }
247
+ .dataTables_wrapper .dataTables_paginate {
248
+ float: right;
249
+ padding-top: 0.25em;
250
+ text-align: right;
251
+ }
252
+ .dataTables_wrapper .dataTables_paginate .paginate_button {
253
+ border-color: transparent;
254
+ border-radius: 3px;
255
+ border-style: solid;
256
+ border-width: 1px;
257
+ box-shadow: none;
258
+ box-sizing: border-box;
259
+ color: #555;
260
+ cursor: pointer;
261
+ display: inline-block;
262
+ font-size: 13px;
263
+ height: 28px;
264
+ line-height: 26px;
265
+ margin: 0 0 0 2px;
266
+ padding: 0 10px 1px;
267
+ text-decoration: none;
268
+ vertical-align: top;
269
+ white-space: nowrap;
270
+ }
271
+ .dataTables_wrapper .dataTables_paginate .paginate_button:hover,
272
+ .dataTables_wrapper .dataTables_paginate .paginate_button:active {
273
+ background: #fafafa;
274
+ border-color: #999;
275
+ color: #23282d;
276
+ }
277
+ .dataTables_wrapper .dataTables_paginate .paginate_button.next,
278
+ .dataTables_wrapper .dataTables_paginate .paginate_button.previous {
279
+ background: transparent;
280
+ border-color: transparent;
281
+ box-shadow: none;
282
+ color: #23282d;
283
+ }
284
+ .dataTables_wrapper .dataTables_paginate .paginate_button.next.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.previous.disabled {
285
+ background: transparent;
286
+ border-color: transparent;
287
+ box-shadow: none;
288
+ }
289
+ .dataTables_wrapper .dataTables_paginate .paginate_button.next.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.previous.disabled:hover {
290
+ background: transparent;
291
+ border-color: transparent;
292
+ box-shadow: none;
293
+ text-decoration: none;
294
+ }
295
+ .dataTables_wrapper .dataTables_paginate .paginate_button.next:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.previous:hover {
296
+ color: #222;
297
+ text-decoration: underline;
298
+ }
299
+ .dataTables_wrapper .dataTables_paginate .paginate_button.current,
300
+ .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
301
+ background: #fcfcfc;
302
+ border-color: #999;
303
+ box-shadow: 0 1px 0 #cccccc;
304
+ color: #23282d;
305
+ text-decoration: none;
306
+ }
307
+ .dataTables_wrapper .dataTables_paginate .paginate_button.disabled,
308
+ .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,
309
+ .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {
310
+ background: #f7f7f7;
311
+ border-color: #ddd;
312
+ box-shadow: none;
313
+ color: #a0a5aa;
314
+ cursor: default;
315
+ text-shadow: 0 1px 0 #fff;
316
+ -webkit-transform: none;
317
+ transform: none;
318
+ }
319
+ .dataTables_wrapper .dataTables_paginate .ellipsis {
320
+ padding: 0 1em;
321
+ }
322
+ .dataTables_wrapper .dataTables_processing {
323
+ position: absolute;
324
+ top: 50%;
325
+ left: 50%;
326
+ width: 100%;
327
+ height: 40px;
328
+ margin-left: -50%;
329
+ margin-top: -25px;
330
+ padding-top: 20px;
331
+ text-align: center;
332
+ font-size: 1.2em;
333
+ background-color: white;
334
+ background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(25%, rgba(255, 255, 255, 0.9)), color-stop(75%, rgba(255, 255, 255, 0.9)), color-stop(100%, rgba(255, 255, 255, 0)));
335
+ background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
336
+ background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
337
+ }
338
+ .dataTables_wrapper .dataTables_length,
339
+ .dataTables_wrapper .dataTables_filter,
340
+ .dataTables_wrapper .dataTables_info,
341
+ .dataTables_wrapper .dataTables_processing,
342
+ .dataTables_wrapper .dataTables_paginate {
343
+ color: #333;
344
+ }
345
+ .dataTables_wrapper .dataTables_scroll {
346
+ clear: both;
347
+ }
348
+ .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody {
349
+ margin-top: -1px;
350
+ -webkit-overflow-scrolling: touch;
351
+ }
352
+ .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td {
353
+ vertical-align: middle;
354
+ }
355
+ .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th > div.dataTables_sizing,
356
+ .dataTables_wrapper .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td > div.dataTables_sizing {
357
+ height: 0;
358
+ overflow: hidden;
359
+ margin: 0 !important;
360
+ padding: 0 !important;
361
+ }
362
+ .dataTables_wrapper.no-footer .dataTables_scrollBody {
363
+ border-bottom: 1px solid #111;
364
+ }
365
+ .dataTables_wrapper.no-footer div.dataTables_scrollHead table,
366
+ .dataTables_wrapper.no-footer div.dataTables_scrollBody table {
367
+ border-bottom: none;
368
+ }
369
+ .dataTables_wrapper:after {
370
+ visibility: hidden;
371
+ display: block;
372
+ content: "";
373
+ clear: both;
374
+ height: 0;
375
+ }
376
+ @media screen and (max-width: 767px) {
377
+ .dataTables_wrapper .dataTables_info,
378
+ .dataTables_wrapper .dataTables_paginate {
379
+ float: none;
380
+ text-align: center;
381
+ }
382
+ .dataTables_wrapper .dataTables_paginate {
383
+ margin-top: 0.5em;
384
+ }
385
+ }
386
+ @media screen and (max-width: 640px) {
387
+ .dataTables_wrapper .dataTables_length,
388
+ .dataTables_wrapper .dataTables_filter {
389
+ float: none;
390
+ text-align: center;
391
+ }
392
+ .dataTables_wrapper .dataTables_filter {
393
+ margin-top: 0.5em;
394
+ }
395
+ }
common/src/resources/css/datatables.min.css ADDED
@@ -0,0 +1 @@
 
1
+ table.dataTable{border-collapse:separate;border-spacing:0;clear:both;margin:0 auto;width:100%}table.dataTable thead td:active,table.dataTable thead th:active{outline:none}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc{cursor:pointer;cursor:hand}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_desc_disabled{background-position:100%;background-repeat:no-repeat}table.dataTable thead .sorting{background-image:url(../../../vendor/datatables/media/images/sort_both.png)}table.dataTable thead .sorting_asc{background-image:url(../../../vendor/datatables/media/images/sort_asc.png)}table.dataTable thead .sorting_desc{background-image:url(../../../vendor/datatables/media/images/sort_desc.png)}table.dataTable thead .sorting_asc_disabled{background-image:url(../../../vendor/datatables/media/images/sort_asc_disabled.png)}table.dataTable thead .sorting_desc_disabled{background-image:url(../../../vendor/datatables/media/images/sort_desc_disabled.png)}table.dataTable.widefat tfoot td input,table.dataTable.widefat tfoot th input,table.dataTable.widefat thead td input,table.dataTable.widefat thead th input{margin:0 0 0 8px;vertical-align:text-top}table.dataTable.widefat tfoot td.check-column,table.dataTable.widefat thead td.check-column{padding-top:4px;vertical-align:middle}table.dataTable.widefat tbody th.check-column,table.dataTable.widefat tfoot th.check-column,table.dataTable.widefat thead th.check-column{padding:11px 0 0 3px}table.dataTable .check-column{width:2.2em}table.dataTable.display tbody td,table.dataTable.display tbody th,table.dataTable.row-border tbody td,table.dataTable.row-border tbody th{border-top:1px solid #ddd}table.dataTable.display tbody tr:first-child td,table.dataTable.display tbody tr:first-child th,table.dataTable.row-border tbody tr:first-child td,table.dataTable.row-border tbody tr:first-child th{border-top:none}table.dataTable.cell-border tbody td,table.dataTable.cell-border tbody th{border-top:1px solid #ddd;border-right:1px solid #ddd}table.dataTable.cell-border tbody tr td:first-child,table.dataTable.cell-border tbody tr th:first-child{border-left:1px solid #ddd}table.dataTable.cell-border tbody tr:first-child td,table.dataTable.cell-border tbody tr:first-child th{border-top:none}table.dataTable.display tbody tr.odd,table.dataTable.stripe tbody tr.odd{background-color:#f9f9f9}table.dataTable.display tbody tr:hover,table.dataTable.hover tbody tr:hover{background-color:#f6f6f6}table.dataTable.display tbody tr>.sorting_1,table.dataTable.display tbody tr>.sorting_2,table.dataTable.display tbody tr>.sorting_3,table.dataTable.order-column tbody tr>.sorting_1,table.dataTable.order-column tbody tr>.sorting_2,table.dataTable.order-column tbody tr>.sorting_3{background-color:#fafafa}table.dataTable.display tbody tr.odd>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd>.sorting_1{background-color:#f1f1f1}table.dataTable.display tbody tr.odd>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd>.sorting_2{background-color:#f3f3f3}table.dataTable.display tbody tr.odd>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd>.sorting_3{background-color:#f5f5f5}table.dataTable.display tbody tr.even>.sorting_1,table.dataTable.order-column.stripe tbody tr.even>.sorting_1{background-color:#fafafa}table.dataTable.display tbody tr.even>.sorting_2,table.dataTable.order-column.stripe tbody tr.even>.sorting_2{background-color:#fcfcfc}table.dataTable.display tbody tr.even>.sorting_3,table.dataTable.order-column.stripe tbody tr.even>.sorting_3{background-color:#fefefe}table.dataTable.display tbody tr:hover>.sorting_1,table.dataTable.order-column.hover tbody tr:hover>.sorting_1{background-color:#eaeaea}table.dataTable.display tbody tr:hover>.sorting_2,table.dataTable.order-column.hover tbody tr:hover>.sorting_2{background-color:#ececec}table.dataTable.display tbody tr:hover>.sorting_3,table.dataTable.order-column.hover tbody tr:hover>.sorting_3{background-color:#efefef}table.dataTable.no-footer{border-bottom:1px solid #111}table.dataTable.nowrap td,table.dataTable.nowrap th{white-space:nowrap}table.dataTable.compact thead td,table.dataTable.compact thead th{padding:4px 17px 4px 4px}table.dataTable.compact tbody td,table.dataTable.compact tbody th,table.dataTable.compact tfoot td,table.dataTable.compact tfoot th{padding:4px}table.dataTable td.dt-left,table.dataTable th.dt-left{text-align:left}table.dataTable td.dataTables_empty,table.dataTable td.dt-center,table.dataTable th.dt-center{text-align:center}table.dataTable td.dt-right,table.dataTable th.dt-right{text-align:right}table.dataTable td.dt-justify,table.dataTable th.dt-justify{text-align:justify}table.dataTable td.dt-nowrap,table.dataTable th.dt-nowrap{white-space:nowrap}table.dataTable tfoot td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable thead th.dt-head-left{text-align:left}table.dataTable tfoot td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable thead th.dt-head-center{text-align:center}table.dataTable tfoot td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable thead th.dt-head-right{text-align:right}table.dataTable tfoot td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable thead th.dt-head-justify{text-align:justify}table.dataTable tfoot td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable thead th.dt-head-nowrap{white-space:nowrap}table.dataTable tbody td.dt-body-left,table.dataTable tbody th.dt-body-left{text-align:left}table.dataTable tbody td.dt-body-center,table.dataTable tbody th.dt-body-center{text-align:center}table.dataTable tbody td.dt-body-right,table.dataTable tbody th.dt-body-right{text-align:right}table.dataTable tbody td.dt-body-justify,table.dataTable tbody th.dt-body-justify{text-align:justify}table.dataTable tbody td.dt-body-nowrap,table.dataTable tbody th.dt-body-nowrap{white-space:nowrap}table.dataTable,table.dataTable td,table.dataTable th{box-sizing:content-box}.dataTables_wrapper{clear:both;position:relative;zoom:1}.dataTables_wrapper .dataTables_length{float:left}.dataTables_wrapper .dataTables_filter{float:right;text-align:right}.dataTables_wrapper .dataTables_filter input{margin-left:.5em}.dataTables_wrapper .dataTables_info{clear:both;float:left;padding-top:.755em}.dataTables_wrapper .dataTables_paginate{float:right;padding-top:.25em;text-align:right}.dataTables_wrapper .dataTables_paginate .paginate_button{border:1px solid transparent;border-radius:3px;box-shadow:none;box-sizing:border-box;color:#555;cursor:pointer;display:inline-block;font-size:13px;height:28px;line-height:26px;margin:0 0 0 2px;padding:0 10px 1px;text-decoration:none;vertical-align:top;white-space:nowrap}.dataTables_wrapper .dataTables_paginate .paginate_button:active,.dataTables_wrapper .dataTables_paginate .paginate_button:hover{background:#fafafa;border-color:#999;color:#23282d}.dataTables_wrapper .dataTables_paginate .paginate_button.next,.dataTables_wrapper .dataTables_paginate .paginate_button.previous{background:transparent;border-color:transparent;box-shadow:none;color:#23282d}.dataTables_wrapper .dataTables_paginate .paginate_button.next.disabled,.dataTables_wrapper .dataTables_paginate .paginate_button.previous.disabled{background:transparent;border-color:transparent;box-shadow:none}.dataTables_wrapper .dataTables_paginate .paginate_button.next.disabled:hover,.dataTables_wrapper .dataTables_paginate .paginate_button.previous.disabled:hover{background:transparent;border-color:transparent;box-shadow:none;text-decoration:none}.dataTables_wrapper .dataTables_paginate .paginate_button.next:hover,.dataTables_wrapper .dataTables_paginate .paginate_button.previous:hover{color:#222;text-decoration:underline}.dataTables_wrapper .dataTables_paginate .paginate_button.current,.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover{background:#fcfcfc;border-color:#999;box-shadow:0 1px 0 #ccc;color:#23282d;text-decoration:none}.dataTables_wrapper .dataTables_paginate .paginate_button.disabled,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover{background:#f7f7f7;border-color:#ddd;box-shadow:none;color:#a0a5aa;cursor:default;text-shadow:0 1px 0 #fff;-webkit-transform:none;transform:none}.dataTables_wrapper .dataTables_paginate .ellipsis{padding:0 1em}.dataTables_wrapper .dataTables_processing{position:absolute;top:50%;left:50%;width:100%;height:40px;margin-left:-50%;margin-top:-25px;padding-top:20px;text-align:center;font-size:1.2em;background-color:#fff;background:-webkit-gradient(linear,left top,right top,color-stop(0,hsla(0,0%,100%,0)),color-stop(25%,hsla(0,0%,100%,.9)),color-stop(75%,hsla(0,0%,100%,.9)),color-stop(100%,hsla(0,0%,100%,0)));background:-webkit-linear-gradient(left,hsla(0,0%,100%,0),hsla(0,0%,100%,.9) 25%,hsla(0,0%,100%,.9) 75%,hsla(0,0%,100%,0));background:linear-gradient(90deg,hsla(0,0%,100%,0) 0,hsla(0,0%,100%,.9) 25%,hsla(0,0%,100%,.9) 75%,hsla(0,0%,100%,0))}.dataTables_wrapper .dataTables_filter,.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_paginate,.dataTables_wrapper .dataTables_processing{color:#333}.dataTables_wrapper .dataTables_scroll{clear:both}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody{margin-top:-1px;-webkit-overflow-scrolling:touch}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th{vertical-align:middle}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th>div.dataTables_sizing,.dataTables_wrapper .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td>div.dataTables_sizing{height:0;overflow:hidden;margin:0!important;padding:0!important}.dataTables_wrapper.no-footer .dataTables_scrollBody{border-bottom:1px solid #111}.dataTables_wrapper.no-footer div.dataTables_scrollBody table,.dataTables_wrapper.no-footer div.dataTables_scrollHead table{border-bottom:none}.dataTables_wrapper:after{visibility:hidden;display:block;content:"";clear:both;height:0}@media screen and (max-width:767px){.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_paginate{float:none;text-align:center}.dataTables_wrapper .dataTables_paginate{margin-top:.5em}}@media screen and (max-width:640px){.dataTables_wrapper .dataTables_filter,.dataTables_wrapper .dataTables_length{float:none;text-align:center}.dataTables_wrapper .dataTables_filter{margin-top:.5em}}
common/src/resources/css/dependency.css ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This CSS file was auto-generated via PostCSS
3
+ *
4
+ * Contributors should avoid editing this file, but instead edit the associated
5
+ * src/resources/postcss/ file. For more information, check out our engineering
6
+ * docs on how we handle CSS in our engineering docs.
7
+ *
8
+ * @see: http://moderntribe.github.io/products-engineering/css/
9
+ */
10
+
11
+ /* Dependency CSS */
12
+ .tribe-dependent {
13
+ display: none;
14
+ }
15
+ .tribe-dependent.tribe-active {
16
+ display: inline-block;
17
+ }
18
+ div.tribe-dependent.tribe-active {
19
+ display: block;
20
+ }
21
+ div.tribe-dependent.tribe-active.select2-container {
22
+ display: inline-block;
23
+ }
24
+ td.tribe-dependent.tribe-active {
25
+ display: table-cell;
26
+ }
27
+ tr.tribe-dependent.tribe-active {
28
+ display: table-row;
29
+ }
common/src/resources/css/dependency.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .tribe-dependent{display:none}.tribe-dependent.tribe-active{display:inline-block}div.tribe-dependent.tribe-active{display:block}div.tribe-dependent.tribe-active.select2-container{display:inline-block}td.tribe-dependent.tribe-active{display:table-cell}tr.tribe-dependent.tribe-active{display:table-row}
common/src/resources/css/tribe-common-admin.css CHANGED
@@ -124,7 +124,8 @@ table.plugins .tribe-plugin-update-message a {
124
  .tribe-settings-form .tribe-field-radio label,
125
  .tribe-settings-form .tribe-field-checkbox_list label {
126
  display: block;
127
- margin: 5px 0;
 
128
  }
129
  .tribe-settings-form .tribe-field-radio label input,
130
  .tribe-settings-form .tribe-field-checkbox_list label input {
@@ -145,9 +146,8 @@ table.plugins .tribe-plugin-update-message a {
145
  margin-bottom: 10px;
146
  padding: 6px 0 6px 12px;
147
  }
148
- .tribe-settings-form .tribe-settings-form-wrap h3 ~ h3 {
149
- margin-top: 2.25em;
150
- }
151
  .tribe-settings-form .tribe-settings-form-wrap h3 + p {
152
  margin: 0 0 10px;
153
  padding-left: 12px;
@@ -263,9 +263,11 @@ table.plugins .tribe-plugin-update-message a {
263
  .key-validity {
264
  display: inline-block;
265
  }
 
266
  .invalid-key {
267
  color: red;
268
  }
 
269
  .valid-key {
270
  color: green;
271
  }
@@ -409,7 +411,7 @@ table.plugins .tribe-plugin-update-message a {
409
  #tribe-log-controls {
410
  padding-bottom: 16px;
411
  padding-bottom: 1rem;
412
-
413
  /* For consistency with help screen h3 and p elements */
414
  padding-left: 12px;
415
  }
@@ -451,6 +453,12 @@ table.plugins .tribe-plugin-update-message a {
451
  margin-left: 25%;
452
  padding-left: 10px;
453
  }
 
 
 
 
 
 
454
  .template-updates-wrapper p {
455
  margin-top: 0;
456
  }
@@ -823,46 +831,123 @@ a.tribe-rating-link {
823
  .tribe-update-bar .progress .bar {
824
  background: #7ad03a;
825
  }
826
- /* bumpdown */
827
- .bumpdown {
828
- background: #f1f1f1;
829
- margin: 16px 0;
830
- margin: 1rem 0;
831
- padding: 16px 24px 16px 16px;
832
- padding: 1rem 1.5rem 1rem 1rem;
833
- position: relative;
834
- }
835
- #poststuff .bumpdown h1,
836
- #poststuff .bumpdown h2,
837
- #poststuff .bumpdown h3,
838
- #poststuff .bumpdown h4,
839
- .bumpdown h1,
840
- .bumpdown h2,
841
- .bumpdown h3,
842
- .bumpdown h4 {
843
- padding-left: 0;
844
- padding-top: 0;
845
  }
846
- .bumpdown-arrow {
847
  display: none;
848
  }
849
- .bumpdown-close {
850
- color: #686868;
851
- cursor: pointer;
852
- position: absolute;
853
- right: 8px;
854
- right: .5rem;
855
- top: 8px;
856
- top: .5rem;
857
- z-index: 2;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
858
  }
859
- .bumpdown-trigger .target {
860
- color: #0074a2;
 
 
861
  }
862
  /* Useful to ensure modals rise above the grey 'miasma' */
863
  .ui-front {
864
  z-index: 1000000;
865
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
866
  @media
867
  only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) {
868
  #tribe-loading span {
@@ -893,3 +978,11 @@ only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device
893
  display: none;
894
  }
895
  }
 
 
 
 
 
 
 
 
124
  .tribe-settings-form .tribe-field-radio label,
125
  .tribe-settings-form .tribe-field-checkbox_list label {
126
  display: block;
127
+ margin: 5px 0 5px 20px;
128
+ text-indent: -20px;
129
  }
130
  .tribe-settings-form .tribe-field-radio label input,
131
  .tribe-settings-form .tribe-field-checkbox_list label input {
146
  margin-bottom: 10px;
147
  padding: 6px 0 6px 12px;
148
  }
149
+ .tribe-settings-form .tribe-settings-form-wrap .tribe-sysinfo-optin-msg,
150
+ .tribe-settings-form .tribe-settings-form-wrap .system-info,
 
151
  .tribe-settings-form .tribe-settings-form-wrap h3 + p {
152
  margin: 0 0 10px;
153
  padding-left: 12px;
263
  .key-validity {
264
  display: inline-block;
265
  }
266
+ .optin-fail,
267
  .invalid-key {
268
  color: red;
269
  }
270
+ .optin-success,
271
  .valid-key {
272
  color: green;
273
  }
411
  #tribe-log-controls {
412
  padding-bottom: 16px;
413
  padding-bottom: 1rem;
414
+
415
  /* For consistency with help screen h3 and p elements */
416
  padding-left: 12px;
417
  }
453
  margin-left: 25%;
454
  padding-left: 10px;
455
  }
456
+ .system-info-copy .system-info-copy-btn {
457
+ padding: 6px;
458
+ }
459
+ .system-info-copy .system-info-copy-btn .dashicons {
460
+ padding-right: 10px;
461
+ }
462
  .template-updates-wrapper p {
463
  margin-top: 0;
464
  }
831
  .tribe-update-bar .progress .bar {
832
  background: #7ad03a;
833
  }
834
+ /* = Modals/thickbox dialogs
835
+ ============================ */
836
+ #tribe-dialog-wrapper > div {
837
+ padding: 16px;
838
+ padding: 1rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
839
  }
840
+ #tribe-dialog-wrapper > div .stage {
841
  display: none;
842
  }
843
+ #tribe-dialog-wrapper #heading {
844
+ background: white;
845
+ }
846
+ #tribe-dialog-wrapper label {
847
+ display: block;
848
+ }
849
+ #tribe-dialog-wrapper .select-single-container {
850
+ border: 1px solid #888;
851
+ overflow-y: scroll;
852
+ height: 300px;
853
+ }
854
+ #tribe-dialog-wrapper .select-single-container label {
855
+ opacity: 1;
856
+ padding: 3px 5px;
857
+ -webkit-transition: opacity 0.2s;
858
+ transition: opacity 0.2s;
859
+ }
860
+ #tribe-dialog-wrapper .select-single-container label:nth-child(odd) {
861
+ background: white;
862
+ }
863
+ #tribe-dialog-wrapper .select-single-container label.selected {
864
+ background: #0073aa;
865
+ color: white;
866
+ font-weight: bold;
867
+ }
868
+ #tribe-dialog-wrapper .select-single-container label input {
869
+ display: none;
870
  }
871
+ #tribe-dialog-wrapper .select-single-container.updating label {
872
+ opacity: 0.35;
873
+ -webkit-transition: opacity 0.2s;
874
+ transition: opacity 0.2s;
875
  }
876
  /* Useful to ensure modals rise above the grey 'miasma' */
877
  .ui-front {
878
  z-index: 1000000;
879
  }
880
+ /* Select2 Specific rule */
881
+ .select2-container .select2-choice abbr {
882
+ top: 6px;
883
+ }
884
+ .wp-list-table.plugins .column-description .update-message {
885
+ color: #d54e21;
886
+ }
887
+ .api-check {
888
+ padding: 1em;
889
+ }
890
+ .api-check + .notice-dismiss:hover:before {
891
+ color: #fff;
892
+ }
893
+ .api-check:before,
894
+ .api-check:after {
895
+ content: '';
896
+ display: table;
897
+ }
898
+ .api-check:after {
899
+ clear: both;
900
+ }
901
+ .api-check .tribe-spirit-animal {
902
+ background: #325f81;
903
+ background: -webkit-linear-gradient(45deg, #325f81 0%,#3d87c0 100%);
904
+ background: linear-gradient(45deg, #325f81 0%,#3d87c0 100%);
905
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#325f81', endColorstr='#3d87c0',GradientType=1 );
906
+ border-left: 4px solid #eee;
907
+ bottom: 0;
908
+ display: none;
909
+ padding: 16px;
910
+ padding: 1rem;
911
+ position: absolute;
912
+ right: 0;
913
+ top: 0;
914
+ width: 180px;
915
+ }
916
+ .api-check .tribe-spirit-animal:before {
917
+ content: '';
918
+ display: inline-block;
919
+ height: 100%;
920
+ width: 1%;
921
+ vertical-align: middle;
922
+ }
923
+ .api-check .tribe-spirit-animal img {
924
+ display: inline-block;
925
+ max-height: 100%;
926
+ max-width: 96%;
927
+ vertical-align: middle;
928
+ }
929
+ .api-check p {
930
+ line-height: 1.7;
931
+ margin-bottom: 1em;
932
+ }
933
+ .api-check a {
934
+ text-decoration: none;
935
+ }
936
+ .api-check a:hover {
937
+ text-decoration: underline;
938
+ }
939
+ .api-check .plugin-list {
940
+ display: inline;
941
+ font-weight: 600;
942
+ margin: 0;
943
+ padding: 0;
944
+ }
945
+ .api-check .plugin-list span.plugin-invalid:after {
946
+ content: ', ';
947
+ }
948
+ .api-check .plugin-list span.plugin-invalid:last-of-type:after {
949
+ content: '';
950
+ }
951
  @media
952
  only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) {
953
  #tribe-loading span {
978
  display: none;
979
  }
980
  }
981
+ @media screen and (min-width: 500px) {
982
+ .api-check .tribe-spirit-animal {
983
+ display: block;
984
+ }
985
+ .api-check .notice-content {
986
+ margin-right: 180px;
987
+ }
988
+ }
common/src/resources/css/tribe-common-admin.min.css CHANGED
@@ -1 +1 @@
1
- .invalid input{border:2px solid red!important}.valid input{border:1px solid green}.clearfix{zoom:1}.placeholder{color:#999;cursor:text;padding:4px}input:placeholder,textarea:placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.bubble{-khtml-border-radius:3px;background-color:#f9f9f9;border:1px solid #dfdfdf;border-radius:3px;border-spacing:0;border-style:solid;padding:10px}.tribe-sticky-tooltip{color:#bbb}td.tribe_message{padding-bottom:10px!important}#tribe_thanks{float:left;margin:5px 0 0;width:200px}.tribe_brand{font-family:Georgia!important;font-size:17px!important;font-weight:400;margin:8px 0}#tribe-upgrade{background:#f6f6f6;border:1px solid #ccc;border-radius:5px;margin:20px 0 30px;padding:0 20px 20px}#tribe-upgrade .message{background-color:#ffffe0;border:1px solid #e6db55;border-radius:3px;padding:6px 12px}table.plugins .tribe-plugin-update-message{background:#d54e21;color:#fff;display:inline-table;margin:6px 0;padding:10px 12px}table.plugins .tribe-plugin-update-message h4{display:inline;font-weight:700;margin-right:8px}table.plugins .tribe-plugin-update-message h4:after{content:' \00BB '}table.plugins .tribe-plugin-update-message a{color:#fff;text-decoration:underline}.tribe-settings-form{max-width:1000px}.tribe-settings-form fieldset{clear:both;display:inline-block;padding:10px 0}.tribe-settings-form legend{float:left;font-weight:700;margin-right:20px;width:220px}.tribe-settings-form fieldset.tribe-field-license_key legend{width:auto}.tribe-settings-form .tribe-field-wrap{float:left;max-width:500px}.tribe-settings-form .tribe-field-checkbox_list label,.tribe-settings-form .tribe-field-radio label{display:block;margin:5px 0}.tribe-settings-form .tribe-field-checkbox_list label input,.tribe-settings-form .tribe-field-radio label input{margin-right:5px}.tribe-settings-form .tribe-settings-form-wrap .description,.tribe-settings-form .tribe-settings-form-wrap fieldset,.tribe-settings-form fieldset[id^=tribe-field-geoloc_]{padding-left:12px}.tribe-settings-form .tribe-settings-form-wrap fieldset .description{margin-left:0;max-width:450px;padding-left:0}.tribe-settings-form .tribe-settings-form-wrap h3{background-color:#f9f9f9;margin-bottom:10px;padding:6px 0 6px 12px}.tribe-settings-form .tribe-settings-form-wrap h3~h3{margin-top:2.25em}.tribe-settings-form .tribe-settings-form-wrap h3+p{margin:0 0 10px;padding-left:12px}.tribe_settings .tribe-field-indent{margin-left:245px}.tribe_settings #pu_dashboard_message{display:none}.tribe_settings .tribe-errors-list{margin-left:15px}.tribe_settings .expiring-license{color:red}.tribe_settings .tribe-error{border:1px solid red}.tribe_settings .tribe-field-description{margin-bottom:0;position:relative;top:-12px}.tribe_settings #ical-link{top:-14px}.tribe-settings-form #tribe-field-stylesheetOption label{margin-left:20px}.tribe-settings-form #tribe-field-stylesheetOption input{margin-left:-20px;margin-right:8px}.tribe-settings-form #tribe-field-stylesheetOption p.description{color:#999}#modern-tribe-info{-khtml-border-radius:4px;background-color:#f9f9f9;border:1px solid #ccc;border-radius:4px;margin:20px 0;padding:8px 20px 12px}#modern-tribe-info img{height:18px;margin:10px 0;width:250px}#modern-tribe-info ul{list-style:disc;margin-left:20px}#modern-tribe-info ul ul{list-style:circle}.tribe-field-textarea.tribe-size-small textarea{height:60px;width:180px}.tribe-field-textarea.tribe-size-medium textarea{height:80px;width:300px}.tribe-field-textarea.tribe-size-large textarea{height:120px;width:450px}.tribe-field-license_key.tribe-size-small input,.tribe-field-text.tribe-size-small input{width:50px}.tribe-field-license_key.tribe-size-medium input,.tribe-field-text.tribe-size-medium input{width:225px}.tribe-field-license_key.tribe-size-large input,.tribe-field-text.tribe-size-large input{width:450px}.tribe-field-dropdown.tribe-size-small select{width:100px}.tribe-field-dropdown.tribe-size-medium select{width:300px}.tribe-field-dropdown.tribe-size-large select{width:450px}.tribe-field-dropdown_chosen.tribe-size-small select{width:100px}.tribe-field-dropdown_chosen.tribe-size-medium select{width:200px}.tribe-field-dropdown_chosen.tribe-size-large select{width:300px}.ajax-loading-license,.invalid-key,.valid-key{display:none;margin:0 5px}.ajax-loading-license{position:relative;top:5px}.key-validity{display:inline-block}.invalid-key{color:red}.valid-key{color:green}.valid-key.service-msg{color:#b72}#additional-field-table{margin-bottom:20px}.tribe-admin-box-left{float:left;width:20%}.tribe-admin-box-left,.tribe-admin-box-right{-khtml-border-radius:4px;background-color:#f9f9f9;border:1px solid #ccc;border-radius:4px;margin:20px 0;padding:0 20px 15px}.tribe-admin-box-right{float:right;width:68%}.ajax-loader{float:right;margin:10px}.tribe-arrangeable-item{border:1px solid #d3d3d3;border-radius:6px}.tribe-arrangeable-item .ui-state-default{border:none}.tribe-arrangeable-item-top{padding:6px}.tribe-arrangeable-item-top:hover{cursor:move}.tribe-arrangeable-action{float:right}.tribe-arrangeable-child{background-color:#f9f9f9;border-top:1px solid #d3d3d3;display:none;padding:25px}.tribe-arrangeable-child label{display:block;margin:0 0 7px}.tribe_events_active_filter_type_options{margin:10px 0}.tribe_events_active_filter_type_options label{margin:7px 0}.tribe-settings-form .tribe-settings-form-wrap fieldset .tribe-style-selection{margin-bottom:18px}#event_organizer td small,.OrganizerInfo td small{display:block;margin:0;max-width:250px}#event_organizer .organizer-email,.OrganizerInfo .organizer-email{vertical-align:top}.tribe-table-field-label{max-width:100%;width:200px}#tribe-help-general,#tribe-help-sidebar{float:left;margin-top:20px}#tribe-help-general p{margin-left:15px}#tribe-help-general ul{list-style-type:square}#tribe-help-general ol,#tribe-help-general ul{margin-bottom:20px;margin-left:35px}#tribe-help-general h3{background-color:#f9f9f9;margin-bottom:10px;padding:6px 0 6px 12px}#tribe-help-general h3~h3{margin-top:2.25em}#tribe-help-general h3+p{margin:0 0 20px;padding-left:12px}#tribe-help-general{width:65%}.tribe-help-section{padding-bottom:10px}.tribe-section-type-box{-khtml-border-radius:4px;background-color:#f9f9f9;border:1px solid #ccc;border-radius:4px;padding:8px 20px 12px}.tribe-section-type-box img{height:auto;margin:10px 0;max-width:300px}.tribe-section-type-box ul{list-style:disc;margin-left:20px}.tribe-section-type-box ul ul{list-style:circle}#tribe-log-controls{padding-bottom:16px;padding-bottom:1rem;padding-left:12px}#tribe-log-controls>div{display:inline-block;padding-right:16px;padding-right:1rem}#tribe-log-controls .working{opacity:1;-webkit-transition:opacity .2s;transition:opacity .2s}#tribe-log-controls .working.hidden{opacity:0;-webkit-transition:opacity .2s;transition:opacity .2s}#tribe-log-viewer,#tribe-system-info dl.support-stats,.template-updates-wrapper{background:#000;border-radius:2px;color:#888;max-height:400px;overflow:scroll;padding:10px}#tribe-system-info dl.support-stats dt,.template-updates-wrapper dt{clear:both;float:left;font-weight:700;text-transform:uppercase;width:25%}#tribe-system-info dl.support-stats dd,.template-updates-wrapper dd{margin-left:25%;padding-left:10px}.template-updates-wrapper p{margin-top:0}#tribe-help-sidebar{margin:20px 0 0 3%;max-width:225px;width:32%}.tribe-help-plugin-info{border:1px solid #ccc;padding:0 12px 12px}.tribe-help-plugin-info dd,.tribe-help-plugin-info dt{display:inline;margin:0}.tribe-help-plugin-info dt{font-weight:700}.tribe-help-plugin-info dd:after{content:'';display:block;height:.4em}.tribe-help-plugin-info dd:last-child:after{height:0}.tribe-help-plugin-info+.tribe-help-plugin-info{margin-top:20px}.tribe-help-plugin-info>div{line-height:2em}.tribe-help-plugin-info .star-rating{display:inline-block;margin-left:3px;position:relative;top:-2px}.tribe-help-plugin-info .tribe-list-addons{color:#21a6cb;font-size:24px;list-style:circle inside;margin-bottom:10px;margin-top:10px;padding-left:4px}.tribe-help-plugin-info .tribe-list-addons a{font-size:13px;left:-5px;position:relative;top:-5px}.tribe-help-plugin-info .tribe-list-addons .tribe-active-addon{list-style:disc inside}.ui-widget-overlay{background:#666;filter:Alpha(Opacity=50);opacity:.5}.ui-widget-shadow{background:#000;-webkit-border-radius:5px;-moz-border-radius:5px;filter:Alpha(Opacity=20);margin:-5px 0 0 -5px;opacity:.2;padding:5px}.ui-resizable{position:relative}.ui-resizable-handle{display:block;font-size:.1px;position:absolute;z-index:3}.ui-resizable-autohide .ui-resizable-handle,.ui-resizable-disabled .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;left:0;top:-5px;width:100%}.ui-resizable-s{bottom:-5px;cursor:s-resize;height:7px;left:0;width:100%}.ui-resizable-e{cursor:e-resize;height:100%;right:-5px;top:0;width:7px}.ui-resizable-w{cursor:w-resize;height:100%;left:-5px;top:0;width:7px}.ui-resizable-se{bottom:1px;cursor:se-resize;height:12px;right:1px;width:12px}.ui-resizable-sw{bottom:-5px;cursor:sw-resize;height:9px;left:-5px;width:9px}.ui-resizable-nw{cursor:nw-resize;height:9px;left:-5px;top:-5px;width:9px}.ui-resizable-ne{cursor:ne-resize;height:9px;right:-5px;top:-5px;width:9px}.ui-dialog{padding:.2em;position:relative;width:375px}.ui-dialog .ui-dialog-titlebar{padding:.5em .3em .3em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0 .2em}.ui-dialog .ui-dialog-titlebar-close{height:18px;margin:-10px 0 0;padding:1px;position:absolute;right:.3em;top:50%;width:19px}.ui-dialog .ui-dialog-titlebar-close span{display:block;margin-left:-8px;margin-top:-8px}.ui-dialog .ui-dialog-titlebar-close:focus,.ui-dialog .ui-dialog-titlebar-close:hover{padding:0}.ui-dialog .ui-dialog-content{background:none;border:0;overflow:auto;padding:.5em 1em;zoom:1}.ui-dialog .ui-dialog-buttonpane{background-image:none;border-width:1px 0 0;margin:.5em 0 0;padding:.3em 1em .5em!important;text-align:right}.ui-dialog .ui-dialog-buttonpane button{cursor:pointer;line-height:1.4em;margin:.5em .4em!important;overflow:visible;padding:.2em .6em .3em;text-shadow:none;width:auto}.ui-dialog .ui-resizable-se{bottom:3px;height:14px;right:3px;width:14px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:none!important;text-align:center}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button .ui-button-text{display:block;line-height:1.4}.ui-datepicker{font-size:8pt}#ui-datepicker-div{display:none}#tribe-loading{background:#fff;background:hsla(0,0%,100%,.8);display:none;height:100%;left:0;position:absolute;top:0;-webkit-transition:all 1s linear;transition:all 1s linear;webkit-transition:all 1s linear;width:100%;z-index:2}#tribe-loading span{background:url(../images/tribe-loading.gif) 0 0 no-repeat;background-size:32px 32px;height:32px;left:50%;margin:-16px 0 0 -16px;position:absolute;top:50%;width:32px}.tribe_update_page,.tribe_welcome_page{max-width:1000px}.tribe-half-column{float:left;margin-bottom:30px;margin-right:5%;width:45%}.tribe-row:after,.tribe-row:before{content:'';display:table}.tribe-row,.tribe-row:after{clear:both}.tribe-row .tribe-half-column:last-child{margin-right:0;width:50%}.tribe_update_page h2,.tribe_welcome_page h2{font-size:30px;line-height:1.2;margin-bottom:20px}.tribe_update_page h3,.tribe_welcome_page h3{font-size:24px;font-weight:400;line-height:24px;margin-top:0}.tribe_update_page h4,.tribe_welcome_page h4{font-size:18px;font-weight:600;line-height:18px;margin:0}.tribe_update_page p,.tribe_welcome_page p{font-size:14px}p.tribe-welcome-message{font-size:20px;font-weight:400}.tribe_update_page h2:before,.tribe_welcome_page h2:before{content:'\f145';font-family:dashicons;font-size:34px;line-height:1;margin-right:5px;position:relative;top:5px}.tribe-welcome-video-wrapper{height:0;margin-bottom:40px;padding-bottom:56.25%;padding-top:25px;position:relative}.tribe-welcome-video-wrapper iframe{height:100%;left:0;position:absolute;top:0;width:100%}a.tribe-rating-link{text-decoration:none}.tribe-update-links,.tribe-welcome-links{margin-top:30px}.tribe_update_page li:before,.tribe_welcome_page li:before{content:'\2022';padding-right:3px}.tribe_update_page .rss-widget{margin:1em 0}.tribe_update_page a.rsswidget{font-size:14px;font-weight:400;line-height:1}.tribe_update_page .rss-widget li:before{display:none}.tribe-update-bar{display:inline-block}.tribe-update-bar .progress{border:1px solid #ccc;float:left;margin-right:16px;margin-right:1rem;padding:1px;width:288px;width:18rem}.tribe-update-bar .progress .bar{background:#ffba00;height:16px;height:1rem;width:1%;background:#7ad03a}.bumpdown{background:#f1f1f1;margin:16px 0;margin:1rem 0;padding:16px 24px 16px 16px;padding:1rem 1.5rem 1rem 1rem;position:relative}#poststuff .bumpdown h1,#poststuff .bumpdown h2,#poststuff .bumpdown h3,#poststuff .bumpdown h4,.bumpdown h1,.bumpdown h2,.bumpdown h3,.bumpdown h4{padding-left:0;padding-top:0}.bumpdown-arrow{display:none}.bumpdown-close{color:#686868;cursor:pointer;position:absolute;right:8px;right:.5rem;top:8px;top:.5rem;z-index:1}.bumpdown-trigger .target{color:#0074a2}.ui-front{z-index:4}@media only screen and (-o-min-device-pixel-ratio:2/1),only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min--moz-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2){#tribe-loading span{background-image:url(../images/tribe-loading@2x.gif)}}@media screen and (max-width:782px){.tribe-half-column,.tribe-row .tribe-half-column:last-child{margin:0 0 20px;width:100%}input[type=email]{width:100%}}@media screen and (max-width:782px){.events-cal .subsubsub{float:none}.events-cal .search-box{width:98%}.events-cal #search-submit{width:100%}.events-cal .tablenav.top{display:none}}
1
+ .invalid input{border:2px solid red!important}.valid input{border:1px solid green}.clearfix{zoom:1}.placeholder{color:#999;cursor:text;padding:4px}input:placeholder,textarea:placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.bubble{-khtml-border-radius:3px;background-color:#f9f9f9;border:1px solid #dfdfdf;border-radius:3px;border-spacing:0;border-style:solid;padding:10px}.tribe-sticky-tooltip{color:#bbb}td.tribe_message{padding-bottom:10px!important}#tribe_thanks{float:left;margin:5px 0 0;width:200px}.tribe_brand{font-family:Georgia!important;font-size:17px!important;font-weight:400;margin:8px 0}#tribe-upgrade{background:#f6f6f6;border:1px solid #ccc;border-radius:5px;margin:20px 0 30px;padding:0 20px 20px}#tribe-upgrade .message{background-color:#ffffe0;border:1px solid #e6db55;border-radius:3px;padding:6px 12px}table.plugins .tribe-plugin-update-message{background:#d54e21;color:#fff;display:inline-table;margin:6px 0;padding:10px 12px}table.plugins .tribe-plugin-update-message h4{display:inline;font-weight:700;margin-right:8px}table.plugins .tribe-plugin-update-message h4:after{content:' \00BB '}table.plugins .tribe-plugin-update-message a{color:#fff;text-decoration:underline}.tribe-settings-form{max-width:1000px}.tribe-settings-form fieldset{clear:both;display:inline-block;padding:10px 0}.tribe-settings-form legend{float:left;font-weight:700;margin-right:20px;width:220px}.tribe-settings-form fieldset.tribe-field-license_key legend{width:auto}.tribe-settings-form .tribe-field-wrap{float:left;max-width:500px}.tribe-settings-form .tribe-field-checkbox_list label,.tribe-settings-form .tribe-field-radio label{display:block;margin:5px 0 5px 20px;text-indent:-20px}.tribe-settings-form .tribe-field-checkbox_list label input,.tribe-settings-form .tribe-field-radio label input{margin-right:5px}.tribe-settings-form .tribe-settings-form-wrap .description,.tribe-settings-form .tribe-settings-form-wrap fieldset,.tribe-settings-form fieldset[id^=tribe-field-geoloc_]{padding-left:12px}.tribe-settings-form .tribe-settings-form-wrap fieldset .description{margin-left:0;max-width:450px;padding-left:0}.tribe-settings-form .tribe-settings-form-wrap h3{background-color:#f9f9f9;margin-bottom:10px;padding:6px 0 6px 12px}.tribe-settings-form .tribe-settings-form-wrap .system-info,.tribe-settings-form .tribe-settings-form-wrap .tribe-sysinfo-optin-msg,.tribe-settings-form .tribe-settings-form-wrap h3+p{margin:0 0 10px;padding-left:12px}.tribe_settings .tribe-field-indent{margin-left:245px}.tribe_settings #pu_dashboard_message{display:none}.tribe_settings .tribe-errors-list{margin-left:15px}.tribe_settings .expiring-license{color:red}.tribe_settings .tribe-error{border:1px solid red}.tribe_settings .tribe-field-description{margin-bottom:0;position:relative;top:-12px}.tribe_settings #ical-link{top:-14px}.tribe-settings-form #tribe-field-stylesheetOption label{margin-left:20px}.tribe-settings-form #tribe-field-stylesheetOption input{margin-left:-20px;margin-right:8px}.tribe-settings-form #tribe-field-stylesheetOption p.description{color:#999}#modern-tribe-info{-khtml-border-radius:4px;background-color:#f9f9f9;border:1px solid #ccc;border-radius:4px;margin:20px 0;padding:8px 20px 12px}#modern-tribe-info img{height:18px;margin:10px 0;width:250px}#modern-tribe-info ul{list-style:disc;margin-left:20px}#modern-tribe-info ul ul{list-style:circle}.tribe-field-textarea.tribe-size-small textarea{height:60px;width:180px}.tribe-field-textarea.tribe-size-medium textarea{height:80px;width:300px}.tribe-field-textarea.tribe-size-large textarea{height:120px;width:450px}.tribe-field-license_key.tribe-size-small input,.tribe-field-text.tribe-size-small input{width:50px}.tribe-field-license_key.tribe-size-medium input,.tribe-field-text.tribe-size-medium input{width:225px}.tribe-field-license_key.tribe-size-large input,.tribe-field-text.tribe-size-large input{width:450px}.tribe-field-dropdown.tribe-size-small select{width:100px}.tribe-field-dropdown.tribe-size-medium select{width:300px}.tribe-field-dropdown.tribe-size-large select{width:450px}.tribe-field-dropdown_chosen.tribe-size-small select{width:100px}.tribe-field-dropdown_chosen.tribe-size-medium select{width:200px}.tribe-field-dropdown_chosen.tribe-size-large select{width:300px}.ajax-loading-license,.invalid-key,.valid-key{display:none;margin:0 5px}.ajax-loading-license{position:relative;top:5px}.key-validity{display:inline-block}.invalid-key,.optin-fail{color:red}.optin-success,.valid-key{color:green}.valid-key.service-msg{color:#b72}#additional-field-table{margin-bottom:20px}.tribe-admin-box-left{float:left;width:20%}.tribe-admin-box-left,.tribe-admin-box-right{-khtml-border-radius:4px;background-color:#f9f9f9;border:1px solid #ccc;border-radius:4px;margin:20px 0;padding:0 20px 15px}.tribe-admin-box-right{float:right;width:68%}.ajax-loader{float:right;margin:10px}.tribe-arrangeable-item{border:1px solid #d3d3d3;border-radius:6px}.tribe-arrangeable-item .ui-state-default{border:none}.tribe-arrangeable-item-top{padding:6px}.tribe-arrangeable-item-top:hover{cursor:move}.tribe-arrangeable-action{float:right}.tribe-arrangeable-child{background-color:#f9f9f9;border-top:1px solid #d3d3d3;display:none;padding:25px}.tribe-arrangeable-child label{display:block;margin:0 0 7px}.tribe_events_active_filter_type_options{margin:10px 0}.tribe_events_active_filter_type_options label{margin:7px 0}.tribe-settings-form .tribe-settings-form-wrap fieldset .tribe-style-selection{margin-bottom:18px}#event_organizer td small,.OrganizerInfo td small{display:block;margin:0;max-width:250px}#event_organizer .organizer-email,.OrganizerInfo .organizer-email{vertical-align:top}.tribe-table-field-label{max-width:100%;width:200px}#tribe-help-general,#tribe-help-sidebar{float:left;margin-top:20px}#tribe-help-general p{margin-left:15px}#tribe-help-general ul{list-style-type:square}#tribe-help-general ol,#tribe-help-general ul{margin-bottom:20px;margin-left:35px}#tribe-help-general h3{background-color:#f9f9f9;margin-bottom:10px;padding:6px 0 6px 12px}#tribe-help-general h3~h3{margin-top:2.25em}#tribe-help-general h3+p{margin:0 0 20px;padding-left:12px}#tribe-help-general{width:65%}.tribe-help-section{padding-bottom:10px}.tribe-section-type-box{-khtml-border-radius:4px;background-color:#f9f9f9;border:1px solid #ccc;border-radius:4px;padding:8px 20px 12px}.tribe-section-type-box img{height:auto;margin:10px 0;max-width:300px}.tribe-section-type-box ul{list-style:disc;margin-left:20px}.tribe-section-type-box ul ul{list-style:circle}#tribe-log-controls{padding-bottom:16px;padding-bottom:1rem;padding-left:12px}#tribe-log-controls>div{display:inline-block;padding-right:16px;padding-right:1rem}#tribe-log-controls .working{opacity:1;-webkit-transition:opacity .2s;transition:opacity .2s}#tribe-log-controls .working.hidden{opacity:0;-webkit-transition:opacity .2s;transition:opacity .2s}#tribe-log-viewer,#tribe-system-info dl.support-stats,.template-updates-wrapper{background:#000;border-radius:2px;color:#888;max-height:400px;overflow:scroll;padding:10px}#tribe-system-info dl.support-stats dt,.template-updates-wrapper dt{clear:both;float:left;font-weight:700;text-transform:uppercase;width:25%}#tribe-system-info dl.support-stats dd,.template-updates-wrapper dd{margin-left:25%;padding-left:10px}.system-info-copy .system-info-copy-btn{padding:6px}.system-info-copy .system-info-copy-btn .dashicons{padding-right:10px}.template-updates-wrapper p{margin-top:0}#tribe-help-sidebar{margin:20px 0 0 3%;max-width:225px;width:32%}.tribe-help-plugin-info{border:1px solid #ccc;padding:0 12px 12px}.tribe-help-plugin-info dd,.tribe-help-plugin-info dt{display:inline;margin:0}.tribe-help-plugin-info dt{font-weight:700}.tribe-help-plugin-info dd:after{content:'';display:block;height:.4em}.tribe-help-plugin-info dd:last-child:after{height:0}.tribe-help-plugin-info+.tribe-help-plugin-info{margin-top:20px}.tribe-help-plugin-info>div{line-height:2em}.tribe-help-plugin-info .star-rating{display:inline-block;margin-left:3px;position:relative;top:-2px}.tribe-help-plugin-info .tribe-list-addons{color:#21a6cb;font-size:24px;list-style:circle inside;margin-bottom:10px;margin-top:10px;padding-left:4px}.tribe-help-plugin-info .tribe-list-addons a{font-size:13px;left:-5px;position:relative;top:-5px}.tribe-help-plugin-info .tribe-list-addons .tribe-active-addon{list-style:disc inside}.ui-widget-overlay{background:#666;filter:Alpha(Opacity=50);opacity:.5}.ui-widget-shadow{background:#000;-webkit-border-radius:5px;-moz-border-radius:5px;filter:Alpha(Opacity=20);margin:-5px 0 0 -5px;opacity:.2;padding:5px}.ui-resizable{position:relative}.ui-resizable-handle{display:block;font-size:.1px;position:absolute;z-index:2}.ui-resizable-autohide .ui-resizable-handle,.ui-resizable-disabled .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;left:0;top:-5px;width:100%}.ui-resizable-s{bottom:-5px;cursor:s-resize;height:7px;left:0;width:100%}.ui-resizable-e{cursor:e-resize;height:100%;right:-5px;top:0;width:7px}.ui-resizable-w{cursor:w-resize;height:100%;left:-5px;top:0;width:7px}.ui-resizable-se{bottom:1px;cursor:se-resize;height:12px;right:1px;width:12px}.ui-resizable-sw{bottom:-5px;cursor:sw-resize;height:9px;left:-5px;width:9px}.ui-resizable-nw{cursor:nw-resize;height:9px;left:-5px;top:-5px;width:9px}.ui-resizable-ne{cursor:ne-resize;height:9px;right:-5px;top:-5px;width:9px}.ui-dialog{padding:.2em;position:relative;width:375px}.ui-dialog .ui-dialog-titlebar{padding:.5em .3em .3em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0 .2em}.ui-dialog .ui-dialog-titlebar-close{height:18px;margin:-10px 0 0;padding:1px;position:absolute;right:.3em;top:50%;width:19px}.ui-dialog .ui-dialog-titlebar-close span{display:block;margin-left:-8px;margin-top:-8px}.ui-dialog .ui-dialog-titlebar-close:focus,.ui-dialog .ui-dialog-titlebar-close:hover{padding:0}.ui-dialog .ui-dialog-content{background:none;border:0;overflow:auto;padding:.5em 1em;zoom:1}.ui-dialog .ui-dialog-buttonpane{background-image:none;border-width:1px 0 0;margin:.5em 0 0;padding:.3em 1em .5em!important;text-align:right}.ui-dialog .ui-dialog-buttonpane button{cursor:pointer;line-height:1.4em;margin:.5em .4em!important;overflow:visible;padding:.2em .6em .3em;text-shadow:none;width:auto}.ui-dialog .ui-resizable-se{bottom:3px;height:14px;right:3px;width:14px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:none!important;text-align:center}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button .ui-button-text{display:block;line-height:1.4}.ui-datepicker{font-size:8pt}#ui-datepicker-div{display:none}#tribe-loading{background:#fff;background:hsla(0,0%,100%,.8);display:none;height:100%;left:0;position:absolute;top:0;-webkit-transition:all 1s linear;transition:all 1s linear;webkit-transition:all 1s linear;width:100%;z-index:1}#tribe-loading span{background:url(../images/tribe-loading.gif) 0 0 no-repeat;background-size:32px 32px;height:32px;left:50%;margin:-16px 0 0 -16px;position:absolute;top:50%;width:32px}.tribe_update_page,.tribe_welcome_page{max-width:1000px}.tribe-half-column{float:left;margin-bottom:30px;margin-right:5%;width:45%}.tribe-row:after,.tribe-row:before{content:'';display:table}.tribe-row,.tribe-row:after{clear:both}.tribe-row .tribe-half-column:last-child{margin-right:0;width:50%}.tribe_update_page h2,.tribe_welcome_page h2{font-size:30px;line-height:1.2;margin-bottom:20px}.tribe_update_page h3,.tribe_welcome_page h3{font-size:24px;font-weight:400;line-height:24px;margin-top:0}.tribe_update_page h4,.tribe_welcome_page h4{font-size:18px;font-weight:600;line-height:18px;margin:0}.tribe_update_page p,.tribe_welcome_page p{font-size:14px}p.tribe-welcome-message{font-size:20px;font-weight:400}.tribe_update_page h2:before,.tribe_welcome_page h2:before{content:'\f145';font-family:dashicons;font-size:34px;line-height:1;margin-right:5px;position:relative;top:5px}.tribe-welcome-video-wrapper{height:0;margin-bottom:40px;padding-bottom:56.25%;padding-top:25px;position:relative}.tribe-welcome-video-wrapper iframe{height:100%;left:0;position:absolute;top:0;width:100%}a.tribe-rating-link{text-decoration:none}.tribe-update-links,.tribe-welcome-links{margin-top:30px}.tribe_update_page li:before,.tribe_welcome_page li:before{content:'\2022';padding-right:3px}.tribe_update_page .rss-widget{margin:1em 0}.tribe_update_page a.rsswidget{font-size:14px;font-weight:400;line-height:1}.tribe_update_page .rss-widget li:before{display:none}.tribe-update-bar{display:inline-block}.tribe-update-bar .progress{border:1px solid #ccc;float:left;margin-right:16px;margin-right:1rem;padding:1px;width:288px;width:18rem}.tribe-update-bar .progress .bar{background:#ffba00;height:16px;height:1rem;width:1%;background:#7ad03a}#tribe-dialog-wrapper>div{padding:16px;padding:1rem}#tribe-dialog-wrapper>div .stage{display:none}#tribe-dialog-wrapper #heading{background:#fff}#tribe-dialog-wrapper label{display:block}#tribe-dialog-wrapper .select-single-container{border:1px solid #888;overflow-y:scroll;height:300px}#tribe-dialog-wrapper .select-single-container label{opacity:1;padding:3px 5px;-webkit-transition:opacity .2s;transition:opacity .2s}#tribe-dialog-wrapper .select-single-container label:nth-child(odd){background:#fff}#tribe-dialog-wrapper .select-single-container label.selected{background:#0073aa;color:#fff;font-weight:700}#tribe-dialog-wrapper .select-single-container label input{display:none}#tribe-dialog-wrapper .select-single-container.updating label{opacity:.35;-webkit-transition:opacity .2s;transition:opacity .2s}.ui-front{z-index:3}.select2-container .select2-choice abbr{top:6px}.wp-list-table.plugins .column-description .update-message{color:#d54e21}.api-check{padding:1em}.api-check+.notice-dismiss:hover:before{color:#fff}.api-check:after,.api-check:before{content:'';display:table}.api-check:after{clear:both}.api-check .tribe-spirit-animal{background:#325f81;background:-webkit-linear-gradient(45deg,#325f81,#3d87c0);background:linear-gradient(45deg,#325f81,#3d87c0);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#325f81',endColorstr='#3d87c0',GradientType=1);border-left:4px solid #eee;bottom:0;display:none;padding:16px;padding:1rem;position:absolute;right:0;top:0;width:180px}.api-check .tribe-spirit-animal:before{content:'';display:inline-block;height:100%;width:1%;vertical-align:middle}.api-check .tribe-spirit-animal img{display:inline-block;max-height:100%;max-width:96%;vertical-align:middle}.api-check p{line-height:1.7;margin-bottom:1em}.api-check a{text-decoration:none}.api-check a:hover{text-decoration:underline}.api-check .plugin-list{display:inline;font-weight:600;margin:0;padding:0}.api-check .plugin-list span.plugin-invalid:after{content:', '}.api-check .plugin-list span.plugin-invalid:last-of-type:after{content:''}@media only screen and (-o-min-device-pixel-ratio:2/1),only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min--moz-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2){#tribe-loading span{background-image:url(../images/tribe-loading@2x.gif)}}@media screen and (max-width:782px){.tribe-half-column,.tribe-row .tribe-half-column:last-child{margin:0 0 20px;width:100%}input[type=email]{width:100%}}@media screen and (max-width:782px){.events-cal .subsubsub{float:none}.events-cal .search-box{width:98%}.events-cal #search-submit{width:100%}.events-cal .tablenav.top{display:none}}@media screen and (min-width:500px){.api-check .tribe-spirit-animal{display:block}.api-check .notice-content{margin-right:180px}}
common/src/resources/images/app-shop-facebook.jpg DELETED
Binary file
common/src/resources/images/app-shop-pro.jpg CHANGED
Binary file
{src → common/src}/resources/images/donate-link-pro-screenshot.jpg RENAMED
File without changes
{src → common/src}/resources/images/donate-link-screenshot.jpg RENAMED
File without changes
common/src/resources/images/spirit-animal.png ADDED
Binary file
common/src/resources/js/bumpdown.js ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function( $, _ ) {
2
+ 'use strict';
3
+ // Configure on Document ready for the default trigger
4
+ $( document ).ready( function() {
5
+ $( '.tribe-bumpdown-trigger' ).bumpdown();
6
+ } );
7
+
8
+ $.fn.bumpdown = function() {
9
+ var $document = $( document ),
10
+ selectors = {
11
+ // A template for the ID if we don't have one already
12
+ ID: 'tribe-bumpdown-',
13
+ data_trigger: function( ID ) {
14
+ return '[data-trigger="' + ID + '"]';
15
+ },
16
+ bumpdown: '.tribe-bumpdown',
17
+ content: '.tribe-bumpdown-content',
18
+ trigger: '.tribe-bumpdown-trigger',
19
+ hover_trigger: '.tribe-bumpdown-trigger:not(.tribe-bumpdown-nohover)',
20
+ close: '.tribe-bumpdown-close',
21
+ permanent: '.tribe-bumpdown-permanent',
22
+ active: '.tribe-bumpdown-active'
23
+ },
24
+ methods = {
25
+ open: function( $bumpdown ) {
26
+ var data = $bumpdown.data( 'bumpdown' ),
27
+ width_rule = data.$trigger.data( 'width-rule' );
28
+
29
+ if ( $bumpdown.is( ':visible' ) ) {
30
+ return;
31
+ }
32
+
33
+ // Adds a Class to signal it's active
34
+ data.$trigger.addClass( selectors.active.replace( '.', '' ) );
35
+
36
+ var $content = $bumpdown.find( selectors.content );
37
+
38
+ if ( 'string' === typeof width_rule && 'all-triggers' === width_rule ) {
39
+ var min_width = 600;
40
+ var trigger_position = 0;
41
+ $( selectors.trigger ).each( function() {
42
+ var $el = $( this );
43
+
44
+ // only attempt to align items with a width rule
45
+ if ( ! $el.data( 'width-rule' ) ) {
46
+ return;
47
+ }
48
+
49
+ var position = $el.position();
50
+
51
+ if ( position.left > trigger_position ) {
52
+ trigger_position = position.left;
53
+ }
54
+ } );
55
+
56
+ if ( trigger_position ) {
57
+ trigger_position = trigger_position > min_width ? trigger_position : min_width;
58
+
59
+ $content.css( 'max-width', trigger_position + 'px' );
60
+ }
61
+ }
62
+
63
+ $content.prepend( '<a class="tribe-bumpdown-close" title="Close"><i class="dashicons dashicons-no"></i></a>' );
64
+ $content.prepend( '<span class="tribe-bumpdown-arrow"></span>' );
65
+ methods.arrow( $bumpdown );
66
+
67
+ $bumpdown.data( 'preventClose', true );
68
+ $bumpdown.slideDown( 'fast', function() {
69
+ $bumpdown.data( 'preventClose', false );
70
+ } );
71
+ },
72
+ close: function( $bumpdown ) {
73
+ var data = $bumpdown.data( 'bumpdown' );
74
+
75
+ if ( ! $bumpdown.is( ':visible' ) || $bumpdown.data( 'preventClose' ) ) {
76
+ return;
77
+ }
78
+
79
+ // When we close we reset the flag about hoverintent
80
+ $( this ).removeData( 'is_hoverintent_queued' );
81
+
82
+ $bumpdown.find( '.tribe-bumpdown-close, .tribe-bumpdown-arrow' ).remove();
83
+ $bumpdown.not( '.tribe-bumpdown-trigger' ).slideUp( 'fast' );
84
+
85
+ data.$trigger.removeClass( selectors.active.replace( '.', '' ) );
86
+ },
87
+ arrow: function( $bumpdown ) {
88
+ var data = $bumpdown.data( 'bumpdown' ),
89
+ arrow;
90
+
91
+ arrow = Math.ceil( data.$trigger.position().left - ( 'block' === data.type ? data.$parent.offset().left : 0 ) );
92
+
93
+ data.$bumpdown.find( '.tribe-bumpdown-arrow' ).css( 'left', arrow );
94
+ }
95
+ };
96
+
97
+ $( window ).on( {
98
+ 'resize.bumpdown': function() {
99
+ $document.find( selectors.active ).each( function() {
100
+ methods.arrow( $( this ) );
101
+ } );
102
+ }
103
+ } );
104
+
105
+ $document
106
+ // Use hoverIntent to make sure we are not opening Bumpdown on a fast hover
107
+ .hoverIntent( {
108
+ over: function() {
109
+ var data = $( this ).data( 'bumpdown' );
110
+
111
+ // Flags that it's open
112
+ data.$trigger.data( 'is_hoverintent_queued', false );
113
+
114
+ // Actually opens
115
+ data.$bumpdown.trigger( 'open.bumpdown' );
116
+ },
117
+ out: function() {}, // Prevents Notice on JS
118
+ selector: selectors.hover_trigger,
119
+ interval: 200
120
+ } )
121
+
122
+ // Setup Events on Trigger
123
+ .on( {
124
+ mouseenter: function() {
125
+ if ( $( this ).data( 'is_hoverintent_queued' ) === undefined ) {
126
+ // Flags that hoverIntent will take care of the
127
+ $( this ).data( 'is_hoverintent_queued', true );
128
+ }
129
+ },
130
+ click: function( e ) {
131
+ var data = $( this ).data( 'bumpdown' );
132
+ e.preventDefault();
133
+ e.stopPropagation();
134
+
135
+ if ( data.$bumpdown.is( ':visible' ) ) {
136
+ // Makes sure we are not dealing with the first enter of the mouse
137
+ if ( data.$trigger.data( 'is_hoverintent_queued' ) ) {
138
+ // On double click it will close, kinda like forcing the closing
139
+ return data.$trigger.data( 'is_hoverintent_queued', false );
140
+ }
141
+
142
+ data.$bumpdown.trigger( 'close.bumpdown' );
143
+ } else {
144
+ data.$bumpdown.trigger( 'open.bumpdown' );
145
+ }
146
+ },
147
+ 'open.bumpdown': function() { methods.open( $( this ) ); },
148
+ 'close.bumpdown': function() { methods.close( $( this ) ); }
149
+ }, selectors.trigger )
150
+
151
+ // Setup Events on Trigger
152
+ .on( {
153
+ click: function( e ) {
154
+ var data = $( this ).parents( selectors.bumpdown ).first().data( 'bumpdown' );
155
+
156
+ e.preventDefault();
157
+ e.stopPropagation();
158
+
159
+ if ( 'undefined' === typeof data ) {
160
+ return;
161
+ }
162
+
163
+ if ( 'undefined' === typeof data.$bumpdown ) {
164
+ return;
165
+ }
166
+
167
+ data.$bumpdown.trigger( 'close.bumpdown' );
168
+ },
169
+ }, selectors.close )
170
+
171
+ // Triggers closing when clicking on the document
172
+ .on( 'click', function( e ) {
173
+ var $target = $( e.target ),
174
+ is_bumpdown = $target.is( selectors.bumpdown ) || 0 !== $target.parents( selectors.bumpdown ).length;
175
+
176
+ if ( is_bumpdown ) {
177
+ return;
178
+ }
179
+
180
+ $( selectors.trigger ).not( selectors.permanent ).trigger( 'close.bumpdown' );
181
+ } )
182
+
183
+ // Creates actions on the actual bumpdown
184
+ .on( {
185
+ 'open.bumpdown': function() { methods.open( $( this ) ); },
186
+ 'close.bumpdown': function() { methods.close( $( this ) ); }
187
+ }, selectors.bumpdown );
188
+
189
+ // Configure all the fields
190
+ return this.each( function() {
191
+ var data = {
192
+ // Store the jQuery Elements
193
+ $trigger: $( this ),
194
+ $parent: null,
195
+ $bumpdown: null,
196
+
197
+ // Store other Variables
198
+ ID: null,
199
+ html: null,
200
+ type: 'block',
201
+
202
+ // Flags
203
+ is_permanent: false
204
+ };
205
+
206
+ // We need a ID for this Bumpdown
207
+ data.ID = data.$trigger.attr( 'id' );
208
+
209
+ // If we currently don't have the ID, set it up
210
+ if ( ! data.ID ) {
211
+ data.ID = _.uniqueId( selectors.ID );
212
+
213
+ // Apply the given ID to
214
+ data.$trigger.attr( 'id', data.ID );
215
+ }
216
+
217
+ // We fetch from `[data-bumpdown]` attr the possible HTML for this Bumpdown
218
+ data.html = data.$trigger.data( 'bumpdown' );
219
+ data.html = '<div class="tribe-bumpdown-content">' + data.html + '</div>';
220
+
221
+ // We fetch from `[data-bumpdown-class]` attr the possible class(es) for this Bumpdown
222
+ data.class = data.$trigger.data( 'bumpdown-class' );
223
+
224
+ // Flags about if this bumpdown is permanent, meaning it only closes when clicking on the close button or the trigger
225
+ data.is_permanent = data.$trigger.is( selectors.permanent );
226
+
227
+ // Fetch the first Block-Level parent
228
+ data.$parent = data.$trigger.parents().filter( function() {
229
+ return $.inArray( $( this ).css( 'display' ), [ 'block', 'table', 'table-cell', 'table-row' ] );
230
+ }).first();
231
+
232
+ if ( ! data.html ) {
233
+ data.$bumpdown = $( selectors.data_trigger( data.ID ) );
234
+ data.type = 'block';
235
+ } else {
236
+ data.type = data.$parent.is( 'td, tr, td, table' ) ? 'table' : 'block';
237
+
238
+ if ( 'table' === data.type ) {
239
+ data.$bumpdown = $( '<td>' ).attr( { colspan: 2 } ).addClass( 'tribe-bumpdown-cell' ).html( data.html );
240
+ var classes = data.class ? 'tribe-bumpdown-row ' + data.class : 'tribe-bumpdown-row',
241
+ $row = $( '<tr>' ).append( data.$bumpdown ).addClass( classes );
242
+
243
+ data.$parent = data.$trigger.parents( 'tr' ).first();
244
+
245
+ data.$parent.after( $row );
246
+ } else {
247
+ data.$bumpdown = $( '<div>' ).addClass( 'tribe-bumpdown-block' ).html( data.html );
248
+ data.$trigger.after( data.$bumpdown );
249
+ }
250
+ }
251
+
252
+ // Setup data on trigger
253
+ data.$trigger
254
+ .data( 'bumpdown', data )
255
+
256
+ // Mark this as the trigger
257
+ .addClass( selectors.trigger.replace( '.', '' ) );
258
+
259
+
260
+ // Setup data on actual bumpdown
261
+ data.$bumpdown
262
+ .data( 'bumpdown', data )
263
+
264
+ // Mark it as the Bumpdown
265
+ .addClass( selectors.bumpdown.replace( '.', '' ) );
266
+
267
+ // support our dependency library
268
+ if ( data.$trigger.data( 'depends' ) ) {
269
+ var field_ids = data.$trigger.data( 'depends' );
270
+ $( document ).on( 'change', field_ids, function() {
271
+ methods.close( data.$bumpdown );
272
+ } );
273
+ }
274
+ });
275
+ };
276
+ }( jQuery, _ ) );
common/src/resources/js/bumpdown.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(t,e){"use strict";t(document).ready(function(){t(".tribe-bumpdown-trigger").bumpdown()}),t.fn.bumpdown=function(){var n=t(document),r={ID:"tribe-bumpdown-",data_trigger:function(t){return'[data-trigger="'+t+'"]'},bumpdown:".tribe-bumpdown",content:".tribe-bumpdown-content",trigger:".tribe-bumpdown-trigger",hover_trigger:".tribe-bumpdown-trigger:not(.tribe-bumpdown-nohover)",close:".tribe-bumpdown-close",permanent:".tribe-bumpdown-permanent",active:".tribe-bumpdown-active"},o={open:function(e){var n=e.data("bumpdown"),i=n.$trigger.data("width-rule");if(!e.is(":visible")){n.$trigger.addClass(r.active.replace(".",""));var a=e.find(r.content);if("string"==typeof i&&"all-triggers"===i){var d=600,p=0;t(r.trigger).each(function(){var e=t(this);if(e.data("width-rule")){var n=e.position();n.left>p&&(p=n.left)}}),p&&(p=p>d?p:d,a.css("max-width",p+"px"))}a.prepend('<a class="tribe-bumpdown-close" title="Close"><i class="dashicons dashicons-no"></i></a>'),a.prepend('<span class="tribe-bumpdown-arrow"></span>'),o.arrow(e),e.data("preventClose",!0),e.slideDown("fast",function(){e.data("preventClose",!1)})}},close:function(e){var n=e.data("bumpdown");e.is(":visible")&&!e.data("preventClose")&&(t(this).removeData("is_hoverintent_queued"),e.find(".tribe-bumpdown-close, .tribe-bumpdown-arrow").remove(),e.not(".tribe-bumpdown-trigger").slideUp("fast"),n.$trigger.removeClass(r.active.replace(".","")))},arrow:function(t){var e,n=t.data("bumpdown");e=Math.ceil(n.$trigger.position().left-("block"===n.type?n.$parent.offset().left:0)),n.$bumpdown.find(".tribe-bumpdown-arrow").css("left",e)}};return t(window).on({"resize.bumpdown":function(){n.find(r.active).each(function(){o.arrow(t(this))})}}),n.hoverIntent({over:function(){var e=t(this).data("bumpdown");e.$trigger.data("is_hoverintent_queued",!1),e.$bumpdown.trigger("open.bumpdown")},out:function(){},selector:r.hover_trigger,interval:200}).on({mouseenter:function(){void 0===t(this).data("is_hoverintent_queued")&&t(this).data("is_hoverintent_queued",!0)},click:function(e){var n=t(this).data("bumpdown");if(e.preventDefault(),e.stopPropagation(),n.$bumpdown.is(":visible")){if(n.$trigger.data("is_hoverintent_queued"))return n.$trigger.data("is_hoverintent_queued",!1);n.$bumpdown.trigger("close.bumpdown")}else n.$bumpdown.trigger("open.bumpdown")},"open.bumpdown":function(){o.open(t(this))},"close.bumpdown":function(){o.close(t(this))}},r.trigger).on({click:function(e){var n=t(this).parents(r.bumpdown).first().data("bumpdown");e.preventDefault(),e.stopPropagation(),"undefined"!=typeof n&&"undefined"!=typeof n.$bumpdown&&n.$bumpdown.trigger("close.bumpdown")}},r.close).on("click",function(e){var n=t(e.target),o=n.is(r.bumpdown)||0!==n.parents(r.bumpdown).length;o||t(r.trigger).not(r.permanent).trigger("close.bumpdown")}).on({"open.bumpdown":function(){o.open(t(this))},"close.bumpdown":function(){o.close(t(this))}},r.bumpdown),this.each(function(){var n={$trigger:t(this),$parent:null,$bumpdown:null,ID:null,html:null,type:"block",is_permanent:!1};if(n.ID=n.$trigger.attr("id"),n.ID||(n.ID=e.uniqueId(r.ID),n.$trigger.attr("id",n.ID)),n.html=n.$trigger.data("bumpdown"),n.html='<div class="tribe-bumpdown-content">'+n.html+"</div>",n["class"]=n.$trigger.data("bumpdown-class"),n.is_permanent=n.$trigger.is(r.permanent),n.$parent=n.$trigger.parents().filter(function(){return t.inArray(t(this).css("display"),["block","table","table-cell","table-row"])}).first(),n.html)if(n.type=n.$parent.is("td, tr, td, table")?"table":"block","table"===n.type){n.$bumpdown=t("<td>").attr({colspan:2}).addClass("tribe-bumpdown-cell").html(n.html);var i=n["class"]?"tribe-bumpdown-row "+n["class"]:"tribe-bumpdown-row",a=t("<tr>").append(n.$bumpdown).addClass(i);n.$parent=n.$trigger.parents("tr").first(),n.$parent.after(a)}else n.$bumpdown=t("<div>").addClass("tribe-bumpdown-block").html(n.html),n.$trigger.after(n.$bumpdown);else n.$bumpdown=t(r.data_trigger(n.ID)),n.type="block";if(n.$trigger.data("bumpdown",n).addClass(r.trigger.replace(".","")),n.$bumpdown.data("bumpdown",n).addClass(r.bumpdown.replace(".","")),n.$trigger.data("depends")){var d=n.$trigger.data("depends");t(document).on("change",d,function(){o.close(n.$bumpdown)})}})}}(jQuery,_);
common/src/resources/js/dependency.js ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function( $, _ ) {
2
+ 'use strict';
3
+ var $document = $( document ),
4
+ selectors = {
5
+ dependent: '.tribe-dependent',
6
+ active: '.tribe-active',
7
+ dependency: '.tribe-dependency',
8
+ fields: 'input, select, textarea',
9
+ advanced_fields: '.select2-container'
10
+ };
11
+
12
+ // Setup a Dependent
13
+ $.fn.dependency = function () {
14
+ this.each( function(){
15
+ var selector = $( this ).data( 'depends' );
16
+ $( selector ).addClass( selectors.dependency.replace( '.', '' ) ).data( 'dependent', $( this ) );
17
+ } );
18
+ };
19
+
20
+ $document
21
+ // Prevents double global actions
22
+ .off( 'change.dependency verify.dependency', selectors.dependency )
23
+ .on( {
24
+ 'verify.dependency': function( e ) {
25
+ var $field = $( this ),
26
+ selector = '#' + $field.attr( 'id' ),
27
+ value = $field.val();
28
+
29
+ // We need an ID to make something depend on this
30
+ if ( ! selector ) {
31
+ return;
32
+ }
33
+
34
+ // Fetch dependent elements
35
+ var $dependents = $document.find( '[data-depends="' + selector + '"]' );
36
+
37
+ $dependents.each( function( k, dependent ) {
38
+ var $dependent = $( dependent ),
39
+ condition = $dependent.data( 'condition' ),
40
+ not_condition = $dependent.data( 'conditionNot' ),
41
+ is_not_empty = $dependent.data( 'conditionNotEmpty' ) || $dependent.is( '[data-condition-not-empty]' ),
42
+ is_empty = $dependent.data( 'conditionEmpty' ) || $dependent.is( '[data-condition-empty]' ),
43
+ is_disabled = $field.is( ':disabled' ),
44
+ active_class = selectors.active.replace( '.', '' );
45
+
46
+ if (
47
+ (
48
+ ( is_empty && '' == value )
49
+ || ( is_not_empty && '' != value )
50
+ || ( _.isArray( condition ) && -1 !== _.findIndex( condition, value ) )
51
+ || ( 'undefined' !== typeof condition && value == condition )
52
+ || ( 'undefined' !== typeof not_condition && value != not_condition )
53
+ ) && ! is_disabled
54
+ ) {
55
+ $dependent
56
+ .addClass( active_class )
57
+ .find( selectors.fields ).prop( 'disabled', false )
58
+ .end().find( '.select2-container' ).select2( 'enable', false );
59
+
60
+ if ( $( '#s2id_' + $dependent.attr( 'id' ) ).length ) {
61
+ $( '#s2id_' + $dependent.attr( 'id' ) ).addClass( active_class );
62
+ }
63
+ } else {
64
+ $dependent
65
+ .removeClass( active_class )
66
+ .find( selectors.fields ).prop( 'disabled', true )
67
+ .end().find( '.select2-container' ).select2( 'enable', true );
68
+
69
+ if ( $( '#s2id_' + $dependent.attr( 'id' ) ).length ) {
70
+ $( '#s2id_' + $dependent.attr( 'id' ) ).removeClass( active_class );
71
+ }
72
+ }
73
+
74
+ // Checks if any child elements have dependencies
75
+ $dependent.find( selectors.dependency ).trigger( 'change' );
76
+ } );
77
+ },
78
+ 'change.dependency': function( e ) {
79
+ $( this ).trigger( 'verify.dependency' );
80
+ }
81
+ }, selectors.dependency )
82
+
83
+ // Configure on Document ready for the default trigger
84
+ $document.ready( function() {
85
+ $( selectors.dependent ).dependency();
86
+ $( selectors.dependency ).trigger( 'verify.dependency' );
87
+ } );
88
+ }( jQuery, _ ) );
common/src/resources/js/dependency.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e,d){"use strict";var n=e(document),t={dependent:".tribe-dependent",active:".tribe-active",dependency:".tribe-dependency",fields:"input, select, textarea",advanced_fields:".select2-container"};e.fn.dependency=function(){this.each(function(){var d=e(this).data("depends");e(d).addClass(t.dependency.replace(".","")).data("dependent",e(this))})},n.off("change.dependency verify.dependency",t.dependency).on({"verify.dependency":function(i){var a=e(this),c="#"+a.attr("id"),s=a.val();if(c){var r=n.find('[data-depends="'+c+'"]');r.each(function(n,i){var c=e(i),r=c.data("condition"),o=c.data("conditionNot"),p=c.data("conditionNotEmpty")||c.is("[data-condition-not-empty]"),f=c.data("conditionEmpty")||c.is("[data-condition-empty]"),y=a.is(":disabled"),l=t.active.replace(".","");(f&&""==s||p&&""!=s||d.isArray(r)&&-1!==d.findIndex(r,s)||"undefined"!=typeof r&&s==r||"undefined"!=typeof o&&s!=o)&&!y?(c.addClass(l).find(t.fields).prop("disabled",!1).end().find(".select2-container").select2("enable",!1),e("#s2id_"+c.attr("id")).length&&e("#s2id_"+c.attr("id")).addClass(l)):(c.removeClass(l).find(t.fields).prop("disabled",!0).end().find(".select2-container").select2("enable",!0),e("#s2id_"+c.attr("id")).length&&e("#s2id_"+c.attr("id")).removeClass(l)),c.find(t.dependency).trigger("change")})}},"change.dependency":function(d){e(this).trigger("verify.dependency")}},t.dependency),n.ready(function(){e(t.dependent).dependency(),e(t.dependency).trigger("verify.dependency")})}(jQuery,_);
common/src/resources/js/inline-bumpdown.js DELETED
@@ -1,103 +0,0 @@
1
- (function( $ ) {
2
- 'use strict';
3
- var methods = {};
4
-
5
- methods.clicked = false;
6
- methods.opening = false;
7
-
8
- methods.close_bumpdown = function( $bumpdown ) {
9
- if ( ! $bumpdown.is( ':visible' ) ) {
10
- return;
11
- }//end if
12
-
13
- $bumpdown.slideUp( 'fast' );
14
- };
15
-
16
- methods.open_bumpdown = function( $bumpdown ) {
17
- if ( $bumpdown.is( ':visible' ) ) {
18
- return;
19
- }//end if
20
-
21
- methods.opening = true;
22
-
23
- $bumpdown.slideDown( 'fast', function() {
24
- methods.opening = false;
25
- });
26
- };
27
-
28
- $.fn.bumpdown = function() {
29
- return this.each( function() {
30
- var $el = $( this );
31
- var the_id = $el.attr( 'id' );
32
- var $trigger_source = $el.find( '.target' );
33
-
34
- $el.addClass( 'bumpdown-trigger' );
35
-
36
- // get the first block-level parent
37
- var $parent = $el.parents().filter( function() {
38
- return 'block' === $( this ).css( 'display' );
39
- }).first();
40
-
41
- if ( ! $trigger_source.length ) {
42
- $trigger_source = $el;
43
- }//end if
44
-
45
- if ( ! the_id ) {
46
- $.error( 'bumpdowns need an id' );
47
- }//end if
48
-
49
- var bumpdown_selector = '[data-trigger="' + the_id + '"]';
50
-
51
- var $bumpdown = $( bumpdown_selector );
52
-
53
- $bumpdown.addClass( 'bumpdown' );
54
-
55
- var source = {};
56
-
57
- source.offset = $trigger_source.offset();
58
- source.width = $trigger_source.outerWidth();
59
- source.halfway = ( source.offset.left - $parent.offset().left ) + Math.round( source.width / 2 ) - 16;
60
-
61
- $bumpdown.prepend( '<a class="bumpdown-close" title="Close"><i class="dashicons dashicons-no"></i></a>' );
62
- $bumpdown.prepend( '<span class="bumpdown-arrow" style="left: ' + source.halfway + 'px;"></span>' );
63
-
64
- $( document ).on( 'click', bumpdown_selector, function() {
65
- if ( $bumpdown.is( ':visible' ) && ! methods.opening ) {
66
- methods.clicked = true;
67
- }//end if
68
- });
69
-
70
- $( document ).on( 'click', function() {
71
- if ( ! methods.clicked && ! methods.opening && $bumpdown.is( ':visible' ) ) {
72
- methods.close_bumpdown( $bumpdown );
73
- }//end if
74
-
75
- methods.clicked = false;
76
- });
77
-
78
- $el.on( 'mouseover', function() {
79
- $el.doTimeout( the_id, 300, function() {
80
- if ( ! $bumpdown.is( ':visible' ) ) {
81
- methods.open_bumpdown( $bumpdown );
82
- }//end if
83
- });
84
- });
85
-
86
- $el.on( 'click', function( e ) {
87
- e.preventDefault();
88
-
89
- if ( ! $bumpdown.is( ':visible' ) ) {
90
- e.stopPropagation();
91
- methods.open_bumpdown( $bumpdown );
92
- }//end if
93
- });
94
-
95
- $bumpdown.find( '.bumpdown-close' ).on( 'click', function( e ) {
96
- e.preventDefault();
97
- e.stopPropagation();
98
-
99
- methods.close_bumpdown( $bumpdown );
100
- });
101
- });
102
- };
103
- }( jQuery ));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/resources/js/inline-bumpdown.min.js DELETED
@@ -1 +0,0 @@
1
- !function(n){"use strict";var i={};i.clicked=!1,i.opening=!1,i.close_bumpdown=function(n){n.is(":visible")&&n.slideUp("fast")},i.open_bumpdown=function(n){n.is(":visible")||(i.opening=!0,n.slideDown("fast",function(){i.opening=!1}))},n.fn.bumpdown=function(){return this.each(function(){var o=n(this),e=o.attr("id"),t=o.find(".target");o.addClass("bumpdown-trigger");var s=o.parents().filter(function(){return"block"===n(this).css("display")}).first();t.length||(t=o),e||n.error("bumpdowns need an id");var c='[data-trigger="'+e+'"]',d=n(c);d.addClass("bumpdown");var a={};a.offset=t.offset(),a.width=t.outerWidth(),a.halfway=a.offset.left-s.offset().left+Math.round(a.width/2)-16,d.prepend('<a class="bumpdown-close" title="Close"><i class="dashicons dashicons-no"></i></a>'),d.prepend('<span class="bumpdown-arrow" style="left: '+a.halfway+'px;"></span>'),n(document).on("click",c,function(){d.is(":visible")&&!i.opening&&(i.clicked=!0)}),n(document).on("click",function(){i.clicked||i.opening||!d.is(":visible")||i.close_bumpdown(d),i.clicked=!1}),o.on("mouseover",function(){o.doTimeout(e,300,function(){d.is(":visible")||i.open_bumpdown(d)})}),o.on("click",function(n){n.preventDefault(),d.is(":visible")||(n.stopPropagation(),i.open_bumpdown(d))}),d.find(".bumpdown-close").on("click",function(n){n.preventDefault(),n.stopPropagation(),i.close_bumpdown(d)})})}}(jQuery);
 
common/src/resources/js/jquery.ba-dotimeout.js DELETED
@@ -1,285 +0,0 @@
1
- /*!
2
- * jQuery doTimeout: Like setTimeout, but better! - v1.0 - 3/3/2010
3
- * http://benalman.com/projects/jquery-dotimeout-plugin/
4
- *
5
- * Copyright (c) 2010 "Cowboy" Ben Alman
6
- * Dual licensed under the MIT and GPL licenses.
7
- * http://benalman.com/about/license/
8
- */
9
-
10
- // Script: jQuery doTimeout: Like setTimeout, but better!
11
- //
12
- // *Version: 1.0, Last updated: 3/3/2010*
13
- //
14
- // Project Home - http://benalman.com/projects/jquery-dotimeout-plugin/
15
- // GitHub - http://github.com/cowboy/jquery-dotimeout/
16
- // Source - http://github.com/cowboy/jquery-dotimeout/raw/master/jquery.ba-dotimeout.js
17
- // (Minified) - http://github.com/cowboy/jquery-dotimeout/raw/master/jquery.ba-dotimeout.min.js (1.0kb)
18
- //
19
- // About: License
20
- //
21
- // Copyright (c) 2010 "Cowboy" Ben Alman,
22
- // Dual licensed under the MIT and GPL licenses.
23
- // http://benalman.com/about/license/
24
- //
25
- // About: Examples
26
- //
27
- // These working examples, complete with fully commented code, illustrate a few
28
- // ways in which this plugin can be used.
29
- //
30
- // Debouncing - http://benalman.com/code/projects/jquery-dotimeout/examples/debouncing/
31
- // Delays, Polling - http://benalman.com/code/projects/jquery-dotimeout/examples/delay-poll/
32
- // Hover Intent - http://benalman.com/code/projects/jquery-dotimeout/examples/hoverintent/
33
- //
34
- // About: Support and Testing
35
- //
36
- // Information about what version or versions of jQuery this plugin has been
37
- // tested with, what browsers it has been tested in, and where the unit tests
38
- // reside (so you can test it yourself).
39
- //
40
- // jQuery Versions - 1.3.2, 1.4.2
41
- // Browsers Tested - Internet Explorer 6-8, Firefox 2-3.6, Safari 3-4, Chrome 4-5, Opera 9.6-10.1.
42
- // Unit Tests - http://benalman.com/code/projects/jquery-dotimeout/unit/
43
- //
44
- // About: Release History
45
- //
46
- // 1.0 - (3/3/2010) Callback can now be a string, in which case it will call
47
- // the appropriate $.method or $.fn.method, depending on where .doTimeout
48
- // was called. Callback must now return `true` (not just a truthy value)
49
- // to poll.
50
- // 0.4 - (7/15/2009) Made the "id" argument optional, some other minor tweaks
51
- // 0.3 - (6/25/2009) Initial release
52
-
53
- (function($){
54
- '$:nomunge'; // Used by YUI compressor.
55
-
56
- var cache = {},
57
-
58
- // Reused internal string.
59
- doTimeout = 'doTimeout',
60
-
61
- // A convenient shortcut.
62
- aps = Array.prototype.slice;
63
-
64
- // Method: jQuery.doTimeout
65
- //
66
- // Initialize, cancel, or force execution of a callback after a delay.
67
- //
68
- // If delay and callback are specified, a doTimeout is initialized. The
69
- // callback will execute, asynchronously, after the delay. If an id is
70
- // specified, this doTimeout will override and cancel any existing doTimeout
71
- // with the same id. Any additional arguments will be passed into callback
72
- // when it is executed.
73
- //
74
- // If the callback returns true, the doTimeout loop will execute again, after
75
- // the delay, creating a polling loop until the callback returns a non-true
76
- // value.
77
- //
78
- // Note that if an id is not passed as the first argument, this doTimeout will
79
- // NOT be able to be manually canceled or forced. (for debouncing, be sure to
80
- // specify an id).
81
- //
82
- // If id is specified, but delay and callback are not, the doTimeout will be
83
- // canceled without executing the callback. If force_mode is specified, the
84
- // callback will be executed, synchronously, but will only be allowed to
85
- // continue a polling loop if force_mode is true (provided the callback
86
- // returns true, of course). If force_mode is false, no polling loop will
87
- // continue, even if the callback returns true.
88
- //
89
- // Usage:
90
- //
91
- // > jQuery.doTimeout( [ id, ] delay, callback [, arg ... ] );
92
- // > jQuery.doTimeout( id [, force_mode ] );
93
- //
94
- // Arguments:
95
- //
96
- // id - (String) An optional unique identifier for this doTimeout. If id is
97
- // not specified, the doTimeout will NOT be able to be manually canceled or
98
- // forced.
99
- // delay - (Number) A zero-or-greater delay in milliseconds after which
100
- // callback will be executed.
101
- // callback - (Function) A function to be executed after delay milliseconds.
102
- // callback - (String) A jQuery method to be executed after delay
103
- // milliseconds. This method will only poll if it explicitly returns
104
- // true.
105
- // force_mode - (Boolean) If true, execute that id's doTimeout callback
106
- // immediately and synchronously, continuing any callback return-true
107
- // polling loop. If false, execute the callback immediately and
108
- // synchronously but do NOT continue a callback return-true polling loop.
109
- // If omitted, cancel that id's doTimeout.
110
- //
111
- // Returns:
112
- //
113
- // If force_mode is true, false or undefined and there is a
114
- // yet-to-be-executed callback to cancel, true is returned, but if no
115
- // callback remains to be executed, undefined is returned.
116
-
117
- $[doTimeout] = function() {
118
- return p_doTimeout.apply( window, [ 0 ].concat( aps.call( arguments ) ) );
119
- };
120
-
121
- // Method: jQuery.fn.doTimeout
122
- //
123
- // Initialize, cancel, or force execution of a callback after a delay.
124
- // Operates like <jQuery.doTimeout>, but the passed callback executes in the
125
- // context of the jQuery collection of elements, and the id is stored as data
126
- // on the first element in that collection.
127
- //
128
- // If delay and callback are specified, a doTimeout is initialized. The
129
- // callback will execute, asynchronously, after the delay. If an id is
130
- // specified, this doTimeout will override and cancel any existing doTimeout
131
- // with the same id. Any additional arguments will be passed into callback
132
- // when it is executed.
133
- //
134
- // If the callback returns true, the doTimeout loop will execute again, after
135
- // the delay, creating a polling loop until the callback returns a non-true
136
- // value.
137
- //
138
- // Note that if an id is not passed as the first argument, this doTimeout will
139
- // NOT be able to be manually canceled or forced (for debouncing, be sure to
140
- // specify an id).
141
- //
142
- // If id is specified, but delay and callback are not, the doTimeout will be
143
- // canceled without executing the callback. If force_mode is specified, the
144
- // callback will be executed, synchronously, but will only be allowed to
145
- // continue a polling loop if force_mode is true (provided the callback
146
- // returns true, of course). If force_mode is false, no polling loop will
147
- // continue, even if the callback returns true.
148
- //
149
- // Usage:
150
- //
151
- // > jQuery('selector').doTimeout( [ id, ] delay, callback [, arg ... ] );
152
- // > jQuery('selector').doTimeout( id [, force_mode ] );
153
- //
154
- // Arguments:
155
- //
156
- // id - (String) An optional unique identifier for this doTimeout, stored as
157
- // jQuery data on the element. If id is not specified, the doTimeout will
158
- // NOT be able to be manually canceled or forced.
159
- // delay - (Number) A zero-or-greater delay in milliseconds after which
160
- // callback will be executed.
161
- // callback - (Function) A function to be executed after delay milliseconds.
162
- // callback - (String) A jQuery.fn method to be executed after delay
163
- // milliseconds. This method will only poll if it explicitly returns
164
- // true (most jQuery.fn methods return a jQuery object, and not `true`,
165
- // which allows them to be chained and prevents polling).
166
- // force_mode - (Boolean) If true, execute that id's doTimeout callback
167
- // immediately and synchronously, continuing any callback return-true
168
- // polling loop. If false, execute the callback immediately and
169
- // synchronously but do NOT continue a callback return-true polling loop.
170
- // If omitted, cancel that id's doTimeout.
171
- //
172
- // Returns:
173
- //
174
- // When creating a <jQuery.fn.doTimeout>, the initial jQuery collection of
175
- // elements is returned. Otherwise, if force_mode is true, false or undefined
176
- // and there is a yet-to-be-executed callback to cancel, true is returned,
177
- // but if no callback remains to be executed, undefined is returned.
178
-
179
- $.fn[doTimeout] = function() {
180
- var args = aps.call( arguments ),
181
- result = p_doTimeout.apply( this, [ doTimeout + args[0] ].concat( args ) );
182
-
183
- return typeof args[0] === 'number' || typeof args[1] === 'number'
184
- ? this
185
- : result;
186
- };
187
-
188
- function p_doTimeout( jquery_data_key ) {
189
- var that = this,
190
- elem,
191
- data = {},
192
-
193
- // Allows the plugin to call a string callback method.
194
- method_base = jquery_data_key ? $.fn : $,
195
-
196
- // Any additional arguments will be passed to the callback.
197
- args = arguments,
198
- slice_args = 4,
199
-
200
- id = args[1],
201
- delay = args[2],
202
- callback = args[3];
203
-
204
- if ( typeof id !== 'string' ) {
205
- slice_args--;
206
-
207
- id = jquery_data_key = 0;
208
- delay = args[1];
209
- callback = args[2];
210
- }
211
-
212
- // If id is passed, store a data reference either as .data on the first
213
- // element in a jQuery collection, or in the internal cache.
214
- if ( jquery_data_key ) { // Note: key is 'doTimeout' + id
215
-
216
- // Get id-object from the first element's data, otherwise initialize it to {}.
217
- elem = that.eq(0);
218
- elem.data( jquery_data_key, data = elem.data( jquery_data_key ) || {} );
219
-
220
- } else if ( id ) {
221
- // Get id-object from the cache, otherwise initialize it to {}.
222
- data = cache[ id ] || ( cache[ id ] = {} );
223
- }
224
-
225
- // Clear any existing timeout for this id.
226
- data.id && clearTimeout( data.id );
227
- delete data.id;
228
-
229
- // Clean up when necessary.
230
- function cleanup() {
231
- if ( jquery_data_key ) {
232
- elem.removeData( jquery_data_key );
233
- } else if ( id ) {
234
- delete cache[ id ];
235
- }
236
- };
237
-
238
- // Yes, there actually is a setTimeout call in here!
239
- function actually_setTimeout() {
240
- data.id = setTimeout( function(){ data.fn(); }, delay );
241
- };
242
-
243
- if ( callback ) {
244
- // A callback (and delay) were specified. Store the callback reference for
245
- // possible later use, and then setTimeout.
246
- data.fn = function( no_polling_loop ) {
247
-
248
- // If the callback value is a string, it is assumed to be the name of a
249
- // method on $ or $.fn depending on where doTimeout was executed.
250
- if ( typeof callback === 'string' ) {
251
- callback = method_base[ callback ];
252
- }
253
-
254
- callback.apply( that, aps.call( args, slice_args ) ) === true && !no_polling_loop
255
-
256
- // Since the callback returned true, and we're not specifically
257
- // canceling a polling loop, do it again!
258
- ? actually_setTimeout()
259
-
260
- // Otherwise, clean up and quit.
261
- : cleanup();
262
- };
263
-
264
- // Set that timeout!
265
- actually_setTimeout();
266
-
267
- } else if ( data.fn ) {
268
- // No callback passed. If force_mode (delay) is true, execute the data.fn
269
- // callback immediately, continuing any callback return-true polling loop.
270
- // If force_mode is false, execute the data.fn callback immediately but do
271
- // NOT continue a callback return-true polling loop. If force_mode is
272
- // undefined, simply clean up. Since data.fn was still defined, whatever
273
- // was supposed to happen hadn't yet, so return true.
274
- delay === undefined ? cleanup() : data.fn( delay === false );
275
- return true;
276
-
277
- } else {
278
- // Since no callback was passed, and data.fn isn't defined, it looks like
279
- // whatever was supposed to happen already did. Clean up and quit!
280
- cleanup();
281
- }
282
-
283
- };
284
-
285
- })(jQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common/src/resources/js/jquery.ba-dotimeout.min.js DELETED
@@ -1 +0,0 @@
1
- !function(n){"$:nomunge";function t(t){function i(){t?a.removeData(t):d&&delete e[d]}function f(){u.id=setTimeout(function(){u.fn()},y)}var a,r=this,u={},c=t?n.fn:n,l=arguments,p=4,d=l[1],y=l[2],s=l[3];if("string"!=typeof d&&(p--,d=t=0,y=l[1],s=l[2]),t?(a=r.eq(0),a.data(t,u=a.data(t)||{})):d&&(u=e[d]||(e[d]={})),u.id&&clearTimeout(u.id),delete u.id,s)u.fn=function(n){"string"==typeof s&&(s=c[s]),s.apply(r,o.call(l,p))!==!0||n?i():f()},f();else{if(u.fn)return void 0===y?i():u.fn(y===!1),!0;i()}}var e={},i="doTimeout",o=Array.prototype.slice;n[i]=function(){return t.apply(window,[0].concat(o.call(arguments)))},n.fn[i]=function(){var n=o.call(arguments),e=t.apply(this,[i+n[0]].concat(n));return"number"==typeof n[0]||"number"==typeof n[1]?this:e}}(jQuery);
 
common/src/resources/js/notice-dismiss.js CHANGED
@@ -21,7 +21,15 @@
21
 
22
  $( document ).ready( function() {
23
  $( '.tribe-dismiss-notice.is-dismissible' ).on( 'click', '.notice-dismiss', function() {
24
- window.location.href = update_query_string( window.location.href, 'tribe-dismiss-notice', $( this ).parents( '.tribe-dismiss-notice' ).data( 'ref' ) );
 
 
 
 
 
 
 
 
25
  } );
26
  } );
27
  }( jQuery ) );
21
 
22
  $( document ).ready( function() {
23
  $( '.tribe-dismiss-notice.is-dismissible' ).on( 'click', '.notice-dismiss', function() {
24
+ var dismiss_ajaxurl = update_query_string( ajaxurl, 'tribe-dismiss-notice', $( this ).parents( '.tribe-dismiss-notice' ).data( 'ref' ) );
25
+
26
+ $.ajax( dismiss_ajaxurl, {
27
+ dataType: 'json',
28
+ method: 'POST',
29
+ data: {
30
+ action: 'tribe_notice_dismiss'
31
+ }
32
+ } );
33
  } );
34
  } );
35
  }( jQuery ) );
common/src/resources/js/notice-dismiss.min.js CHANGED
@@ -1 +1 @@
1
- !function(i){function n(i,n,e){var s=i.indexOf("#"),t=-1===s?"":i.substr(s);i=-1===s?i:i.substr(0,s);var o=new RegExp("([?&])"+n+"=.*?(&|$)","i"),r=-1!==i.indexOf("?")?"&":"?";return i=i.match(o)?i.replace(o,"$1"+n+"="+e+"$2"):i+r+n+"="+e,i+t}i(document).ready(function(){i(".tribe-dismiss-notice.is-dismissible").on("click",".notice-dismiss",function(){window.location.href=n(window.location.href,"tribe-dismiss-notice",i(this).parents(".tribe-dismiss-notice").data("ref"))})})}(jQuery);
1
+ !function(i){function s(i,s,t){var e=i.indexOf("#"),n=-1===e?"":i.substr(e);i=-1===e?i:i.substr(0,e);var a=new RegExp("([?&])"+s+"=.*?(&|$)","i"),r=-1!==i.indexOf("?")?"&":"?";return i=i.match(a)?i.replace(a,"$1"+s+"="+t+"$2"):i+r+s+"="+t,i+n}i(document).ready(function(){i(".tribe-dismiss-notice.is-dismissible").on("click",".notice-dismiss",function(){var t=s(ajaxurl,"tribe-dismiss-notice",i(this).parents(".tribe-dismiss-notice").data("ref"));i.ajax(t,{dataType:"json",method:"POST",data:{action:"tribe_notice_dismiss"}})})})}(jQuery);
common/src/resources/js/pue-notices.js ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var tribe_plugin_notices = tribe_plugin_notices || {};
2
+
3
+ /**
4
+ * Appends license key notifications inline within the plugin table.
5
+ *
6
+ * This is done via JS because the options for achieving the same things
7
+ * server-side are currently limited.
8
+ */
9
+ (function( $, my ) {
10
+ 'use strict';
11
+
12
+ my.init = function() {
13
+ for ( var plugin_slug in tribe_plugin_notices ) {
14
+ if ( ! tribe_plugin_notices.hasOwnProperty( plugin_slug ) ) {
15
+ continue;
16
+ }
17
+
18
+ var $row = $( tribe_plugin_notices[ plugin_slug ].message_row_html );
19
+ var $active_plugin_row = $( 'tr[data-plugin$="' + plugin_slug + '.php"].active' );
20
+
21
+ // Add the .update class to the plugin row and append our new row with the update message
22
+ $active_plugin_row.addClass( 'update' ).after( $row );
23
+ }
24
+ };
25
+
26
+ $( function() {
27
+ if ( 'object' === typeof tribe_plugin_notices ) {
28
+ my.init();
29
+ }
30
+ });
31
+ })( jQuery, tribe_plugin_notices );
common/src/resources/js/pue-notices.min.js ADDED
@@ -0,0 +1 @@
 
1
+ var tribe_plugin_notices=tribe_plugin_notices||{};!function(i,t){"use strict";t.init=function(){for(var t in tribe_plugin_notices)if(tribe_plugin_notices.hasOwnProperty(t)){var n=i(tribe_plugin_notices[t].message_row_html),e=i('tr[data-plugin$="'+t+'.php"].active');e.addClass("update").after(n)}},i(function(){"object"==typeof tribe_plugin_notices&&t.init()})}(jQuery,tribe_plugin_notices);
common/src/resources/js/tribe-common.js ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var tribe_auto_sysinfo = tribe_auto_sysinfo || {};
2
+
3
+ tribe_auto_sysinfo.ajax = {
4
+ event: {}
5
+ };
6
+
7
+ (function ( $, my ) {
8
+ 'use strict';
9
+
10
+ my.init = function () {
11
+ this.init_ajax();
12
+ this.init_copy();
13
+ };
14
+
15
+ /**
16
+ * Initialize system info opt in copy
17
+ */
18
+ my.init_copy = function () {
19
+
20
+ var clipboard = new Clipboard( '.system-info-copy-btn' );
21
+ var button_icon = '<span class="dashicons dashicons-clipboard license-btn"></span>';
22
+ var button_text = tribe_system_info.clipboard_btn_text;
23
+
24
+ //Prevent Button From Doing Anything Else
25
+ $( ".system-info-copy-btn" ).click( function ( e ) {
26
+ e.preventDefault();
27
+ } );
28
+
29
+ clipboard.on( 'success', function ( event ) {
30
+ event.clearSelection();
31
+ event.trigger.innerHTML = button_icon + '<span class="optin-success">' + tribe_system_info.clipboard_copied_text + '<span>';
32
+ window.setTimeout( function () {
33
+ event.trigger.innerHTML = button_icon + button_text;
34
+ }, 5000 );
35
+ } );
36
+
37
+ clipboard.on( 'error', function ( event ) {
38
+ event.trigger.innerHTML = button_icon + '<span class="optin-fail">' + tribe_system_info.clipboard_fail_text + '<span>';
39
+ window.setTimeout( function () {
40
+ event.trigger.innerHTML = button_icon + button_text;
41
+ }, 5000 );
42
+ } );
43
+
44
+ };
45
+
46
+ /**
47
+ * Initialize system info opt in
48
+ */
49
+ my.init_ajax = function () {
50
+
51
+ this.$system_info_opt_in = $( "#tribe_auto_sysinfo_opt_in" );
52
+ this.$system_info_opt_in_msg = $( ".tribe-sysinfo-optin-msg" );
53
+
54
+ this.$system_info_opt_in.change( function () {
55
+ if ( this.checked ) {
56
+ my.event.ajax( "generate" );
57
+ } else {
58
+ my.event.ajax( "remove" );
59
+ }
60
+
61
+ } );
62
+
63
+ };
64
+
65
+ my.event.ajax = function ( generate ) {
66
+
67
+ var request = {
68
+ "action": "tribe_toggle_sysinfo_optin",
69
+ "confirm": tribe_system_info.sysinfo_optin_nonce,
70
+ "generate_key": generate
71
+ };
72
+
73
+ // Send our request
74
+ $.post(
75
+ ajaxurl,
76
+ request,
77
+ function ( results ) {
78
+ if ( results.success ) {
79
+ my.$system_info_opt_in_msg.html( "<p class=\'optin-success\'>" + results.data + "</p>" );
80
+ } else {
81
+ my.$system_info_opt_in_msg.html( "<p class=\'optin-fail\'>" + results.data.message + " Code:" + results.data.code + " Status:" + results.data.data.status + "</p>" );
82
+ $( "#tribe_auto_sysinfo_opt_in" ).prop( "checked", false );
83
+ }
84
+ } );
85
+
86
+ };
87
+
88
+ $( function () {
89
+ my.init();
90
+ } );
91
+
92
+ })( jQuery, tribe_auto_sysinfo.ajax );
common/src/resources/js/tribe-common.min.js ADDED
@@ -0,0 +1 @@
 
1
+ var tribe_auto_sysinfo=tribe_auto_sysinfo||{};tribe_auto_sysinfo.ajax={event:{}},function(t,i){"use strict";i.init=function(){this.init_ajax(),this.init_copy()},i.init_copy=function(){var i=new Clipboard(".system-info-copy-btn"),n='<span class="dashicons dashicons-clipboard license-btn"></span>',s=tribe_system_info.clipboard_btn_text;t(".system-info-copy-btn").click(function(t){t.preventDefault()}),i.on("success",function(t){t.clearSelection(),t.trigger.innerHTML=n+'<span class="optin-success">'+tribe_system_info.clipboard_copied_text+"<span>",window.setTimeout(function(){t.trigger.innerHTML=n+s},5e3)}),i.on("error",function(t){t.trigger.innerHTML=n+'<span class="optin-fail">'+tribe_system_info.clipboard_fail_text+"<span>",window.setTimeout(function(){t.trigger.innerHTML=n+s},5e3)})},i.init_ajax=function(){this.$system_info_opt_in=t("#tribe_auto_sysinfo_opt_in"),this.$system_info_opt_in_msg=t(".tribe-sysinfo-optin-msg"),this.$system_info_opt_in.change(function(){this.checked?i.event.ajax("generate"):i.event.ajax("remove")})},i.event.ajax=function(n){var s={action:"tribe_toggle_sysinfo_optin",confirm:tribe_system_info.sysinfo_optin_nonce,generate_key:n};t.post(ajaxurl,s,function(n){n.success?i.$system_info_opt_in_msg.html("<p class='optin-success'>"+n.data+"</p>"):(i.$system_info_opt_in_msg.html("<p class='optin-fail'>"+n.data.message+" Code:"+n.data.code+" Status:"+n.data.data.status+"</p>"),t("#tribe_auto_sysinfo_opt_in").prop("checked",!1))})},t(function(){i.init()})}(jQuery,tribe_auto_sysinfo.ajax);
common/src/resources/js/tribe-datatables.js ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ window.tribe_data_table = null;
2
+
3
+ ( function( $ ) {
4
+ 'use strict';
5
+
6
+ $.fn.tribeDataTable = function( options ) {
7
+ var $document = $( document );
8
+ var settings = $.extend( {
9
+ language: {
10
+ lengthMenu : tribe_l10n_datatables.length_menu,
11
+ emptyTable : tribe_l10n_datatables.emptyTable,
12
+ info : tribe_l10n_datatables.info,
13
+ infoEmpty : tribe_l10n_datatables.info_empty,
14
+ infoFiltered : tribe_l10n_datatables.info_filtered,
15
+ zeroRecords : tribe_l10n_datatables.zero_records,
16
+ search : tribe_l10n_datatables.search,
17
+ paginate : {
18
+ next : tribe_l10n_datatables.pagination.next,
19
+ previous : tribe_l10n_datatables.pagination.previous,
20
+ },
21
+ aria : {
22
+ sortAscending : tribe_l10n_datatables.aria.sort_ascending,
23
+ sortDescending : tribe_l10n_datatables.aria.sort_descending
24
+ },
25
+ select: {
26
+ rows: {
27
+ '0': tribe_l10n_datatables.select.rows[0],
28
+ _: tribe_l10n_datatables.select.rows._,
29
+ '1': tribe_l10n_datatables.select.rows[1]
30
+ }
31
+ }
32
+ },
33
+ lengthMenu: [
34
+ [10, 25, 50, -1],
35
+ [10, 25, 50, tribe_l10n_datatables.pagination.all ]
36
+ ],
37
+ }, options );
38
+
39
+ var only_data = false;
40
+ if ( this.is( '.dataTable' ) ) {
41
+ only_data = true;
42
+ }
43
+
44
+ var methods = {
45
+ toggle_global_checkbox: function( $checkbox, table ) {
46
+ var $table = $checkbox.closest( '.dataTable' );
47
+ var $header_checkbox = $table.find( 'thead .column-cb input:checkbox' );
48
+ var $footer_checkbox = $table.find( 'tfoot .column-cb input:checkbox' );
49
+
50
+ if ( $checkbox.is( ':checked' ) ) {
51
+ $table.find( 'tbody .check-column input:checkbox' ).prop( 'checked', true );
52
+ $header_checkbox.prop( 'checked', true );
53
+ $footer_checkbox.prop( 'checked', true );
54
+ var $filtered = table.$( 'tr', { 'filter': 'applied' } );
55
+
56
+ if ( $filtered.length ) {
57
+ table.rows( { search: 'applied' } ).select();
58
+ } else {
59
+ table.rows().select();
60
+ }
61
+ return;
62
+ }
63
+
64
+ $table.find( 'tbody .check-column input:checkbox' ).prop( 'checked', false );
65
+ $header_checkbox.prop( 'checked', false );
66
+ $footer_checkbox.prop( 'checked', false );
67
+ table.rows().deselect();
68
+ },
69
+ toggle_row_checkbox: function( $checkbox, table ) {
70
+ var $row = $checkbox.closest( 'tr' );
71
+
72
+ if ( $checkbox.is( ':checked' ) ) {
73
+ table.row( $row ).select();
74
+ return;
75
+ }
76
+
77
+ table.row( $row ).deselect();
78
+ $checkbox.closest( '.dataTable' ).find( 'thead .column-cb input:checkbox, tfoot .column-cb input:checkbox' ).prop( 'checked', false );
79
+ }
80
+ };
81
+
82
+ return this.each( function() {
83
+ var $el = $( this );
84
+ var table;
85
+
86
+ if ( only_data ) {
87
+ table = $el.DataTable();
88
+ } else {
89
+ table = $el.DataTable( settings );
90
+ }
91
+
92
+ window.tribe_data_table = table;
93
+
94
+ if ( 'undefined' !== typeof settings.data ) {
95
+ table.clear().draw();
96
+ table.rows.add( settings.data );
97
+ table.draw();
98
+ }
99
+
100
+ $el.on(
101
+ 'click',
102
+ 'thead .column-cb input:checkbox, tfoot .column-cb input:checkbox',
103
+ function() {
104
+ methods.toggle_global_checkbox( $( this ), table );
105
+ }
106
+ );
107
+
108
+ $el.on(
109
+ 'click',
110
+ 'tbody .check-column input:checkbox',
111
+ function() {
112
+ methods.toggle_row_checkbox( $( this ), table );
113
+ }
114
+ );
115
+ } );
116
+ };
117
+ } )( jQuery );
common/src/resources/js/tribe-datatables.min.js ADDED
@@ -0,0 +1 @@
 
1
+ window.tribe_data_table=null,function(e){"use strict";e.fn.tribeDataTable=function(t){var a=(e(document),e.extend({language:{lengthMenu:tribe_l10n_datatables.length_menu,emptyTable:tribe_l10n_datatables.emptyTable,info:tribe_l10n_datatables.info,infoEmpty:tribe_l10n_datatables.info_empty,infoFiltered:tribe_l10n_datatables.info_filtered,zeroRecords:tribe_l10n_datatables.zero_records,search:tribe_l10n_datatables.search,paginate:{next:tribe_l10n_datatables.pagination.next,previous:tribe_l10n_datatables.pagination.previous},aria:{sortAscending:tribe_l10n_datatables.aria.sort_ascending,sortDescending:tribe_l10n_datatables.aria.sort_descending},select:{rows:{0:tribe_l10n_datatables.select.rows[0],_:tribe_l10n_datatables.select.rows._,1:tribe_l10n_datatables.select.rows[1]}}},lengthMenu:[[10,25,50,-1],[10,25,50,tribe_l10n_datatables.pagination.all]]},t)),c=!1;this.is(".dataTable")&&(c=!0);var n={toggle_global_checkbox:function(e,t){var a=e.closest(".dataTable"),c=a.find("thead .column-cb input:checkbox"),n=a.find("tfoot .column-cb input:checkbox");if(e.is(":checked")){a.find("tbody .check-column input:checkbox").prop("checked",!0),c.prop("checked",!0),n.prop("checked",!0);var o=t.$("tr",{filter:"applied"});return void(o.length?t.rows({search:"applied"}).select():t.rows().select())}a.find("tbody .check-column input:checkbox").prop("checked",!1),c.prop("checked",!1),n.prop("checked",!1),t.rows().deselect()},toggle_row_checkbox:function(e,t){var a=e.closest("tr");return e.is(":checked")?void t.row(a).select():(t.row(a).deselect(),void e.closest(".dataTable").find("thead .column-cb input:checkbox, tfoot .column-cb input:checkbox").prop("checked",!1))}};return this.each(function(){var t,o=e(this);t=c?o.DataTable():o.DataTable(a),window.tribe_data_table=t,"undefined"!=typeof a.data&&(t.clear().draw(),t.rows.add(a.data),t.draw()),o.on("click","thead .column-cb input:checkbox, tfoot .column-cb input:checkbox",function(){n.toggle_global_checkbox(e(this),t)}),o.on("click","tbody .check-column input:checkbox",function(){n.toggle_row_checkbox(e(this),t)})})}}(jQuery);
common/src/resources/postcss/app-shop.pcss CHANGED
@@ -1,58 +1,84 @@
1
- .tribe-addon {
2
- -webkit-box-sizing: border-box;
3
- -moz-box-sizing: border-box;
4
- box-sizing: border-box;
5
- display: inline-block;
6
- margin-right: 10px;
7
- overflow: hidden;
8
- padding: 20px 20px 20px 0;
9
- vertical-align: top;
10
- width: 300px;
11
- }
12
 
13
- .tribe-addon .thumb img {
14
- max-width: 100%;
15
- width: 100%;
16
- }
17
 
18
- .tribe-addon h4 {
19
- font-size: 1.17em;
20
- }
 
21
 
22
- .tribe-addon h4 a {
23
- text-decoration: none;
24
- }
 
25
 
26
- .addon-grid {
27
- width: 100%;
28
- }
 
 
 
 
 
 
 
 
 
29
 
30
- .category-title {
31
- clear: both;
32
- display: block;
33
- margin-bottom: 10px;
34
- }
35
 
36
- .tribe-addon.first {
37
- border-bottom: 1px solid #dfdfdf;
38
- margin: 20px 0 10px;
39
- overflow: hidden;
40
- padding: 0 0 20px 330px;
41
- width: 100%;
42
- }
43
 
44
- .tribe-addon.first h4 {
45
- font-size: 20px;
46
- line-height: 1.4;
47
- margin: 0;
48
- }
 
49
 
50
- .tribe-addon.first .thumb {
51
- float: left;
52
- margin-left: -330px;
53
- width: 300px;
54
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
- .tribe-addon.first .description {
57
- max-width: 600px;
 
 
 
 
 
58
  }
1
+ #tribe-app-shop {
2
+ max-width: 960px;
 
 
 
 
 
 
 
 
 
3
 
4
+ .addon-grid {
5
+ width: 100%;
6
+ }
 
7
 
8
+ .header {
9
+ h1 {
10
+ display: inline-block;
11
+ }
12
 
13
+ .button {
14
+ margin-top: 10px;
15
+ }
16
+ }
17
 
18
+ .tribe-addon {
19
+ background-color: #fff;
20
+ border-bottom: 1px solid #dfdfdf;
21
+ display: inline-block;
22
+ margin: 0 15px 15px 0;
23
+ overflow: hidden;
24
+ padding: 0;
25
+ position: relative;
26
+ vertical-align: top;
27
+ width: 300px;
28
+ border-radius: 3px 3px 3px 3px;
29
+ box-sizing: border-box;
30
 
31
+ h4 {
32
+ font-size: 1.17em;
33
+ margin: 15px 0;
 
 
34
 
35
+ a {
36
+ text-decoration: none;
37
+ }
38
+ }
 
 
 
39
 
40
+ .button {
41
+ bottom: 15px;
42
+ display: block;
43
+ position: absolute;
44
+ width: 123px;
45
+ }
46
 
47
+ .caption {
48
+ padding: 0 15px 45px 15px;
49
+ }
50
+
51
+ .thumb {
52
+ img {
53
+ height: 228px;
54
+ max-width: 100%;
55
+ width: 300px;
56
+ }
57
+ }
58
+
59
+ &.first {
60
+ margin: 20px 0;
61
+ overflow: hidden;
62
+ padding: 0;
63
+ width: 937px;
64
+
65
+ h4 {
66
+ font-size: 20px;
67
+ line-height: 1.4;
68
+ margin: 15px 0 0 0;
69
+ }
70
+
71
+ .caption {
72
+ display: inline-block;
73
+ padding-left: 20px;
74
+ width: 600px;
75
+ }
76
 
77
+ .thumb {
78
+ float: left;
79
+ height: 228px;
80
+ width: 300px;
81
+ }
82
+ }
83
+ }
84
  }
common/src/resources/postcss/bumpdown.pcss ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* = Bumpdown CSS
2
+ =============================================*/
3
+ .wrap .tribe-bumpdown,
4
+ .tribe-bumpdown,
5
+ .tribe-bumpdown-cell.tribe-bumpdown {
6
+ display: none;
7
+ margin: 1rem 0;
8
+
9
+ .tribe-bumpdown-content {
10
+ background: #f1f1f1;
11
+ padding: 10px 35px 10px 10px;
12
+ font-size: 12px;
13
+ position: relative;
14
+ }
15
+ }
16
+
17
+ .tribe-bumpdown-cell.tribe-bumpdown {
18
+ padding: 0;
19
+ }
20
+
21
+ #poststuff .tribe-bumpdown h1,
22
+ #poststuff .tribe-bumpdown h2,
23
+ #poststuff .tribe-bumpdown h3,
24
+ #poststuff .tribe-bumpdown h4,
25
+ .tribe-bumpdown h1,
26
+ .tribe-bumpdown h2,
27
+ .tribe-bumpdown h3,
28
+ .tribe-bumpdown h4 {
29
+ padding-left: 0;
30
+ padding-top: 0;
31
+ }
32
+
33
+ .tribe-bumpdown-arrow {
34
+ position: absolute;
35
+ width: 0;
36
+ height: 0;
37
+ top: -11px;
38
+ margin-left: -18px;
39
+ border-left: 8px solid transparent;
40
+ border-right: 8px solid transparent;
41
+
42
+ border-bottom: 11px solid #f1f1f1;
43
+ }
44
+
45
+ .tribe-bumpdown-close {
46
+ color: #686868;
47
+ cursor: pointer;
48
+ position: absolute;
49
+ right: .5rem;
50
+ top: .5rem;
51
+ z-index: 2;
52
+ }
53
+
54
+ .tribe-bumpdown-trigger .target {
55
+ color: #0074a2;
56
+ }
57
+
58
+ @media screen and (max-width: 782px) {
59
+ .wrap td.tribe-bumpdown {
60
+ padding-right: 35px;
61
+ }
62
+
63
+ .tribe-bumpdown-arrow {
64
+ margin-left: -15px;
65
+ }
66
+ }
common/src/resources/postcss/datatables.pcss ADDED
@@ -0,0 +1,477 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Table styles
3
+ */
4
+ table.dataTable {
5
+ border-collapse: separate;
6
+ border-spacing: 0;
7
+ clear: both;
8
+ margin: 0 auto;
9
+ width: 100%;
10
+
11
+ thead {
12
+ th:active,
13
+ td:active {
14
+ outline: none;
15
+ }
16
+
17
+ .sorting,
18
+ .sorting_asc,
19
+ .sorting_desc {
20
+ cursor: pointer;
21
+ *cursor: hand;
22
+ }
23
+
24
+ .sorting,
25
+ .sorting_asc,
26
+ .sorting_desc,
27
+ .sorting_asc_disabled,
28
+ .sorting_desc_disabled {
29
+ background-position: center right;
30
+ background-repeat: no-repeat;
31
+ }
32
+
33
+ .sorting {
34
+ background-image: url("../../../vendor/datatables/media/images/sort_both.png");
35
+ }
36
+
37
+ .sorting_asc {
38
+ background-image: url("../../../vendor/datatables/media/images/sort_asc.png");
39
+ }
40
+
41
+ .sorting_desc {
42
+ background-image: url("../../../vendor/datatables/media/images/sort_desc.png");
43
+ }
44
+
45
+ .sorting_asc_disabled {
46
+ background-image: url("../../../vendor/datatables/media/images/sort_asc_disabled.png");
47
+ }
48
+
49
+ .sorting_desc_disabled {
50
+ background-image: url("../../../vendor/datatables/media/images/sort_desc_disabled.png");
51
+ }
52
+ }
53
+
54
+ &.widefat {
55
+ thead,
56
+ tfoot {
57
+ th,
58
+ td {
59
+ input {
60
+ margin: 0 0 0 8px;
61
+ vertical-align: text-top;
62
+ }
63
+ }
64
+
65
+ td.check-column {
66
+ padding-top: 4px;
67
+ vertical-align: middle;
68
+ }
69
+ }
70
+
71
+ thead,
72
+ tfoot,
73
+ tbody {
74
+ th.check-column {
75
+ padding: 11px 0 0 3px;
76
+ }
77
+ }
78
+ }
79
+
80
+ .check-column {
81
+ width: 2.2em;
82
+ }
83
+
84
+ &.row-border tbody th, &.row-border tbody td, &.display tbody th, &.display tbody td {
85
+ border-top: 1px solid #ddd;
86
+ }
87
+
88
+ &.row-border tbody tr:first-child th,
89
+ &.row-border tbody tr:first-child td, &.display tbody tr:first-child th,
90
+ &.display tbody tr:first-child td {
91
+ border-top: none;
92
+ }
93
+
94
+ &.cell-border tbody th, &.cell-border tbody td {
95
+ border-top: 1px solid #ddd;
96
+ border-right: 1px solid #ddd;
97
+ }
98
+
99
+ &.cell-border tbody tr th:first-child,
100
+ &.cell-border tbody tr td:first-child {
101
+ border-left: 1px solid #ddd;
102
+ }
103
+
104
+ &.cell-border tbody tr:first-child th,
105
+ &.cell-border tbody tr:first-child td {
106
+ border-top: none;
107
+ }
108
+
109
+ &.stripe tbody tr.odd, &.display tbody tr.odd {
110
+ background-color: #f9f9f9;
111
+ }
112
+
113
+ &.hover tbody tr:hover, &.display tbody tr:hover {
114
+ background-color: #f6f6f6;
115
+ }
116
+
117
+ &.order-column tbody tr > .sorting_1,
118
+ &.order-column tbody tr > .sorting_2,
119
+ &.order-column tbody tr > .sorting_3, &.display tbody tr > .sorting_1,
120
+ &.display tbody tr > .sorting_2,
121
+ &.display tbody tr > .sorting_3 {
122
+ background-color: #fafafa;
123
+ }
124
+
125
+ &.display tbody tr.odd > .sorting_1, &.order-column.stripe tbody tr.odd > .sorting_1 {
126
+ background-color: #f1f1f1;
127
+ }
128
+
129
+ &.display tbody tr.odd > .sorting_2, &.order-column.stripe tbody tr.odd > .sorting_2 {
130
+ background-color: #f3f3f3;
131
+ }
132
+
133
+ &.display tbody tr.odd > .sorting_3, &.order-column.stripe tbody tr.odd > .sorting_3 {
134
+ background-color: whitesmoke;
135
+ }
136
+
137
+ &.display tbody tr.even > .sorting_1, &.order-column.stripe tbody tr.even > .sorting_1 {
138
+ background-color: #fafafa;
139
+ }
140
+
141
+ &.display tbody tr.even > .sorting_2, &.order-column.stripe tbody tr.even > .sorting_2 {
142
+ background-color: #fcfcfc;
143
+ }
144
+
145
+ &.display tbody tr.even > .sorting_3, &.order-column.stripe tbody tr.even > .sorting_3 {
146
+ background-color: #fefefe;
147
+ }
148
+
149
+ &.display tbody tr:hover > .sorting_1, &.order-column.hover tbody tr:hover > .sorting_1 {
150
+ background-color: #eaeaea;
151
+ }
152
+
153
+ &.display tbody tr:hover > .sorting_2, &.order-column.hover tbody tr:hover > .sorting_2 {
154
+ background-color: #ececec;
155
+ }
156
+
157
+ &.display tbody tr:hover > .sorting_3, &.order-column.hover tbody tr:hover > .sorting_3 {
158
+ background-color: #efefef;
159
+ }
160
+
161
+ &.no-footer {
162
+ border-bottom: 1px solid #111;
163
+ }
164
+
165
+ &.nowrap th, &.nowrap td {
166
+ white-space: nowrap;
167
+ }
168
+
169
+ &.compact thead th,
170
+ &.compact thead td {
171
+ padding: 4px 17px 4px 4px;
172
+ }
173
+
174
+ &.compact tfoot th,
175
+ &.compact tfoot td {
176
+ padding: 4px;
177
+ }
178
+
179
+ &.compact tbody th,
180
+ &.compact tbody td {
181
+ padding: 4px;
182
+ }
183
+
184
+ th.dt-left,
185
+ td.dt-left {
186
+ text-align: left;
187
+ }
188
+
189
+ th.dt-center,
190
+ td.dt-center,
191
+ td.dataTables_empty {
192
+ text-align: center;
193
+ }
194
+
195
+ th.dt-right,
196
+ td.dt-right {
197
+ text-align: right;
198
+ }
199
+
200
+ th.dt-justify,
201
+ td.dt-justify {
202
+ text-align: justify;
203
+ }
204
+
205
+ th.dt-nowrap,
206
+ td.dt-nowrap {
207
+ white-space: nowrap;
208
+ }
209
+
210
+ thead th.dt-head-left,
211
+ thead td.dt-head-left,
212
+ tfoot th.dt-head-left,
213
+ tfoot td.dt-head-left {
214
+ text-align: left;
215
+ }
216
+
217
+ thead th.dt-head-center,
218
+ thead td.dt-head-center,
219
+ tfoot th.dt-head-center,
220
+ tfoot td.dt-head-center {
221
+ text-align: center;
222
+ }
223
+
224
+ thead th.dt-head-right,
225
+ thead td.dt-head-right,
226
+ tfoot th.dt-head-right,
227
+ tfoot td.dt-head-right {
228
+ text-align: right;
229
+ }
230
+
231
+ thead th.dt-head-justify,
232
+ thead td.dt-head-justify,
233
+ tfoot th.dt-head-justify,
234
+ tfoot td.dt-head-justify {
235
+ text-align: justify;
236
+ }
237
+
238
+ thead th.dt-head-nowrap,
239
+ thead td.dt-head-nowrap,
240
+ tfoot th.dt-head-nowrap,
241
+ tfoot td.dt-head-nowrap {
242
+ white-space: nowrap;
243
+ }
244
+
245
+ tbody th.dt-body-left,
246
+ tbody td.dt-body-left {
247
+ text-align: left;
248
+ }
249
+
250
+ tbody th.dt-body-center,
251
+ tbody td.dt-body-center {
252
+ text-align: center;
253
+ }
254
+
255
+ tbody th.dt-body-right,
256
+ tbody td.dt-body-right {
257
+ text-align: right;
258
+ }
259
+
260
+ tbody th.dt-body-justify,
261
+ tbody td.dt-body-justify {
262
+ text-align: justify;
263
+ }
264
+
265
+ tbody th.dt-body-nowrap,
266
+ tbody td.dt-body-nowrap {
267
+ white-space: nowrap;
268
+ }
269
+
270
+ &,
271
+ th,
272
+ td {
273
+ -webkit-box-sizing: content-box;
274
+ box-sizing: content-box;
275
+ }
276
+ }
277
+
278
+ /*
279
+ * Control feature layout
280
+ */
281
+ .dataTables_wrapper {
282
+ clear: both;
283
+ position: relative;
284
+ zoom: 1;
285
+
286
+ .dataTables_length {
287
+ float: left;
288
+ }
289
+
290
+ .dataTables_filter {
291
+ float: right;
292
+ text-align: right;
293
+ }
294
+
295
+ .dataTables_filter input {
296
+ margin-left: 0.5em;
297
+ }
298
+
299
+ .dataTables_info {
300
+ clear: both;
301
+ float: left;
302
+ padding-top: 0.755em;
303
+ }
304
+
305
+ .dataTables_paginate {
306
+ float: right;
307
+ padding-top: 0.25em;
308
+ text-align: right;
309
+
310
+ .paginate_button {
311
+ border-color: transparent;
312
+ border-radius: 3px;
313
+ border-style: solid;
314
+ border-width: 1px;
315
+ box-shadow: none;
316
+ box-sizing: border-box;
317
+ color: #555;
318
+ cursor: pointer;
319
+ display: inline-block;
320
+ font-size: 13px;
321
+ height: 28px;
322
+ line-height: 26px;
323
+ margin: 0 0 0 2px;
324
+ padding: 0 10px 1px;
325
+ text-decoration: none;
326
+ vertical-align: top;
327
+ white-space: nowrap;
328
+
329
+ &:hover,
330
+ &:active {
331
+ background: #fafafa;
332
+ border-color: #999;
333
+ color: #23282d;
334
+ }
335
+
336
+ &.next,
337
+ &.previous {
338
+ background: transparent;
339
+ border-color: transparent;
340
+ box-shadow: none;
341
+ color: #23282d;
342
+
343
+ &.disabled {
344
+ background: transparent;
345
+ border-color: transparent;
346
+ box-shadow: none;
347
+
348
+ &:hover {
349
+ background: transparent;
350
+ border-color: transparent;
351
+ box-shadow: none;
352
+ text-decoration: none;
353
+ }
354
+ }
355
+
356
+ &:hover {
357
+ color: #222;
358
+ text-decoration: underline;
359
+ }
360
+ }
361
+
362
+ &.current,
363
+ &.current:hover {
364
+ background: #fcfcfc;
365
+ border-color: #999;
366
+ box-shadow: 0 1px 0 #cccccc;
367
+ color: #23282d;
368
+ text-decoration: none;
369
+ }
370
+
371
+ &.disabled,
372
+ &.disabled:hover,
373
+ &.disabled:active {
374
+ background: #f7f7f7;
375
+ border-color: #ddd;
376
+ box-shadow: none;
377
+ color: #a0a5aa;
378
+ cursor: default;
379
+ text-shadow: 0 1px 0 #fff;
380
+ transform: none;
381
+ }
382
+ }
383
+
384
+ .ellipsis {
385
+ padding: 0 1em;
386
+ }
387
+ }
388
+
389
+ .dataTables_processing {
390
+ position: absolute;
391
+ top: 50%;
392
+ left: 50%;
393
+ width: 100%;
394
+ height: 40px;
395
+ margin-left: -50%;
396
+ margin-top: -25px;
397
+ padding-top: 20px;
398
+ text-align: center;
399
+ font-size: 1.2em;
400
+ background-color: white;
401
+ background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(25%, rgba(255, 255, 255, 0.9)), color-stop(75%, rgba(255, 255, 255, 0.9)), color-stop(100%, rgba(255, 255, 255, 0)));
402
+ background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
403
+ background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
404
+ background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
405
+ background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
406
+ background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
407
+ }
408
+
409
+ .dataTables_length,
410
+ .dataTables_filter,
411
+ .dataTables_info,
412
+ .dataTables_processing,
413
+ .dataTables_paginate {
414
+ color: #333;
415
+ }
416
+
417
+ .dataTables_scroll {
418
+ clear: both;
419
+ }
420
+
421
+ .dataTables_scroll div.dataTables_scrollBody {
422
+ *margin-top: -1px;
423
+ -webkit-overflow-scrolling: touch;
424
+ }
425
+
426
+ .dataTables_scroll div.dataTables_scrollBody th, .dataTables_scroll div.dataTables_scrollBody td {
427
+ vertical-align: middle;
428
+ }
429
+
430
+ .dataTables_scroll div.dataTables_scrollBody th > div.dataTables_sizing,
431
+ .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td > div.dataTables_sizing {
432
+ height: 0;
433
+ overflow: hidden;
434
+ margin: 0 !important;
435
+ padding: 0 !important;
436
+ }
437
+
438
+ &.no-footer .dataTables_scrollBody {
439
+ border-bottom: 1px solid #111;
440
+ }
441
+
442
+ &.no-footer div.dataTables_scrollHead table,
443
+ &.no-footer div.dataTables_scrollBody table {
444
+ border-bottom: none;
445
+ }
446
+
447
+ &:after {
448
+ visibility: hidden;
449
+ display: block;
450
+ content: "";
451
+ clear: both;
452
+ height: 0;
453
+ }
454
+ }
455
+
456
+ @media screen and (max-width: 767px) {
457
+ .dataTables_wrapper .dataTables_info,
458
+ .dataTables_wrapper .dataTables_paginate {
459
+ float: none;
460
+ text-align: center;
461
+ }
462
+
463
+ .dataTables_wrapper .dataTables_paginate {
464
+ margin-top: 0.5em;
465
+ }
466
+ }
467
+ @media screen and (max-width: 640px) {
468
+ .dataTables_wrapper .dataTables_length,
469
+ .dataTables_wrapper .dataTables_filter {
470
+ float: none;
471
+ text-align: center;
472
+ }
473
+
474
+ .dataTables_wrapper .dataTables_filter {
475
+ margin-top: 0.5em;
476
+ }
477
+ }
common/src/resources/postcss/dependency.pcss ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Dependency CSS */
2
+ .tribe-dependent {
3
+ display: none;
4
+
5
+ &.tribe-active {
6
+ display: inline-block;
7
+
8
+ div& {
9
+ display: block;
10
+
11
+ &.select2-container {
12
+ display: inline-block;
13
+ }
14
+ }
15
+
16
+ td& {
17
+ display: table-cell;
18
+ }
19
+
20
+ tr& {
21
+ display: table-row;
22
+ }
23
+ }
24
+ }
common/src/resources/postcss/tribe-common-admin.pcss CHANGED
@@ -128,7 +128,8 @@ table.plugins .tribe-plugin-update-message a {
128
  .tribe-settings-form .tribe-field-radio label,
129
  .tribe-settings-form .tribe-field-checkbox_list label {
130
  display: block;
131
- margin: 5px 0;
 
132
  }
133
  .tribe-settings-form .tribe-field-radio label input,
134
  .tribe-settings-form .tribe-field-checkbox_list label input {
@@ -149,9 +150,8 @@ table.plugins .tribe-plugin-update-message a {
149
  margin-bottom: 10px;
150
  padding: 6px 0 6px 12px;
151
  }
152
- .tribe-settings-form .tribe-settings-form-wrap h3 ~ h3 {
153
- margin-top: 2.25em;
154
- }
155
  .tribe-settings-form .tribe-settings-form-wrap h3 + p {
156
  margin: 0 0 10px;
157
  padding-left: 12px;
@@ -275,9 +275,11 @@ table.plugins .tribe-plugin-update-message a {
275
  .key-validity {
276
  display: inline-block;
277
  }
 
278
  .invalid-key {
279
  color: red;
280
  }
 
281
  .valid-key {
282
  color: green;
283
  }
@@ -448,7 +450,7 @@ table.plugins .tribe-plugin-update-message a {
448
 
449
  #tribe-log-controls {
450
  padding-bottom: 1rem;
451
-
452
  /* For consistency with help screen h3 and p elements */
453
  padding-left: 12px;
454
 
@@ -494,6 +496,16 @@ table.plugins .tribe-plugin-update-message a {
494
  padding-left: 10px;
495
  }
496
 
 
 
 
 
 
 
 
 
 
 
497
  .template-updates-wrapper p {
498
  margin-top: 0;
499
  }
@@ -948,44 +960,169 @@ a.tribe-rating-link {
948
  background: #7ad03a;
949
  }
950
 
951
- /* bumpdown */
952
- .bumpdown {
953
- background: #f1f1f1;
954
- margin: 1rem 0;
955
- padding: 1rem 1.5rem 1rem 1rem;
956
- position: relative;
957
- }
958
 
959
- #poststuff .bumpdown h1,
960
- #poststuff .bumpdown h2,
961
- #poststuff .bumpdown h3,
962
- #poststuff .bumpdown h4,
963
- .bumpdown h1,
964
- .bumpdown h2,
965
- .bumpdown h3,
966
- .bumpdown h4 {
967
- padding-left: 0;
968
- padding-top: 0;
969
- }
970
 
971
- .bumpdown-arrow {
972
- display: none;
973
- }
 
974
 
975
- .bumpdown-close {
976
- color: #686868;
977
- cursor: pointer;
978
- position: absolute;
979
- right: .5rem;
980
- top: .5rem;
981
- z-index: 2;
982
- }
983
 
984
- .bumpdown-trigger .target {
985
- color: #0074a2;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
986
  }
987
 
988
  /* Useful to ensure modals rise above the grey 'miasma' */
989
  .ui-front {
990
  z-index: 1000000;
991
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  .tribe-settings-form .tribe-field-radio label,
129
  .tribe-settings-form .tribe-field-checkbox_list label {
130
  display: block;
131
+ margin: 5px 0 5px 20px;
132
+ text-indent: -20px;
133
  }
134
  .tribe-settings-form .tribe-field-radio label input,
135
  .tribe-settings-form .tribe-field-checkbox_list label input {
150
  margin-bottom: 10px;
151
  padding: 6px 0 6px 12px;
152
  }
153
+ .tribe-settings-form .tribe-settings-form-wrap .tribe-sysinfo-optin-msg,
154
+ .tribe-settings-form .tribe-settings-form-wrap .system-info,
 
155
  .tribe-settings-form .tribe-settings-form-wrap h3 + p {
156
  margin: 0 0 10px;
157
  padding-left: 12px;
275
  .key-validity {
276
  display: inline-block;
277
  }
278
+ .optin-fail,
279
  .invalid-key {
280
  color: red;
281
  }
282
+ .optin-success,
283
  .valid-key {
284
  color: green;
285
  }
450
 
451
  #tribe-log-controls {
452
  padding-bottom: 1rem;
453
+
454
  /* For consistency with help screen h3 and p elements */
455
  padding-left: 12px;
456
 
496
  padding-left: 10px;
497
  }
498
 
499
+ .system-info-copy {
500
+ .system-info-copy-btn {
501
+ padding: 6px;
502
+
503
+ .dashicons {
504
+ padding-right: 10px;
505
+ }
506
+ }
507
+ }
508
+
509
  .template-updates-wrapper p {
510
  margin-top: 0;
511
  }
960
  background: #7ad03a;
961
  }
962
 
963
+ /* = Modals/thickbox dialogs
964
+ ============================ */
 
 
 
 
 
965
 
966
+ #tribe-dialog-wrapper {
967
+ & > div {
968
+ padding: 1rem;
 
 
 
 
 
 
 
 
969
 
970
+ .stage {
971
+ display: none;
972
+ }
973
+ }
974
 
975
+ #heading {
976
+ background: white;
977
+ }
978
+
979
+ label {
980
+ display: block;
981
+ }
 
982
 
983
+ .select-single-container {
984
+ border: 1px solid #888;
985
+ overflow-y: scroll;
986
+ height: 300px;
987
+
988
+ label {
989
+ opacity: 1;
990
+ padding: 3px 5px;
991
+ transition: opacity 0.2s;
992
+
993
+ &:nth-child(odd) {
994
+ background: white;
995
+ }
996
+
997
+ &.selected {
998
+ background: #0073aa;
999
+ color: white;
1000
+ font-weight: bold;
1001
+ }
1002
+
1003
+ input {
1004
+ display: none;
1005
+ }
1006
+ }
1007
+
1008
+ &.updating label {
1009
+ opacity: 0.35;
1010
+ transition: opacity 0.2s;
1011
+ }
1012
+ }
1013
  }
1014
 
1015
  /* Useful to ensure modals rise above the grey 'miasma' */
1016
  .ui-front {
1017
  z-index: 1000000;
1018
  }
1019
+
1020
+ /* Select2 Specific rule */
1021
+ .select2-container .select2-choice abbr {
1022
+ top: 6px;
1023
+ }
1024
+
1025
+ .wp-list-table.plugins {
1026
+ .column-description {
1027
+ .update-message {
1028
+ color: #d54e21;
1029
+ }
1030
+ }
1031
+ }
1032
+
1033
+ .api-check {
1034
+ padding: 1em;
1035
+
1036
+ & + .notice-dismiss {
1037
+ &:hover {
1038
+ &:before {
1039
+ color: #fff;
1040
+ }
1041
+ }
1042
+ }
1043
+
1044
+ &:before,
1045
+ &:after {
1046
+ content: '';
1047
+ display: table;
1048
+ }
1049
+
1050
+ &:after {
1051
+ clear: both;
1052
+ }
1053
+
1054
+ .tribe-spirit-animal {
1055
+ background: #325f81;
1056
+ background: -moz-linear-gradient(45deg, #325f81 0%, #3d87c0 100%);
1057
+ background: -webkit-linear-gradient(45deg, #325f81 0%,#3d87c0 100%);
1058
+ background: linear-gradient(45deg, #325f81 0%,#3d87c0 100%);
1059
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#325f81', endColorstr='#3d87c0',GradientType=1 );
1060
+ border-left: 4px solid #eee;
1061
+ bottom: 0;
1062
+ display: none;
1063
+ padding: 1rem;
1064
+ position: absolute;
1065
+ right: 0;
1066
+ top: 0;
1067
+ width: 180px;
1068
+
1069
+ &:before {
1070
+ content: '';
1071
+ display: inline-block;
1072
+ height: 100%;
1073
+ width: 1%;
1074
+ vertical-align: middle;
1075
+ }
1076
+
1077
+ img {
1078
+ display: inline-block;
1079
+ max-height: 100%;
1080
+ max-width: 96%;
1081
+ vertical-align: middle;
1082
+ }
1083
+
1084
+ @media screen and (min-width: 500px) {
1085
+ display: block;
1086
+ }
1087
+ }
1088
+
1089
+ .notice-content {
1090
+
1091
+ @media screen and (min-width: 500px) {
1092
+ margin-right: 180px;
1093
+ }
1094
+ }
1095
+
1096
+ p {
1097
+ line-height: 1.7;
1098
+ margin-bottom: 1em;
1099
+ }
1100
+
1101
+ a {
1102
+ text-decoration: none;
1103
+
1104
+ &:hover {
1105
+ text-decoration: underline;
1106
+ }
1107
+ }
1108
+ .plugin-list {
1109
+ display: inline;
1110
+ font-weight: 600;
1111
+ margin: 0;
1112
+ padding: 0;
1113
+
1114
+ span.plugin-invalid {
1115
+
1116
+ &:after {
1117
+ content: ', ';
1118
+ }
1119
+
1120
+ &:last-of-type {
1121
+
1122
+ &:after {
1123
+ content: '';
1124
+ }
1125
+ }
1126
+ }
1127
+ }
1128
+ }
common/tribe-common.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  /*
3
  Description: An event settings framework for managing shared options
4
- Version: 4.2.7
5
  Author: Modern Tribe, Inc.
6
  Author URI: http://m.tri.be/1x
7
  Text Domain: tribe-common
1
  <?php
2
  /*
3
  Description: An event settings framework for managing shared options
4
+ Version: 4.3
5
  Author: Modern Tribe, Inc.
6
  Author URI: http://m.tri.be/1x
7
  Text Domain: tribe-common
common/vendor/clipboard/clipboard.js ADDED
@@ -0,0 +1,742 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * clipboard.js v1.5.12
3
+ * https://zenorocha.github.io/clipboard.js
4
+ *
5
+ * Licensed MIT © Zeno Rocha
6
+ */
7
+ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Clipboard = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
8
+ var matches = require('matches-selector')
9
+
10
+ module.exports = function (element, selector, checkYoSelf) {
11
+ var parent = checkYoSelf ? element : element.parentNode
12
+
13
+ while (parent && parent !== document) {
14
+ if (matches(parent, selector)) return parent;
15
+ parent = parent.parentNode
16
+ }
17
+ }
18
+
19
+ },{"matches-selector":5}],2:[function(require,module,exports){
20
+ var closest = require('closest');
21
+
22
+ /**
23
+ * Delegates event to a selector.
24
+ *
25
+ * @param {Element} element
26
+ * @param {String} selector
27
+ * @param {String} type
28
+ * @param {Function} callback
29
+ * @param {Boolean} useCapture
30
+ * @return {Object}
31
+ */
32
+ function delegate(element, selector, type, callback, useCapture) {
33
+ var listenerFn = listener.apply(this, arguments);
34
+
35
+ element.addEventListener(type, listenerFn, useCapture);
36
+
37
+ return {
38
+ destroy: function() {
39
+ element.removeEventListener(type, listenerFn, useCapture);
40
+ }
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Finds closest match and invokes callback.
46
+ *
47
+ * @param {Element} element
48
+ * @param {String} selector
49
+ * @param {String} type
50
+ * @param {Function} callback
51
+ * @return {Function}
52
+ */
53
+ function listener(element, selector, type, callback) {
54
+ return function(e) {
55
+ e.delegateTarget = closest(e.target, selector, true);
56
+
57
+ if (e.delegateTarget) {
58
+ callback.call(element, e);
59
+ }
60
+ }
61
+ }
62
+
63
+ module.exports = delegate;
64
+
65
+ },{"closest":1}],3:[function(require,module,exports){
66
+ /**
67
+ * Check if argument is a HTML element.
68
+ *
69
+ * @param {Object} value
70
+ * @return {Boolean}
71
+ */
72
+ exports.node = function(value) {
73
+ return value !== undefined
74
+ && value instanceof HTMLElement
75
+ && value.nodeType === 1;
76
+ };
77
+
78
+ /**
79
+ * Check if argument is a list of HTML elements.
80
+ *
81
+ * @param {Object} value
82
+ * @return {Boolean}
83
+ */
84
+ exports.nodeList = function(value) {
85
+ var type = Object.prototype.toString.call(value);
86
+
87
+ return value !== undefined
88
+ && (type === '[object NodeList]' || type === '[object HTMLCollection]')
89
+ && ('length' in value)
90
+ && (value.length === 0 || exports.node(value[0]));
91
+ };
92
+
93
+ /**
94
+ * Check if argument is a string.
95
+ *
96
+ * @param {Object} value
97
+ * @return {Boolean}
98
+ */
99
+ exports.string = function(value) {
100
+ return typeof value === 'string'
101
+ || value instanceof String;
102
+ };
103
+
104
+ /**
105
+ * Check if argument is a function.
106
+ *
107
+ * @param {Object} value
108
+ * @return {Boolean}
109
+ */
110
+ exports.fn = function(value) {
111
+ var type = Object.prototype.toString.call(value);
112
+
113
+ return type === '[object Function]';
114
+ };
115
+
116
+ },{}],4:[function(require,module,exports){
117
+ var is = require('./is');
118
+ var delegate = require('delegate');
119
+
120
+ /**
121
+ * Validates all params and calls the right
122
+ * listener function based on its target type.
123
+ *
124
+ * @param {String|HTMLElement|HTMLCollection|NodeList} target
125
+ * @param {String} type
126
+ * @param {Function} callback
127
+ * @return {Object}
128
+ */
129
+ function listen(target, type, callback) {
130
+ if (!target && !type && !callback) {
131
+ throw new Error('Missing required arguments');
132
+ }
133
+
134
+ if (!is.string(type)) {
135
+ throw new TypeError('Second argument must be a String');
136
+ }
137
+
138
+ if (!is.fn(callback)) {
139
+ throw new TypeError('Third argument must be a Function');
140
+ }
141
+
142
+ if (is.node(target)) {
143
+ return listenNode(target, type, callback);
144
+ }
145
+ else if (is.nodeList(target)) {
146
+ return listenNodeList(target, type, callback);
147
+ }
148
+ else if (is.string(target)) {
149
+ return listenSelector(target, type, callback);
150
+ }
151
+ else {
152
+ throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Adds an event listener to a HTML element
158
+ * and returns a remove listener function.
159
+ *
160
+ * @param {HTMLElement} node
161
+ * @param {String} type
162
+ * @param {Function} callback
163
+ * @return {Object}
164
+ */
165
+ function listenNode(node, type, callback) {
166
+ node.addEventListener(type, callback);
167
+
168
+ return {
169
+ destroy: function() {
170
+ node.removeEventListener(type, callback);
171
+ }
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Add an event listener to a list of HTML elements
177
+ * and returns a remove listener function.
178
+ *
179
+ * @param {NodeList|HTMLCollection} nodeList
180
+ * @param {String} type
181
+ * @param {Function} callback
182
+ * @return {Object}
183
+ */
184
+ function listenNodeList(nodeList, type, callback) {
185
+ Array.prototype.forEach.call(nodeList, function(node) {
186
+ node.addEventListener(type, callback);
187
+ });
188
+
189
+ return {
190
+ destroy: function() {
191
+ Array.prototype.forEach.call(nodeList, function(node) {
192
+ node.removeEventListener(type, callback);
193
+ });
194
+ }
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Add an event listener to a selector
200
+ * and returns a remove listener function.
201
+ *
202
+ * @param {String} selector
203
+ * @param {String} type
204
+ * @param {Function} callback
205
+ * @return {Object}
206
+ */
207
+ function listenSelector(selector, type, callback) {
208
+ return delegate(document.body, selector, type, callback);
209
+ }
210
+
211
+ module.exports = listen;
212
+
213
+ },{"./is":3,"delegate":2}],5:[function(require,module,exports){
214
+
215
+ /**
216
+ * Element prototype.
217
+ */
218
+
219
+ var proto = Element.prototype;
220
+
221
+ /**
222
+ * Vendor function.
223
+ */
224
+
225
+ var vendor = proto.matchesSelector
226
+ || proto.webkitMatchesSelector
227
+ || proto.mozMatchesSelector
228
+ || proto.msMatchesSelector
229
+ || proto.oMatchesSelector;
230
+
231
+ /**
232
+ * Expose `match()`.
233
+ */
234
+
235
+ module.exports = match;
236
+
237
+ /**
238
+ * Match `el` to `selector`.
239
+ *
240
+ * @param {Element} el
241
+ * @param {String} selector
242
+ * @return {Boolean}
243
+ * @api public
244
+ */
245
+
246
+ function match(el, selector) {
247
+ if (vendor) return vendor.call(el, selector);
248
+ var nodes = el.parentNode.querySelectorAll(selector);
249
+ for (var i = 0; i < nodes.length; ++i) {
250
+ if (nodes[i] == el) return true;
251
+ }
252
+ return false;
253
+ }
254
+ },{}],6:[function(require,module,exports){
255
+ function select(element) {
256
+ var selectedText;
257
+
258
+ if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
259
+ element.focus();
260
+ element.setSelectionRange(0, element.value.length);
261
+
262
+ selectedText = element.value;
263
+ }
264
+ else {
265
+ if (element.hasAttribute('contenteditable')) {
266
+ element.focus();
267
+ }
268
+
269
+ var selection = window.getSelection();
270
+ var range = document.createRange();
271
+
272
+ range.selectNodeContents(element);
273
+ selection.removeAllRanges();
274
+ selection.addRange(range);
275
+
276
+ selectedText = selection.toString();
277
+ }
278
+
279
+ return selectedText;
280
+ }
281
+
282
+ module.exports = select;
283
+
284
+ },{}],7:[function(require,module,exports){
285
+ function E () {
286
+ // Keep this empty so it's easier to inherit from
287
+ // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
288
+ }
289
+
290
+ E.prototype = {
291
+ on: function (name, callback, ctx) {
292
+ var e = this.e || (this.e = {});
293
+
294
+ (e[name] || (e[name] = [])).push({
295
+ fn: callback,
296
+ ctx: ctx
297
+ });
298
+
299
+ return this;
300
+ },
301
+
302
+ once: function (name, callback, ctx) {
303
+ var self = this;
304
+ function listener () {
305
+ self.off(name, listener);
306
+ callback.apply(ctx, arguments);
307
+ };
308
+
309
+ listener._ = callback
310
+ return this.on(name, listener, ctx);
311
+ },
312
+
313
+ emit: function (name) {
314
+ var data = [].slice.call(arguments, 1);
315
+ var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
316
+ var i = 0;
317
+ var len = evtArr.length;
318
+
319
+ for (i; i < len; i++) {
320
+ evtArr[i].fn.apply(evtArr[i].ctx, data);
321
+ }
322
+
323
+ return this;
324
+ },
325
+
326
+ off: function (name, callback) {
327
+ var e = this.e || (this.e = {});
328
+ var evts = e[name];
329
+ var liveEvents = [];
330
+
331
+ if (evts && callback) {
332
+ for (var i = 0, len = evts.length; i < len; i++) {
333
+ if (evts[i].fn !== callback && evts[i].fn._ !== callback)
334
+ liveEvents.push(evts[i]);
335
+ }
336
+ }
337
+
338
+ // Remove event from queue to prevent memory leak
339
+ // Suggested by https://github.com/lazd
340
+ // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910
341
+
342
+ (liveEvents.length)
343
+ ? e[name] = liveEvents
344
+ : delete e[name];
345
+
346
+ return this;
347
+ }
348
+ };
349
+
350
+ module.exports = E;
351
+
352
+ },{}],8:[function(require,module,exports){
353
+ (function (global, factory) {
354
+ if (typeof define === "function" && define.amd) {
355
+ define(['module', 'select'], factory);
356
+ } else if (typeof exports !== "undefined") {
357
+ factory(module, require('select'));
358
+ } else {
359
+ var mod = {
360
+ exports: {}
361
+ };
362
+ factory(mod, global.select);
363
+ global.clipboardAction = mod.exports;
364
+ }
365
+ })(this, function (module, _select) {
366
+ 'use strict';
367
+
368
+ var _select2 = _interopRequireDefault(_select);
369
+
370
+ function _interopRequireDefault(obj) {
371
+ return obj && obj.__esModule ? obj : {
372
+ default: obj
373
+ };
374
+ }
375
+
376
+ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
377
+ return typeof obj;
378
+ } : function (obj) {
379
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj;
380
+ };
381
+
382
+ function _classCallCheck(instance, Constructor) {
383
+ if (!(instance instanceof Constructor)) {
384
+ throw new TypeError("Cannot call a class as a function");
385
+ }
386
+ }
387
+
388
+ var _createClass = function () {
389
+ function defineProperties(target, props) {
390
+ for (var i = 0; i < props.length; i++) {
391
+ var descriptor = props[i];
392
+ descriptor.enumerable = descriptor.enumerable || false;
393
+ descriptor.configurable = true;
394
+ if ("value" in descriptor) descriptor.writable = true;
395
+ Object.defineProperty(target, descriptor.key, descriptor);
396
+ }
397
+ }
398
+
399
+ return function (Constructor, protoProps, staticProps) {
400
+ if (protoProps) defineProperties(Constructor.prototype, protoProps);
401
+ if (staticProps) defineProperties(Constructor, staticProps);
402
+ return Constructor;
403
+ };
404
+ }();
405
+
406
+ var ClipboardAction = function () {
407
+ /**
408
+ * @param {Object} options
409
+ */
410
+
411
+ function ClipboardAction(options) {
412
+ _classCallCheck(this, ClipboardAction);
413
+
414
+ this.resolveOptions(options);
415
+ this.initSelection();
416
+ }
417
+
418
+ /**
419
+ * Defines base properties passed from constructor.
420
+ * @param {Object} options
421
+ */
422
+
423
+
424
+ ClipboardAction.prototype.resolveOptions = function resolveOptions() {
425
+ var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
426
+
427
+ this.action = options.action;
428
+ this.emitter = options.emitter;
429
+ this.target = options.target;
430
+ this.text = options.text;
431
+ this.trigger = options.trigger;
432
+
433
+ this.selectedText = '';
434
+ };
435
+
436
+ ClipboardAction.prototype.initSelection = function initSelection() {
437
+ if (this.text) {
438
+ this.selectFake();
439
+ } else if (this.target) {
440
+ this.selectTarget();
441
+ }
442
+ };
443
+
444
+ ClipboardAction.prototype.selectFake = function selectFake() {
445
+ var _this = this;
446
+
447
+ var isRTL = document.documentElement.getAttribute('dir') == 'rtl';
448
+
449
+ this.removeFake();
450
+
451
+ this.fakeHandlerCallback = function () {
452
+ return _this.removeFake();
453
+ };
454
+ this.fakeHandler = document.body.addEventListener('click', this.fakeHandlerCallback) || true;
455
+
456
+ this.fakeElem = document.createElement('textarea');
457
+ // Prevent zooming on iOS
458
+ this.fakeElem.style.fontSize = '12pt';
459
+ // Reset box model
460
+ this.fakeElem.style.border = '0';
461
+ this.fakeElem.style.padding = '0';
462
+ this.fakeElem.style.margin = '0';
463
+ // Move element out of screen horizontally
464
+ this.fakeElem.style.position = 'absolute';
465
+ this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px';
466
+ // Move element to the same position vertically
467
+ this.fakeElem.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px';
468
+ this.fakeElem.setAttribute('readonly', '');
469
+ this.fakeElem.value = this.text;
470
+
471
+ document.body.appendChild(this.fakeElem);
472
+
473
+ this.selectedText = (0, _select2.default)(this.fakeElem);
474
+ this.copyText();
475
+ };
476
+
477
+ ClipboardAction.prototype.removeFake = function removeFake() {
478
+ if (this.fakeHandler) {
479
+ document.body.removeEventListener('click', this.fakeHandlerCallback);
480
+ this.fakeHandler = null;
481
+ this.fakeHandlerCallback = null;
482
+ }
483
+
484
+ if (this.fakeElem) {
485
+ document.body.removeChild(this.fakeElem);
486
+ this.fakeElem = null;
487
+ }
488
+ };
489
+
490
+ ClipboardAction.prototype.selectTarget = function selectTarget() {
491
+ this.selectedText = (0, _select2.default)(this.target);
492
+ this.copyText();
493
+ };
494
+
495
+ ClipboardAction.prototype.copyText = function copyText() {
496
+ var succeeded = undefined;
497
+
498
+ try {
499
+ succeeded = document.execCommand(this.action);
500
+ } catch (err) {
501
+ succeeded = false;
502
+ }
503
+
504
+ this.handleResult(succeeded);
505
+ };
506
+
507
+ ClipboardAction.prototype.handleResult = function handleResult(succeeded) {
508
+ if (succeeded) {
509
+ this.emitter.emit('success', {
510
+ action: this.action,
511
+ text: this.selectedText,
512
+ trigger: this.trigger,
513
+ clearSelection: this.clearSelection.bind(this)
514
+ });
515
+ } else {
516
+ this.emitter.emit('error', {
517
+ action: this.action,
518
+ trigger: this.trigger,
519
+ clearSelection: this.clearSelection.bind(this)
520
+ });
521
+ }
522
+ };
523
+
524
+ ClipboardAction.prototype.clearSelection = function clearSelection() {
525
+ if (this.target) {
526
+ this.target.blur();
527
+ }
528
+
529
+ window.getSelection().removeAllRanges();
530
+ };
531
+
532
+ ClipboardAction.prototype.destroy = function destroy() {
533
+ this.removeFake();
534
+ };
535
+
536
+ _createClass(ClipboardAction, [{
537
+ key: 'action',
538
+ set: function set() {
539
+ var action = arguments.length <= 0 || arguments[0] === undefined ? 'copy' : arguments[0];
540
+
541
+ this._action = action;
542
+
543
+ if (this._action !== 'copy' && this._action !== 'cut') {
544
+ throw new Error('Invalid "action" value, use either "copy" or "cut"');
545
+ }
546
+ },
547
+ get: function get() {
548
+ return this._action;
549
+ }
550
+ }, {
551
+ key: 'target',
552
+ set: function set(target) {
553
+ if (target !== undefined) {
554
+ if (target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && target.nodeType === 1) {
555
+ if (this.action === 'copy' && target.hasAttribute('disabled')) {
556
+ throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');
557
+ }
558
+
559
+ if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {
560
+ throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');
561
+ }
562
+
563
+ this._target = target;
564
+ } else {
565
+ throw new Error('Invalid "target" value, use a valid Element');
566
+ }
567
+ }
568
+ },
569
+ get: function get() {
570
+ return this._target;
571
+ }
572
+ }]);
573
+
574
+ return ClipboardAction;
575
+ }();
576
+
577
+ module.exports = ClipboardAction;
578
+ });
579
+
580
+ },{"select":6}],9:[function(require,module,exports){
581
+ (function (global, factory) {
582
+ if (typeof define === "function" && define.amd) {
583
+ define(['module', './clipboard-action', 'tiny-emitter', 'good-listener'], factory);
584
+ } else if (typeof exports !== "undefined") {
585
+ factory(module, require('./clipboard-action'), require('tiny-emitter'), require('good-listener'));
586
+ } else {
587
+ var mod = {
588
+ exports: {}
589
+ };
590
+ factory(mod, global.clipboardAction, global.tinyEmitter, global.goodListener);
591
+ global.clipboard = mod.exports;
592
+ }
593
+ })(this, function (module, _clipboardAction, _tinyEmitter, _goodListener) {
594
+ 'use strict';
595
+
596
+ var _clipboardAction2 = _interopRequireDefault(_clipboardAction);
597
+
598
+ var _tinyEmitter2 = _interopRequireDefault(_tinyEmitter);
599
+
600
+ var _goodListener2 = _interopRequireDefault(_goodListener);
601
+
602
+ function _interopRequireDefault(obj) {
603
+ return obj && obj.__esModule ? obj : {
604
+ default: obj
605
+ };
606
+ }
607
+
608
+ function _classCallCheck(instance, Constructor) {
609
+ if (!(instance instanceof Constructor)) {
610
+ throw new TypeError("Cannot call a class as a function");
611
+ }
612
+ }
613
+
614
+ function _possibleConstructorReturn(self, call) {
615
+ if (!self) {
616
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
617
+ }
618
+
619
+ return call && (typeof call === "object" || typeof call === "function") ? call : self;
620
+ }
621
+
622
+ function _inherits(subClass, superClass) {
623
+ if (typeof superClass !== "function" && superClass !== null) {
624
+ throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
625
+ }
626
+
627
+ subClass.prototype = Object.create(superClass && superClass.prototype, {
628
+ constructor: {
629
+ value: subClass,
630
+ enumerable: false,
631
+ writable: true,
632
+ configurable: true
633
+ }
634
+ });
635
+ if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
636
+ }
637
+
638
+ var Clipboard = function (_Emitter) {
639
+ _inherits(Clipboard, _Emitter);
640
+
641
+ /**
642
+ * @param {String|HTMLElement|HTMLCollection|NodeList} trigger
643
+ * @param {Object} options
644
+ */
645
+
646
+ function Clipboard(trigger, options) {
647
+ _classCallCheck(this, Clipboard);
648
+
649
+ var _this = _possibleConstructorReturn(this, _Emitter.call(this));
650
+
651
+ _this.resolveOptions(options);
652
+ _this.listenClick(trigger);
653
+ return _this;
654
+ }
655
+
656
+ /**
657
+ * Defines if attributes would be resolved using internal setter functions
658
+ * or custom functions that were passed in the constructor.
659
+ * @param {Object} options
660
+ */
661
+
662
+
663
+ Clipboard.prototype.resolveOptions = function resolveOptions() {
664
+ var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
665
+
666
+ this.action = typeof options.action === 'function' ? options.action : this.defaultAction;
667
+ this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;
668
+ this.text = typeof options.text === 'function' ? options.text : this.defaultText;
669
+ };
670
+
671
+ Clipboard.prototype.listenClick = function listenClick(trigger) {
672
+ var _this2 = this;
673
+
674
+ this.listener = (0, _goodListener2.default)(trigger, 'click', function (e) {
675
+ return _this2.onClick(e);
676
+ });
677
+ };
678
+
679
+ Clipboard.prototype.onClick = function onClick(e) {
680
+ var trigger = e.delegateTarget || e.currentTarget;
681
+
682
+ if (this.clipboardAction) {
683
+ this.clipboardAction = null;
684
+ }
685
+
686
+ this.clipboardAction = new _clipboardAction2.default({
687
+ action: this.action(trigger),
688
+ target: this.target(trigger),
689
+ text: this.text(trigger),
690
+ trigger: trigger,
691
+ emitter: this
692
+ });
693
+ };
694
+
695
+ Clipboard.prototype.defaultAction = function defaultAction(trigger) {
696
+ return getAttributeValue('action', trigger);
697
+ };
698
+
699
+ Clipboard.prototype.defaultTarget = function defaultTarget(trigger) {
700
+ var selector = getAttributeValue('target', trigger);
701
+
702
+ if (selector) {
703
+ return document.querySelector(selector);
704
+ }
705
+ };
706
+
707
+ Clipboard.prototype.defaultText = function defaultText(trigger) {
708
+ return getAttributeValue('text', trigger);
709
+ };
710
+
711
+ Clipboard.prototype.destroy = function destroy() {
712
+ this.listener.destroy();
713
+
714
+ if (this.clipboardAction) {
715
+ this.clipboardAction.destroy();
716
+ this.clipboardAction = null;
717
+ }
718
+ };
719
+
720
+ return Clipboard;
721
+ }(_tinyEmitter2.default);
722
+
723
+ /**
724
+ * Helper function to retrieve attribute value.
725
+ * @param {String} suffix
726
+ * @param {Element} element
727
+ */
728
+ function getAttributeValue(suffix, element) {
729
+ var attribute = 'data-clipboard-' + suffix;
730
+
731
+ if (!element.hasAttribute(attribute)) {
732
+ return;
733
+ }
734
+
735
+ return element.getAttribute(attribute);
736
+ }
737
+
738
+ module.exports = Clipboard;
739
+ });
740
+
741
+ },{"./clipboard-action":8,"good-listener":4,"tiny-emitter":7}]},{},[9])(9)
742
+ });
common/vendor/clipboard/clipboard.min.js ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ /*!
2
+ * clipboard.js v1.5.12
3
+ * https://zenorocha.github.io/clipboard.js
4
+ *
5
+ * Licensed MIT � Zeno Rocha
6
+ */
7
+ !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.Clipboard=t()}}(function(){var t,e,n;return function t(e,n,o){function i(a,c){if(!n[a]){if(!e[a]){var s="function"==typeof require&&require;if(!c&&s)return s(a,!0);if(r)return r(a,!0);var l=new Error("Cannot find module '"+a+"'");throw l.code="MODULE_NOT_FOUND",l}var u=n[a]={exports:{}};e[a][0].call(u.exports,function(t){var n=e[a][1][t];return i(n?n:t)},u,u.exports,t,e,n,o)}return n[a].exports}for(var r="function"==typeof require&&require,a=0;a<o.length;a++)i(o[a]);return i}({1:[function(t,e,n){var o=t("matches-selector");e.exports=function(t,e,n){for(var i=n?t:t.parentNode;i&&i!==document;){if(o(i,e))return i;i=i.parentNode}}},{"matches-selector":5}],2:[function(t,e,n){function o(t,e,n,o,r){var a=i.apply(this,arguments);return t.addEventListener(n,a,r),{destroy:function(){t.removeEventListener(n,a,r)}}}function i(t,e,n,o){return function(n){n.delegateTarget=r(n.target,e,!0),n.delegateTarget&&o.call(t,n)}}var r=t("closest");e.exports=o},{closest:1}],3:[function(t,e,n){n.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},n.nodeList=function(t){var e=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===e||"[object HTMLCollection]"===e)&&"length"in t&&(0===t.length||n.node(t[0]))},n.string=function(t){return"string"==typeof t||t instanceof String},n.fn=function(t){var e=Object.prototype.toString.call(t);return"[object Function]"===e}},{}],4:[function(t,e,n){function o(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!c.string(e))throw new TypeError("Second argument must be a String");if(!c.fn(n))throw new TypeError("Third argument must be a Function");if(c.node(t))return i(t,e,n);if(c.nodeList(t))return r(t,e,n);if(c.string(t))return a(t,e,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function i(t,e,n){return t.addEventListener(e,n),{destroy:function(){t.removeEventListener(e,n)}}}function r(t,e,n){return Array.prototype.forEach.call(t,function(t){t.addEventListener(e,n)}),{destroy:function(){Array.prototype.forEach.call(t,function(t){t.removeEventListener(e,n)})}}}function a(t,e,n){return s(document.body,t,e,n)}var c=t("./is"),s=t("delegate");e.exports=o},{"./is":3,delegate:2}],5:[function(t,e,n){function o(t,e){if(r)return r.call(t,e);for(var n=t.parentNode.querySelectorAll(e),o=0;o<n.length;++o)if(n[o]==t)return!0;return!1}var i=Element.prototype,r=i.matchesSelector||i.webkitMatchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector;e.exports=o},{}],6:[function(t,e,n){function o(t){var e;if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName)t.focus(),t.setSelectionRange(0,t.value.length),e=t.value;else{t.hasAttribute("contenteditable")&&t.focus();var n=window.getSelection(),o=document.createRange();o.selectNodeContents(t),n.removeAllRanges(),n.addRange(o),e=n.toString()}return e}e.exports=o},{}],7:[function(t,e,n){function o(){}o.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){function o(){i.off(t,o),e.apply(n,arguments)}var i=this;return o._=e,this.on(t,o,n)},emit:function(t){var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,i=n.length;for(o;i>o;o++)n[o].fn.apply(n[o].ctx,e);return this},off:function(t,e){var n=this.e||(this.e={}),o=n[t],i=[];if(o&&e)for(var r=0,a=o.length;a>r;r++)o[r].fn!==e&&o[r].fn._!==e&&i.push(o[r]);return i.length?n[t]=i:delete n[t],this}},e.exports=o},{}],8:[function(e,n,o){!function(i,r){if("function"==typeof t&&t.amd)t(["module","select"],r);else if("undefined"!=typeof o)r(n,e("select"));else{var a={exports:{}};r(a,i.select),i.clipboardAction=a.exports}}(this,function(t,e){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var i=n(e),r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol?"symbol":typeof t},a=function(){function t(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}return function(e,n,o){return n&&t(e.prototype,n),o&&t(e,o),e}}(),c=function(){function t(e){o(this,t),this.resolveOptions(e),this.initSelection()}return t.prototype.resolveOptions=function t(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];this.action=e.action,this.emitter=e.emitter,this.target=e.target,this.text=e.text,this.trigger=e.trigger,this.selectedText=""},t.prototype.initSelection=function t(){this.text?this.selectFake():this.target&&this.selectTarget()},t.prototype.selectFake=function t(){var e=this,n="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return e.removeFake()},this.fakeHandler=document.body.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[n?"right":"left"]="-9999px",this.fakeElem.style.top=(window.pageYOffset||document.documentElement.scrollTop)+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,document.body.appendChild(this.fakeElem),this.selectedText=(0,i.default)(this.fakeElem),this.copyText()},t.prototype.removeFake=function t(){this.fakeHandler&&(document.body.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(document.body.removeChild(this.fakeElem),this.fakeElem=null)},t.prototype.selectTarget=function t(){this.selectedText=(0,i.default)(this.target),this.copyText()},t.prototype.copyText=function t(){var e=void 0;try{e=document.execCommand(this.action)}catch(n){e=!1}this.handleResult(e)},t.prototype.handleResult=function t(e){e?this.emitter.emit("success",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)}):this.emitter.emit("error",{action:this.action,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})},t.prototype.clearSelection=function t(){this.target&&this.target.blur(),window.getSelection().removeAllRanges()},t.prototype.destroy=function t(){this.removeFake()},a(t,[{key:"action",set:function t(){var e=arguments.length<=0||void 0===arguments[0]?"copy":arguments[0];if(this._action=e,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function t(){return this._action}},{key:"target",set:function t(e){if(void 0!==e){if(!e||"object"!==("undefined"==typeof e?"undefined":r(e))||1!==e.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&e.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(e.hasAttribute("readonly")||e.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=e}},get:function t(){return this._target}}]),t}();t.exports=c})},{select:6}],9:[function(e,n,o){!function(i,r){if("function"==typeof t&&t.amd)t(["module","./clipboard-action","tiny-emitter","good-listener"],r);else if("undefined"!=typeof o)r(n,e("./clipboard-action"),e("tiny-emitter"),e("good-listener"));else{var a={exports:{}};r(a,i.clipboardAction,i.tinyEmitter,i.goodListener),i.clipboard=a.exports}}(this,function(t,e,n,o){"use strict";function i(t){return t&&t.__esModule?t:{"default":t}}function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function a(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function c(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function s(t,e){var n="data-clipboard-"+t;if(e.hasAttribute(n))return e.getAttribute(n)}var l=i(e),u=i(n),f=i(o),d=function(t){function e(n,o){r(this,e);var i=a(this,t.call(this));return i.resolveOptions(o),i.listenClick(n),i}return c(e,t),e.prototype.resolveOptions=function t(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];this.action="function"==typeof e.action?e.action:this.defaultAction,this.target="function"==typeof e.target?e.target:this.defaultTarget,this.text="function"==typeof e.text?e.text:this.defaultText},e.prototype.listenClick=function t(e){var n=this;this.listener=(0,f.default)(e,"click",function(t){return n.onClick(t)})},e.prototype.onClick=function t(e){var n=e.delegateTarget||e.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new l.default({action:this.action(n),target:this.target(n),text:this.text(n),trigger:n,emitter:this})},e.prototype.defaultAction=function t(e){return s("action",e)},e.prototype.defaultTarget=function t(e){var n=s("target",e);return n?document.querySelector(n):void 0},e.prototype.defaultText=function t(e){return s("text",e)},e.prototype.destroy=function t(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)},e}(u.default);t.exports=d})},{"./clipboard-action":8,"good-listener":4,"tiny-emitter":7}]},{},[9])(9)});
common/vendor/datatables/extensions/FixedHeader/css/fixedHeader.dataTables.css ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ table.fixedHeader-floating {
2
+ position: fixed !important;
3
+ background-color: white;
4
+ }
5
+
6
+ table.fixedHeader-floating.no-footer {
7
+ border-bottom-width: 0;
8
+ }
9
+
10
+ table.fixedHeader-locked {
11
+ position: absolute !important;
12
+ background-color: white;
13
+ }
14
+
15
+ @media print {
16
+ table.fixedHeader-floating {
17
+ display: none;
18
+ }
19
+ }
common/vendor/datatables/extensions/FixedHeader/css/fixedHeader.dataTables.min.css ADDED
@@ -0,0 +1 @@
 
1
+ table.fixedHeader-floating{position:fixed !important;background-color:white}table.fixedHeader-floating.no-footer{border-bottom-width:0}table.fixedHeader-locked{position:absolute !important;background-color:white}@media print{table.fixedHeader-floating{display:none}}
common/vendor/datatables/extensions/FixedHeader/js/dataTables.fixedHeader.js ADDED
@@ -0,0 +1,672 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*! FixedHeader 3.1.2
2
+ * ©2009-2016 SpryMedia Ltd - datatables.net/license
3
+ */
4
+
5
+ /**
6
+ * @summary FixedHeader
7
+ * @description Fix a table's header or footer, so it is always visible while
8
+ * scrolling
9
+ * @version 3.1.2
10
+ * @file dataTables.fixedHeader.js
11
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
12
+ * @contact www.sprymedia.co.uk/contact
13
+ * @copyright Copyright 2009-2016 SpryMedia Ltd.
14
+ *
15
+ * This source file is free software, available under the following license:
16
+ * MIT license - http://datatables.net/license/mit
17
+ *
18
+ * This source file is distributed in the hope that it will be useful, but
19
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
21
+ *
22
+ * For details please refer to: http://www.datatables.net
23
+ */
24
+
25
+ (function( factory ){
26
+ if ( typeof define === 'function' && define.amd ) {
27
+ // AMD
28
+ define( ['jquery', 'datatables.net'], function ( $ ) {
29
+ return factory( $, window, document );
30
+ } );
31
+ }
32
+ else if ( typeof exports === 'object' ) {
33
+ // CommonJS
34
+ module.exports = function (root, $) {
35
+ if ( ! root ) {
36
+ root = window;
37
+ }
38
+
39
+ if ( ! $ || ! $.fn.dataTable ) {
40
+ $ = require('datatables.net')(root, $).$;
41
+ }
42
+
43
+ return factory( $, root, root.document );
44
+ };
45
+ }
46
+ else {
47
+ // Browser
48
+ factory( jQuery, window, document );
49
+ }
50
+ }(function( $, window, document, undefined ) {
51
+ 'use strict';
52
+ var DataTable = $.fn.dataTable;
53
+
54
+
55
+ var _instCounter = 0;
56
+
57
+ var FixedHeader = function ( dt, config ) {
58
+ // Sanity check - you just know it will happen
59
+ if ( ! (this instanceof FixedHeader) ) {
60
+ throw "FixedHeader must be initialised with the 'new' keyword.";
61
+ }
62
+
63
+ // Allow a boolean true for defaults
64
+ if ( config === true ) {
65
+ config = {};
66
+ }
67
+
68
+ dt = new DataTable.Api( dt );
69
+
70
+ this.c = $.extend( true, {}, FixedHeader.defaults, config );
71
+
72
+ this.s = {
73
+ dt: dt,
74
+ position: {
75
+ theadTop: 0,
76
+ tbodyTop: 0,
77
+ tfootTop: 0,
78
+ tfootBottom: 0,
79
+ width: 0,
80
+ left: 0,
81
+ tfootHeight: 0,
82
+ theadHeight: 0,
83
+ windowHeight: $(window).height(),
84
+ visible: true
85
+ },
86
+ headerMode: null,
87
+ footerMode: null,
88
+ autoWidth: dt.settings()[0].oFeatures.bAutoWidth,
89
+ namespace: '.dtfc'+(_instCounter++),
90
+ scrollLeft: {
91
+ header: -1,
92
+ footer: -1
93
+ },
94
+ enable: true
95
+ };
96
+
97
+ this.dom = {
98
+ floatingHeader: null,
99
+ thead: $(dt.table().header()),
100
+ tbody: $(dt.table().body()),
101
+ tfoot: $(dt.table().footer()),
102
+ header: {
103
+ host: null,
104
+ floating: null,
105
+ placeholder: null
106
+ },
107
+ footer: {
108
+ host: null,
109
+ floating: null,
110
+ placeholder: null
111
+ }
112
+ };
113
+
114
+ this.dom.header.host = this.dom.thead.parent();
115
+ this.dom.footer.host = this.dom.tfoot.parent();
116
+
117
+ var dtSettings = dt.settings()[0];
118
+ if ( dtSettings._fixedHeader ) {
119
+ throw "FixedHeader already initialised on table "+dtSettings.nTable.id;
120
+ }
121
+
122
+ dtSettings._fixedHeader = this;
123
+
124
+ this._constructor();
125
+ };
126
+
127
+
128
+ /*
129
+ * Variable: FixedHeader
130
+ * Purpose: Prototype for FixedHeader
131
+ * Scope: global
132
+ */
133
+ $.extend( FixedHeader.prototype, {
134
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
135
+ * API methods
136
+ */
137
+
138
+ /**
139
+ * Enable / disable the fixed elements
140
+ *
141
+ * @param {boolean} enable `true` to enable, `false` to disable
142
+ */
143
+ enable: function ( enable )
144
+ {
145
+ this.s.enable = enable;
146
+
147
+ if ( this.c.header ) {
148
+ this._modeChange( 'in-place', 'header', true );
149
+ }
150
+
151
+ if ( this.c.footer && this.dom.tfoot.length ) {
152
+ this._modeChange( 'in-place', 'footer', true );
153
+ }
154
+
155
+ this.update();
156
+ },
157
+
158
+ /**
159
+ * Set header offset
160
+ *
161
+ * @param {int} new value for headerOffset
162
+ */
163
+ headerOffset: function ( offset )
164
+ {
165
+ if ( offset !== undefined ) {
166
+ this.c.headerOffset = offset;
167
+ this.update();
168
+ }
169
+
170
+ return this.c.headerOffset;
171
+ },
172
+
173
+ /**
174
+ * Set footer offset
175
+ *
176
+ * @param {int} new value for footerOffset
177
+ */
178
+ footerOffset: function ( offset )
179
+ {
180
+ if ( offset !== undefined ) {
181
+ this.c.footerOffset = offset;
182
+ this.update();
183
+ }
184
+
185
+ return this.c.footerOffset;
186
+ },
187
+
188
+
189
+ /**
190
+ * Recalculate the position of the fixed elements and force them into place
191
+ */
192
+ update: function ()
193
+ {
194
+ this._positions();
195
+ this._scroll( true );
196
+ },
197
+
198
+
199
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
200
+ * Constructor
201
+ */
202
+
203
+ /**
204
+ * FixedHeader constructor - adding the required event listeners and
205
+ * simple initialisation
206
+ *
207
+ * @private
208
+ */
209
+ _constructor: function ()
210
+ {
211
+ var that = this;
212
+ var dt = this.s.dt;
213
+
214
+ $(window)
215
+ .on( 'scroll'+this.s.namespace, function () {
216
+ that._scroll();
217
+ } )
218
+ .on( 'resize'+this.s.namespace, function () {
219
+ that.s.position.windowHeight = $(window).height();
220
+ that.update();
221
+ } );
222
+
223
+ var autoHeader = $('.fh-fixedHeader');
224
+ if ( ! this.c.headerOffset && autoHeader.length ) {
225
+ this.c.headerOffset = autoHeader.outerHeight();
226
+ }
227
+
228
+ var autoFooter = $('.fh-fixedFooter');
229
+ if ( ! this.c.footerOffset && autoFooter.length ) {
230
+ this.c.footerOffset = autoFooter.outerHeight();
231
+ }
232
+
233
+ dt.on( 'column-reorder.dt.dtfc column-visibility.dt.dtfc draw.dt.dtfc column-sizing.dt.dtfc', function () {
234
+ that.update();
235
+ } );
236
+
237
+ dt.on( 'destroy.dtfc', function () {
238
+ dt.off( '.dtfc' );
239
+ $(window).off( that.s.namespace );
240
+ } );
241
+
242
+ this._positions();
243
+ this._scroll();
244
+ },
245
+
246
+
247
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
248
+ * Private methods
249
+ */
250
+
251
+ /**
252
+ * Clone a fixed item to act as a place holder for the original element
253
+ * which is moved into a clone of the table element, and moved around the
254
+ * document to give the fixed effect.
255
+ *
256
+ * @param {string} item 'header' or 'footer'
257
+ * @param {boolean} force Force the clone to happen, or allow automatic
258
+ * decision (reuse existing if available)
259
+ * @private
260
+ */
261
+ _clone: function ( item, force )
262
+ {
263
+ var dt = this.s.dt;
264
+ var itemDom = this.dom[ item ];
265
+ var itemElement = item === 'header' ?
266
+ this.dom.thead :
267
+ this.dom.tfoot;
268
+
269
+ if ( ! force && itemDom.floating ) {
270
+ // existing floating element - reuse it
271
+ itemDom.floating.removeClass( 'fixedHeader-floating fixedHeader-locked' );
272
+ }
273
+ else {
274
+ if ( itemDom.floating ) {
275
+ itemDom.placeholder.remove();
276
+ this._unsize( item );
277
+ itemDom.floating.children().detach();
278
+ itemDom.floating.remove();
279
+ }
280
+
281
+ itemDom.floating = $( dt.table().node().cloneNode( false ) )
282
+ .css( 'table-layout', 'fixed' )
283
+ .removeAttr( 'id' )
284
+ .append( itemElement )
285
+ .appendTo( 'body' );
286
+
287
+ // Insert a fake thead/tfoot into the DataTable to stop it jumping around
288
+ itemDom.placeholder = itemElement.clone( false );
289
+ itemDom.host.prepend( itemDom.placeholder );
290
+
291
+ // Clone widths
292
+ this._matchWidths( itemDom.placeholder, itemDom.floating );
293
+ }
294
+ },
295
+
296
+ /**
297
+ * Copy widths from the cells in one element to another. This is required
298
+ * for the footer as the footer in the main table takes its sizes from the
299
+ * header columns. That isn't present in the footer so to have it still
300
+ * align correctly, the sizes need to be copied over. It is also required
301
+ * for the header when auto width is not enabled
302
+ *
303
+ * @param {jQuery} from Copy widths from
304
+ * @param {jQuery} to Copy widths to
305
+ * @private
306
+ */
307
+ _matchWidths: function ( from, to ) {
308
+ var get = function ( name ) {
309
+ return $(name, from)
310
+ .map( function () {
311
+ return $(this).width();
312
+ } ).toArray();
313
+ };
314
+
315
+ var set = function ( name, toWidths ) {
316
+ $(name, to).each( function ( i ) {
317
+ $(this).css( {
318
+ width: toWidths[i],
319
+ minWidth: toWidths[i]
320
+ } );
321
+ } );
322
+ };
323
+
324
+ var thWidths = get( 'th' );
325
+ var tdWidths = get( 'td' );
326
+
327
+ set( 'th', thWidths );
328
+ set( 'td', tdWidths );
329
+ },
330
+
331
+ /**
332
+ * Remove assigned widths from the cells in an element. This is required
333
+ * when inserting the footer back into the main table so the size is defined
334
+ * by the header columns and also when auto width is disabled in the
335
+ * DataTable.
336
+ *
337
+ * @param {string} item The `header` or `footer`
338
+ * @private
339
+ */
340
+ _unsize: function ( item ) {
341
+ var el = this.dom[ item ].floating;
342
+
343
+ if ( el && (item === 'footer' || (item === 'header' && ! this.s.autoWidth)) ) {
344
+ $('th, td', el).css( {
345
+ width: '',
346
+ minWidth: ''
347
+ } );
348
+ }
349
+ else if ( el && item === 'header' ) {
350
+ $('th, td', el).css( 'min-width', '' );
351
+ }
352
+ },
353
+
354
+ /**
355
+ * Reposition the floating elements to take account of horizontal page
356
+ * scroll
357
+ *
358
+ * @param {string} item The `header` or `footer`
359
+ * @param {int} scrollLeft Document scrollLeft
360
+ * @private
361
+ */
362
+ _horizontal: function ( item, scrollLeft )
363
+ {
364
+ var itemDom = this.dom[ item ];
365
+ var position = this.s.position;
366
+ var lastScrollLeft = this.s.scrollLeft;
367
+
368
+ if ( itemDom.floating && lastScrollLeft[ item ] !== scrollLeft ) {
369
+ itemDom.floating.css( 'left', position.left - scrollLeft );
370
+
371
+ lastScrollLeft[ item ] = scrollLeft;
372
+ }
373
+ },
374
+
375
+ /**
376
+ * Change from one display mode to another. Each fixed item can be in one
377
+ * of:
378
+ *
379
+ * * `in-place` - In the main DataTable
380
+ * * `in` - Floating over the DataTable
381
+ * * `below` - (Header only) Fixed to the bottom of the table body
382
+ * * `above` - (Footer only) Fixed to the top of the table body
383
+ *
384
+ * @param {string} mode Mode that the item should be shown in
385
+ * @param {string} item 'header' or 'footer'
386
+ * @param {boolean} forceChange Force a redraw of the mode, even if already
387
+ * in that mode.
388
+ * @private
389
+ */
390
+ _modeChange: function ( mode, item, forceChange )
391
+ {
392
+ var dt = this.s.dt;
393
+ var itemDom = this.dom[ item ];
394
+ var position = this.s.position;
395
+
396
+ // Record focus. Browser's will cause input elements to loose focus if
397
+ // they are inserted else where in the doc
398
+ var tablePart = this.dom[ item==='footer' ? 'tfoot' : 'thead' ];
399
+ var focus = $.contains( tablePart[0], document.activeElement ) ?
400
+ document.activeElement :
401
+ null;
402
+
403
+ if ( mode === 'in-place' ) {
404
+ // Insert the header back into the table's real header
405
+ if ( itemDom.placeholder ) {
406
+ itemDom.placeholder.remove();
407
+ itemDom.placeholder = null;
408
+ }
409
+
410
+ this._unsize( item );
411
+
412
+ if ( item === 'header' ) {
413
+ itemDom.host.prepend( this.dom.thead );
414
+ }
415
+ else {
416
+ itemDom.host.append( this.dom.tfoot );
417
+ }
418
+
419
+ if ( itemDom.floating ) {
420
+ itemDom.floating.remove();
421
+ itemDom.floating = null;
422
+ }
423
+ }
424
+ else if ( mode === 'in' ) {
425
+ // Remove the header from the read header and insert into a fixed
426
+ // positioned floating table clone
427
+ this._clone( item, forceChange );
428
+
429
+ itemDom.floating
430
+ .addClass( 'fixedHeader-floating' )
431
+ .css( item === 'header' ? 'top' : 'bottom', this.c[item+'Offset'] )
432
+ .css( 'left', position.left+'px' )
433
+ .css( 'width', position.width+'px' );
434
+
435
+ if ( item === 'footer' ) {
436
+ itemDom.floating.css( 'top', '' );
437
+ }
438
+ }
439
+ else if ( mode === 'below' ) { // only used for the header
440
+ // Fix the position of the floating header at base of the table body
441
+ this._clone( item, forceChange );
442
+
443
+ itemDom.floating
444
+ .addClass( 'fixedHeader-locked' )
445
+ .css( 'top', position.tfootTop - position.theadHeight )
446
+ .css( 'left', position.left+'px' )
447
+ .css( 'width', position.width+'px' );
448
+ }
449
+ else if ( mode === 'above' ) { // only used for the footer
450
+ // Fix the position of the floating footer at top of the table body
451
+ this._clone( item, forceChange );
452
+
453
+ itemDom.floating
454
+ .addClass( 'fixedHeader-locked' )
455
+ .css( 'top', position.tbodyTop )
456
+ .css( 'left', position.left+'px' )
457
+ .css( 'width', position.width+'px' );
458
+ }
459
+
460
+ // Restore focus if it was lost
461
+ if ( focus && focus !== document.activeElement ) {
462
+ focus.focus();
463
+ }
464
+
465
+ this.s.scrollLeft.header = -1;
466
+ this.s.scrollLeft.footer = -1;
467
+ this.s[item+'Mode'] = mode;
468
+ },
469
+
470
+ /**
471
+ * Cache the positional information that is required for the mode
472
+ * calculations that FixedHeader performs.
473
+ *
474
+ * @private
475
+ */
476
+ _positions: function ()
477
+ {
478
+ var dt = this.s.dt;
479
+ var table = dt.table();
480
+ var position = this.s.position;
481
+ var dom = this.dom;
482
+ var tableNode = $(table.node());
483
+
484
+ // Need to use the header and footer that are in the main table,
485
+ // regardless of if they are clones, since they hold the positions we
486
+ // want to measure from
487
+ var thead = tableNode.children('thead');
488
+ var tfoot = tableNode.children('tfoot');
489
+ var tbody = dom.tbody;
490
+
491
+ position.visible = tableNode.is(':visible');
492
+ position.width = tableNode.outerWidth();
493
+ position.left = tableNode.offset().left;
494
+ position.theadTop = thead.offset().top;
495
+ position.tbodyTop = tbody.offset().top;
496
+ position.theadHeight = position.tbodyTop - position.theadTop;
497
+
498
+ if ( tfoot.length ) {
499
+ position.tfootTop = tfoot.offset().top;
500
+ position.tfootBottom = position.tfootTop + tfoot.outerHeight();
501
+ position.tfootHeight = position.tfootBottom - position.tfootTop;
502
+ }
503
+ else {
504
+ position.tfootTop = position.tbodyTop + tbody.outerHeight();
505
+ position.tfootBottom = position.tfootTop;
506
+ position.tfootHeight = position.tfootTop;
507
+ }
508
+ },
509
+
510
+
511
+ /**
512
+ * Mode calculation - determine what mode the fixed items should be placed
513
+ * into.
514
+ *
515
+ * @param {boolean} forceChange Force a redraw of the mode, even if already
516
+ * in that mode.
517
+ * @private
518
+ */
519
+ _scroll: function ( forceChange )
520
+ {
521
+ var windowTop = $(document).scrollTop();
522
+ var windowLeft = $(document).scrollLeft();
523
+ var position = this.s.position;
524
+ var headerMode, footerMode;
525
+
526
+ if ( ! this.s.enable ) {
527
+ return;
528
+ }
529
+
530
+ if ( this.c.header ) {
531
+ if ( ! position.visible || windowTop <= position.theadTop - this.c.headerOffset ) {
532
+ headerMode = 'in-place';
533
+ }
534
+ else if ( windowTop <= position.tfootTop - position.theadHeight - this.c.headerOffset ) {
535
+ headerMode = 'in';
536
+ }
537
+ else {
538
+ headerMode = 'below';
539
+ }
540
+
541
+ if ( forceChange || headerMode !== this.s.headerMode ) {
542
+ this._modeChange( headerMode, 'header', forceChange );
543
+ }
544
+
545
+ this._horizontal( 'header', windowLeft );
546
+ }
547
+
548
+ if ( this.c.footer && this.dom.tfoot.length ) {
549
+ if ( ! position.visible || windowTop + position.windowHeight >= position.tfootBottom + this.c.footerOffset ) {
550
+ footerMode = 'in-place';
551
+ }
552
+ else if ( position.windowHeight + windowTop > position.tbodyTop + position.tfootHeight + this.c.footerOffset ) {
553
+ footerMode = 'in';
554
+ }
555
+ else {
556
+ footerMode = 'above';
557
+ }
558
+
559
+ if ( forceChange || footerMode !== this.s.footerMode ) {
560
+ this._modeChange( footerMode, 'footer', forceChange );
561
+ }
562
+
563
+ this._horizontal( 'footer', windowLeft );
564
+ }
565
+ }
566
+ } );
567
+
568
+
569
+ /**
570
+ * Version
571
+ * @type {String}
572
+ * @static
573
+ */
574
+ FixedHeader.version = "3.1.2";
575
+
576
+ /**
577
+ * Defaults
578
+ * @type {Object}
579
+ * @static
580
+ */
581
+ FixedHeader.defaults = {
582
+ header: true,
583
+ footer: false,
584
+ headerOffset: 0,
585
+ footerOffset: 0
586
+ };
587
+
588
+
589
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
590
+ * DataTables interfaces
591
+ */
592
+
593
+ // Attach for constructor access
594
+ $.fn.dataTable.FixedHeader = FixedHeader;
595
+ $.fn.DataTable.FixedHeader = FixedHeader;
596
+
597
+
598
+ // DataTables creation - check if the FixedHeader option has been defined on the
599
+ // table and if so, initialise
600
+ $(document).on( 'init.dt.dtfh', function (e, settings, json) {
601
+ if ( e.namespace !== 'dt' ) {
602
+ return;
603
+ }
604
+
605
+ var init = settings.oInit.fixedHeader;
606
+ var defaults = DataTable.defaults.fixedHeader;
607
+
608
+ if ( (init || defaults) && ! settings._fixedHeader ) {
609
+ var opts = $.extend( {}, defaults, init );
610
+
611
+ if ( init !== false ) {
612
+ new FixedHeader( settings, opts );
613
+ }
614
+ }
615
+ } );
616
+
617
+ // DataTables API methods
618
+ DataTable.Api.register( 'fixedHeader()', function () {} );
619
+
620
+ DataTable.Api.register( 'fixedHeader.adjust()', function () {
621
+ return this.iterator( 'table', function ( ctx ) {
622
+ var fh = ctx._fixedHeader;
623
+
624
+ if ( fh ) {
625
+ fh.update();
626
+ }
627
+ } );
628
+ } );
629
+
630
+ DataTable.Api.register( 'fixedHeader.enable()', function ( flag ) {
631
+ return this.iterator( 'table', function ( ctx ) {
632
+ var fh = ctx._fixedHeader;
633
+
634
+ if ( fh ) {
635
+ fh.enable( flag !== undefined ? flag : true );
636
+ }
637
+ } );
638
+ } );
639
+
640
+ DataTable.Api.register( 'fixedHeader.disable()', function ( ) {
641
+ return this.iterator( 'table', function ( ctx ) {
642
+ var fh = ctx._fixedHeader;
643
+
644
+ if ( fh ) {
645
+ fh.enable( false );
646
+ }
647
+ } );
648
+ } );
649
+
650
+ $.each( ['header', 'footer'], function ( i, el ) {
651
+ DataTable.Api.register( 'fixedHeader.'+el+'Offset()', function ( offset ) {
652
+ var ctx = this.context;
653
+
654
+ if ( offset === undefined ) {
655
+ return ctx.length && ctx[0]._fixedHeader ?
656
+ ctx[0]._fixedHeader[el +'Offset']() :
657
+ undefined;
658
+ }
659
+
660
+ return this.iterator( 'table', function ( ctx ) {
661
+ var fh = ctx._fixedHeader;
662
+
663
+ if ( fh ) {
664
+ fh[ el +'Offset' ]( offset );
665
+ }
666
+ } );
667
+ } );
668
+ } );
669
+
670
+
671
+ return FixedHeader;
672
+ }));
common/vendor/datatables/extensions/FixedHeader/js/dataTables.fixedHeader.min.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ FixedHeader 3.1.2
3
+ ©2009-2016 SpryMedia Ltd - datatables.net/license
4
+ */
5
+ (function(d){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(g){return d(g,window,document)}):"object"===typeof exports?module.exports=function(g,h){g||(g=window);if(!h||!h.fn.dataTable)h=require("datatables.net")(g,h).$;return d(h,g,g.document)}:d(jQuery,window,document)})(function(d,g,h,k){var j=d.fn.dataTable,l=0,i=function(b,a){if(!(this instanceof i))throw"FixedHeader must be initialised with the 'new' keyword.";!0===a&&(a={});b=new j.Api(b);this.c=d.extend(!0,
6
+ {},i.defaults,a);this.s={dt:b,position:{theadTop:0,tbodyTop:0,tfootTop:0,tfootBottom:0,width:0,left:0,tfootHeight:0,theadHeight:0,windowHeight:d(g).height(),visible:!0},headerMode:null,footerMode:null,autoWidth:b.settings()[0].oFeatures.bAutoWidth,namespace:".dtfc"+l++,scrollLeft:{header:-1,footer:-1},enable:!0};this.dom={floatingHeader:null,thead:d(b.table().header()),tbody:d(b.table().body()),tfoot:d(b.table().footer()),header:{host:null,floating:null,placeholder:null},footer:{host:null,floating:null,
7
+ placeholder:null}};this.dom.header.host=this.dom.thead.parent();this.dom.footer.host=this.dom.tfoot.parent();var e=b.settings()[0];if(e._fixedHeader)throw"FixedHeader already initialised on table "+e.nTable.id;e._fixedHeader=this;this._constructor()};d.extend(i.prototype,{enable:function(b){this.s.enable=b;this.c.header&&this._modeChange("in-place","header",!0);this.c.footer&&this.dom.tfoot.length&&this._modeChange("in-place","footer",!0);this.update()},headerOffset:function(b){b!==k&&(this.c.headerOffset=
8
+ b,this.update());return this.c.headerOffset},footerOffset:function(b){b!==k&&(this.c.footerOffset=b,this.update());return this.c.footerOffset},update:function(){this._positions();this._scroll(!0)},_constructor:function(){var b=this,a=this.s.dt;d(g).on("scroll"+this.s.namespace,function(){b._scroll()}).on("resize"+this.s.namespace,function(){b.s.position.windowHeight=d(g).height();b.update()});var e=d(".fh-fixedHeader");!this.c.headerOffset&&e.length&&(this.c.headerOffset=e.outerHeight());e=d(".fh-fixedFooter");
9
+ !this.c.footerOffset&&e.length&&(this.c.footerOffset=e.outerHeight());a.on("column-reorder.dt.dtfc column-visibility.dt.dtfc draw.dt.dtfc column-sizing.dt.dtfc",function(){b.update()});a.on("destroy.dtfc",function(){a.off(".dtfc");d(g).off(b.s.namespace)});this._positions();this._scroll()},_clone:function(b,a){var e=this.s.dt,c=this.dom[b],f="header"===b?this.dom.thead:this.dom.tfoot;!a&&c.floating?c.floating.removeClass("fixedHeader-floating fixedHeader-locked"):(c.floating&&(c.placeholder.remove(),
10
+ this._unsize(b),c.floating.children().detach(),c.floating.remove()),c.floating=d(e.table().node().cloneNode(!1)).css("table-layout","fixed").removeAttr("id").append(f).appendTo("body"),c.placeholder=f.clone(!1),c.host.prepend(c.placeholder),this._matchWidths(c.placeholder,c.floating))},_matchWidths:function(b,a){var e=function(a){return d(a,b).map(function(){return d(this).width()}).toArray()},c=function(b,c){d(b,a).each(function(a){d(this).css({width:c[a],minWidth:c[a]})})},f=e("th"),e=e("td");c("th",
11
+ f);c("td",e)},_unsize:function(b){var a=this.dom[b].floating;a&&("footer"===b||"header"===b&&!this.s.autoWidth)?d("th, td",a).css({width:"",minWidth:""}):a&&"header"===b&&d("th, td",a).css("min-width","")},_horizontal:function(b,a){var e=this.dom[b],c=this.s.position,d=this.s.scrollLeft;e.floating&&d[b]!==a&&(e.floating.css("left",c.left-a),d[b]=a)},_modeChange:function(b,a,e){var c=this.dom[a],f=this.s.position,g=d.contains(this.dom["footer"===a?"tfoot":"thead"][0],h.activeElement)?h.activeElement:
12
+ null;if("in-place"===b){if(c.placeholder&&(c.placeholder.remove(),c.placeholder=null),this._unsize(a),"header"===a?c.host.prepend(this.dom.thead):c.host.append(this.dom.tfoot),c.floating)c.floating.remove(),c.floating=null}else"in"===b?(this._clone(a,e),c.floating.addClass("fixedHeader-floating").css("header"===a?"top":"bottom",this.c[a+"Offset"]).css("left",f.left+"px").css("width",f.width+"px"),"footer"===a&&c.floating.css("top","")):"below"===b?(this._clone(a,e),c.floating.addClass("fixedHeader-locked").css("top",
13
+ f.tfootTop-f.theadHeight).css("left",f.left+"px").css("width",f.width+"px")):"above"===b&&(this._clone(a,e),c.floating.addClass("fixedHeader-locked").css("top",f.tbodyTop).css("left",f.left+"px").css("width",f.width+"px"));g&&g!==h.activeElement&&g.focus();this.s.scrollLeft.header=-1;this.s.scrollLeft.footer=-1;this.s[a+"Mode"]=b},_positions:function(){var b=this.s.dt.table(),a=this.s.position,e=this.dom,b=d(b.node()),c=b.children("thead"),f=b.children("tfoot"),e=e.tbody;a.visible=b.is(":visible");
14
+ a.width=b.outerWidth();a.left=b.offset().left;a.theadTop=c.offset().top;a.tbodyTop=e.offset().top;a.theadHeight=a.tbodyTop-a.theadTop;f.length?(a.tfootTop=f.offset().top,a.tfootBottom=a.tfootTop+f.outerHeight(),a.tfootHeight=a.tfootBottom-a.tfootTop):(a.tfootTop=a.tbodyTop+e.outerHeight(),a.tfootBottom=a.tfootTop,a.tfootHeight=a.tfootTop)},_scroll:function(b){var a=d(h).scrollTop(),e=d(h).scrollLeft(),c=this.s.position,f;if(this.s.enable&&(this.c.header&&(f=!c.visible||a<=c.theadTop-this.c.headerOffset?
15
+ "in-place":a<=c.tfootTop-c.theadHeight-this.c.headerOffset?"in":"below",(b||f!==this.s.headerMode)&&this._modeChange(f,"header",b),this._horizontal("header",e)),this.c.footer&&this.dom.tfoot.length))a=!c.visible||a+c.windowHeight>=c.tfootBottom+this.c.footerOffset?"in-place":c.windowHeight+a>c.tbodyTop+c.tfootHeight+this.c.footerOffset?"in":"above",(b||a!==this.s.footerMode)&&this._modeChange(a,"footer",b),this._horizontal("footer",e)}});i.version="3.1.2";i.defaults={header:!0,footer:!1,headerOffset:0,
16
+ footerOffset:0};d.fn.dataTable.FixedHeader=i;d.fn.DataTable.FixedHeader=i;d(h).on("init.dt.dtfh",function(b,a){if("dt"===b.namespace){var e=a.oInit.fixedHeader,c=j.defaults.fixedHeader;if((e||c)&&!a._fixedHeader)c=d.extend({},c,e),!1!==e&&new i(a,c)}});j.Api.register("fixedHeader()",function(){});j.Api.register("fixedHeader.adjust()",function(){return this.iterator("table",function(b){(b=b._fixedHeader)&&b.update()})});j.Api.register("fixedHeader.enable()",function(b){return this.iterator("table",
17
+ function(a){(a=a._fixedHeader)&&a.enable(b!==k?b:!0)})});j.Api.register("fixedHeader.disable()",function(){return this.iterator("table",function(b){(b=b._fixedHeader)&&b.enable(!1)})});d.each(["header","footer"],function(b,a){j.Api.register("fixedHeader."+a+"Offset()",function(b){var c=this.context;return b===k?c.length&&c[0]._fixedHeader?c[0]._fixedHeader[a+"Offset"]():k:this.iterator("table",function(c){if(c=c._fixedHeader)c[a+"Offset"](b)})})});return i});
common/vendor/datatables/extensions/Responsive/css/responsive.dataTables.css ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ table.dataTable.dtr-inline.collapsed > tbody > tr > td.child,
2
+ table.dataTable.dtr-inline.collapsed > tbody > tr > th.child,
3
+ table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty {
4
+ cursor: default !important;
5
+ }
6
+ table.dataTable.dtr-inline.collapsed > tbody > tr > td.child:before,
7
+ table.dataTable.dtr-inline.collapsed > tbody > tr > th.child:before,
8
+ table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty:before {
9
+ display: none !important;
10
+ }
11
+ table.dataTable.dtr-inline.collapsed > tbody > tr > td:first-child,
12
+ table.dataTable.dtr-inline.collapsed > tbody > tr > th:first-child {
13
+ position: relative;
14
+ padding-left: 30px;
15
+ cursor: pointer;
16
+ }
17
+ table.dataTable.dtr-inline.collapsed > tbody > tr > td:first-child:before,
18
+ table.dataTable.dtr-inline.collapsed > tbody > tr > th:first-child:before {
19
+ top: 9px;
20
+ left: 4px;
21
+ height: 14px;
22
+ width: 14px;
23
+ display: block;
24
+ position: absolute;
25
+ color: white;
26
+ border: 2px solid white;
27
+ border-radius: 14px;
28
+ box-shadow: 0 0 3px #444;
29
+ box-sizing: content-box;
30
+ text-align: center;
31
+ font-family: 'Courier New', Courier, monospace;
32
+ line-height: 14px;
33
+ content: '+';
34
+ background-color: #31b131;
35
+ }
36
+ table.dataTable.dtr-inline.collapsed > tbody > tr.parent > td:first-child:before,
37
+ table.dataTable.dtr-inline.collapsed > tbody > tr.parent > th:first-child:before {
38
+ content: '-';
39
+ background-color: #d33333;
40
+ }
41
+ table.dataTable.dtr-inline.collapsed > tbody > tr.child td:before {
42
+ display: none;
43
+ }
44
+ table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td:first-child,
45
+ table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th:first-child {
46
+ padding-left: 27px;
47
+ }
48
+ table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td:first-child:before,
49
+ table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th:first-child:before {
50
+ top: 5px;
51
+ left: 4px;
52
+ height: 14px;
53
+ width: 14px;
54
+ border-radius: 14px;
55
+ line-height: 14px;
56
+ text-indent: 3px;
57
+ }
58
+ table.dataTable.dtr-column > tbody > tr > td.control,
59
+ table.dataTable.dtr-column > tbody > tr > th.control {
60
+ position: relative;
61
+ cursor: pointer;
62
+ }
63
+ table.dataTable.dtr-column > tbody > tr > td.control:before,
64
+ table.dataTable.dtr-column > tbody > tr > th.control:before {
65
+ top: 50%;
66
+ left: 50%;
67
+ height: 16px;
68
+ width: 16px;
69
+ margin-top: -10px;
70
+ margin-left: -10px;
71
+ display: block;
72
+ position: absolute;
73
+ color: white;
74
+ border: 2px solid white;
75
+ border-radius: 14px;
76
+ box-shadow: 0 0 3px #444;
77
+ box-sizing: content-box;
78
+ text-align: center;
79
+ font-family: 'Courier New', Courier, monospace;
80
+ line-height: 14px;
81
+ content: '+';
82
+ background-color: #31b131;
83
+ }
84
+ table.dataTable.dtr-column > tbody > tr.parent td.control:before,
85
+ table.dataTable.dtr-column > tbody > tr.parent th.control:before {
86
+ content: '-';
87
+ background-color: #d33333;
88
+ }
89
+ table.dataTable > tbody > tr.child {
90
+ padding: 0.5em 1em;
91
+ }
92
+ table.dataTable > tbody > tr.child:hover {
93
+ background: transparent !important;
94
+ }
95
+ table.dataTable > tbody > tr.child ul {
96
+ display: inline-block;
97
+ list-style-type: none;
98
+ margin: 0;
99
+ padding: 0;
100
+ }
101
+ table.dataTable > tbody > tr.child ul li {
102
+ border-bottom: 1px solid #efefef;
103
+ padding: 0.5em 0;
104
+ }
105
+ table.dataTable > tbody > tr.child ul li:first-child {
106
+ padding-top: 0;
107
+ }
108
+ table.dataTable > tbody > tr.child ul li:last-child {
109
+ border-bottom: none;
110
+ }
111
+ table.dataTable > tbody > tr.child span.dtr-title {
112
+ display: inline-block;
113
+ min-width: 75px;
114
+ font-weight: bold;
115
+ }
116
+
117
+ div.dtr-modal {
118
+ position: fixed;
119
+ box-sizing: border-box;
120
+ top: 0;
121
+ left: 0;
122
+ height: 100%;
123
+ width: 100%;
124
+ z-index: 100;
125
+ padding: 10em 1em;
126
+ }
127
+ div.dtr-modal div.dtr-modal-display {
128
+ position: absolute;
129
+ top: 0;
130
+ left: 0;
131
+ bottom: 0;
132
+ right: 0;
133
+ width: 50%;
134
+ height: 50%;
135
+ overflow: auto;
136
+ margin: auto;
137
+ z-index: 102;
138
+ overflow: auto;
139
+ background-color: #f5f5f7;
140
+ border: 1px solid black;
141
+ border-radius: 0.5em;
142
+ box-shadow: 0 12px 30px rgba(0, 0, 0, 0.6);
143
+ }
144
+ div.dtr-modal div.dtr-modal-content {
145
+ position: relative;
146
+ padding: 1em;
147
+ }
148
+ div.dtr-modal div.dtr-modal-close {
149
+ position: absolute;
150
+ top: 6px;
151
+ right: 6px;
152
+ width: 22px;
153
+ height: 22px;
154
+ border: 1px solid #eaeaea;
155
+ background-color: #f9f9f9;
156
+ text-align: center;
157
+ border-radius: 3px;
158
+ cursor: pointer;
159
+ z-index: 12;
160
+ }
161
+ div.dtr-modal div.dtr-modal-close:hover {
162
+ background-color: #eaeaea;
163
+ }
164
+ div.dtr-modal div.dtr-modal-background {
165
+ position: fixed;
166
+ top: 0;
167
+ left: 0;
168
+ right: 0;
169
+ bottom: 0;
170
+ z-index: 101;
171
+ background: rgba(0, 0, 0, 0.6);
172
+ }
173
+
174
+ @media screen and (max-width: 767px) {
175
+ div.dtr-modal div.dtr-modal-display {
176
+ width: 95%;
177
+ }
178
+ }
common/vendor/datatables/extensions/Responsive/css/responsive.dataTables.min.css ADDED
@@ -0,0 +1 @@
 
1
+ table.dataTable.dtr-inline.collapsed>tbody>tr>td.child,table.dataTable.dtr-inline.collapsed>tbody>tr>th.child,table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty{cursor:default !important}table.dataTable.dtr-inline.collapsed>tbody>tr>td.child:before,table.dataTable.dtr-inline.collapsed>tbody>tr>th.child:before,table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty:before{display:none !important}table.dataTable.dtr-inline.collapsed>tbody>tr>td:first-child,table.dataTable.dtr-inline.collapsed>tbody>tr>th:first-child{position:relative;padding-left:30px;cursor:pointer}table.dataTable.dtr-inline.collapsed>tbody>tr>td:first-child:before,table.dataTable.dtr-inline.collapsed>tbody>tr>th:first-child:before{top:9px;left:4px;height:14px;width:14px;display:block;position:absolute;color:white;border:2px solid white;border-radius:14px;box-shadow:0 0 3px #444;box-sizing:content-box;text-align:center;font-family:'Courier New', Courier, monospace;line-height:14px;content:'+';background-color:#31b131}table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td:first-child:before,table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th:first-child:before{content:'-';background-color:#d33333}table.dataTable.dtr-inline.collapsed>tbody>tr.child td:before{display:none}table.dataTable.dtr-inline.collapsed.compact>tbody>tr>td:first-child,table.dataTable.dtr-inline.collapsed.compact>tbody>tr>th:first-child{padding-left:27px}table.dataTable.dtr-inline.collapsed.compact>tbody>tr>td:first-child:before,table.dataTable.dtr-inline.collapsed.compact>tbody>tr>th:first-child:before{top:5px;left:4px;height:14px;width:14px;border-radius:14px;line-height:14px;text-indent:3px}table.dataTable.dtr-column>tbody>tr>td.control,table.dataTable.dtr-column>tbody>tr>th.control{position:relative;cursor:pointer}table.dataTable.dtr-column>tbody>tr>td.control:before,table.dataTable.dtr-column>tbody>tr>th.control:before{top:50%;left:50%;height:16px;width:16px;margin-top:-10px;margin-left:-10px;display:block;position:absolute;color:white;border:2px solid white;border-radius:14px;box-shadow:0 0 3px #444;box-sizing:content-box;text-align:center;font-family:'Courier New', Courier, monospace;line-height:14px;content:'+';background-color:#31b131}table.dataTable.dtr-column>tbody>tr.parent td.control:before,table.dataTable.dtr-column>tbody>tr.parent th.control:before{content:'-';background-color:#d33333}table.dataTable>tbody>tr.child{padding:0.5em 1em}table.dataTable>tbody>tr.child:hover{background:transparent !important}table.dataTable>tbody>tr.child ul{display:inline-block;list-style-type:none;margin:0;padding:0}table.dataTable>tbody>tr.child ul li{border-bottom:1px solid #efefef;padding:0.5em 0}table.dataTable>tbody>tr.child ul li:first-child{padding-top:0}table.dataTable>tbody>tr.child ul li:last-child{border-bottom:none}table.dataTable>tbody>tr.child span.dtr-title{display:inline-block;min-width:75px;font-weight:bold}div.dtr-modal{position:fixed;box-sizing:border-box;top:0;left:0;height:100%;width:100%;z-index:100;padding:10em 1em}div.dtr-modal div.dtr-modal-display{position:absolute;top:0;left:0;bottom:0;right:0;width:50%;height:50%;overflow:auto;margin:auto;z-index:102;overflow:auto;background-color:#f5f5f7;border:1px solid black;border-radius:0.5em;box-shadow:0 12px 30px rgba(0,0,0,0.6)}div.dtr-modal div.dtr-modal-content{position:relative;padding:1em}div.dtr-modal div.dtr-modal-close{position:absolute;top:6px;right:6px;width:22px;height:22px;border:1px solid #eaeaea;background-color:#f9f9f9;text-align:center;border-radius:3px;cursor:pointer;z-index:12}div.dtr-modal div.dtr-modal-close:hover{background-color:#eaeaea}div.dtr-modal div.dtr-modal-background{position:fixed;top:0;left:0;right:0;bottom:0;z-index:101;background:rgba(0,0,0,0.6)}@media screen and (max-width: 767px){div.dtr-modal div.dtr-modal-display{width:95%}}
common/vendor/datatables/extensions/Responsive/js/dataTables.responsive.js ADDED
@@ -0,0 +1,1232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*! Responsive 2.1.0
2
+ * 2014-2016 SpryMedia Ltd - datatables.net/license
3
+ */
4
+
5
+ /**
6
+ * @summary Responsive
7
+ * @description Responsive tables plug-in for DataTables
8
+ * @version 2.1.0
9
+ * @file dataTables.responsive.js
10
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
11
+ * @contact www.sprymedia.co.uk/contact
12
+ * @copyright Copyright 2014-2016 SpryMedia Ltd.
13
+ *
14
+ * This source file is free software, available under the following license:
15
+ * MIT license - http://datatables.net/license/mit
16
+ *
17
+ * This source file is distributed in the hope that it will be useful, but
18
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
20
+ *
21
+ * For details please refer to: http://www.datatables.net
22
+ */
23
+ (function( factory ){
24
+ if ( typeof define === 'function' && define.amd ) {
25
+ // AMD
26
+ define( ['jquery', 'datatables.net'], function ( $ ) {
27
+ return factory( $, window, document );
28
+ } );
29
+ }
30
+ else if ( typeof exports === 'object' ) {
31
+ // CommonJS
32
+ module.exports = function (root, $) {
33
+ if ( ! root ) {
34
+ root = window;
35
+ }
36
+
37
+ if ( ! $ || ! $.fn.dataTable ) {
38
+ $ = require('datatables.net')(root, $).$;
39
+ }
40
+
41
+ return factory( $, root, root.document );
42
+ };
43
+ }
44
+ else {
45
+ // Browser
46
+ factory( jQuery, window, document );
47
+ }
48
+ }(function( $, window, document, undefined ) {
49
+ 'use strict';
50
+ var DataTable = $.fn.dataTable;
51
+
52
+
53
+ /**
54
+ * Responsive is a plug-in for the DataTables library that makes use of
55
+ * DataTables' ability to change the visibility of columns, changing the
56
+ * visibility of columns so the displayed columns fit into the table container.
57
+ * The end result is that complex tables will be dynamically adjusted to fit
58
+ * into the viewport, be it on a desktop, tablet or mobile browser.
59
+ *
60
+ * Responsive for DataTables has two modes of operation, which can used
61
+ * individually or combined:
62
+ *
63
+ * * Class name based control - columns assigned class names that match the
64
+ * breakpoint logic can be shown / hidden as required for each breakpoint.
65
+ * * Automatic control - columns are automatically hidden when there is no
66
+ * room left to display them. Columns removed from the right.
67
+ *
68
+ * In additional to column visibility control, Responsive also has built into
69
+ * options to use DataTables' child row display to show / hide the information
70
+ * from the table that has been hidden. There are also two modes of operation
71
+ * for this child row display:
72
+ *
73
+ * * Inline - when the control element that the user can use to show / hide
74
+ * child rows is displayed inside the first column of the table.
75
+ * * Column - where a whole column is dedicated to be the show / hide control.
76
+ *
77
+ * Initialisation of Responsive is performed by:
78
+ *
79
+ * * Adding the class `responsive` or `dt-responsive` to the table. In this case
80
+ * Responsive will automatically be initialised with the default configuration
81
+ * options when the DataTable is created.
82
+ * * Using the `responsive` option in the DataTables configuration options. This
83
+ * can also be used to specify the configuration options, or simply set to
84
+ * `true` to use the defaults.
85
+ *
86
+ * @class
87
+ * @param {object} settings DataTables settings object for the host table
88
+ * @param {object} [opts] Configuration options
89
+ * @requires jQuery 1.7+
90
+ * @requires DataTables 1.10.3+
91
+ *
92
+ * @example
93
+ * $('#example').DataTable( {
94
+ * responsive: true
95
+ * } );
96
+ * } );
97
+ */
98
+ var Responsive = function ( settings, opts ) {
99
+ // Sanity check that we are using DataTables 1.10 or newer
100
+ if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.3' ) ) {
101
+ throw 'DataTables Responsive requires DataTables 1.10.3 or newer';
102
+ }
103
+
104
+ this.s = {
105
+ dt: new DataTable.Api( settings ),
106
+ columns: [],
107
+ current: []
108
+ };
109
+
110
+ // Check if responsive has already been initialised on this table
111
+ if ( this.s.dt.settings()[0].responsive ) {
112
+ return;
113
+ }
114
+
115
+ // details is an object, but for simplicity the user can give it as a string
116
+ // or a boolean
117
+ if ( opts && typeof opts.details === 'string' ) {
118
+ opts.details = { type: opts.details };
119
+ }
120
+ else if ( opts && opts.details === false ) {
121
+ opts.details = { type: false };
122
+ }
123
+ else if ( opts && opts.details === true ) {
124
+ opts.details = { type: 'inline' };
125
+ }
126
+
127
+ this.c = $.extend( true, {}, Responsive.defaults, DataTable.defaults.responsive, opts );
128
+ settings.responsive = this;
129
+ this._constructor();
130
+ };
131
+
132
+ $.extend( Responsive.prototype, {
133
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
134
+ * Constructor
135
+ */
136
+
137
+ /**
138
+ * Initialise the Responsive instance
139
+ *
140
+ * @private
141
+ */
142
+ _constructor: function ()
143
+ {
144
+ var that = this;
145
+ var dt = this.s.dt;
146
+ var dtPrivateSettings = dt.settings()[0];
147
+ var oldWindowWidth = $(window).width();
148
+
149
+ dt.settings()[0]._responsive = this;
150
+
151
+ // Use DataTables' throttle function to avoid processor thrashing on
152
+ // resize
153
+ $(window).on( 'resize.dtr orientationchange.dtr', DataTable.util.throttle( function () {
154
+ // iOS has a bug whereby resize can fire when only scrolling
155
+ // See: http://stackoverflow.com/questions/8898412
156
+ var width = $(window).width();
157
+
158
+ if ( width !== oldWindowWidth ) {
159
+ that._resize();
160
+ oldWindowWidth = width;
161
+ }
162
+ } ) );
163
+
164
+ // DataTables doesn't currently trigger an event when a row is added, so
165
+ // we need to hook into its private API to enforce the hidden rows when
166
+ // new data is added
167
+ dtPrivateSettings.oApi._fnCallbackReg( dtPrivateSettings, 'aoRowCreatedCallback', function (tr, data, idx) {
168
+ if ( $.inArray( false, that.s.current ) !== -1 ) {
169
+ $('td, th', tr).each( function ( i ) {
170
+ var idx = dt.column.index( 'toData', i );
171
+
172
+ if ( that.s.current[idx] === false ) {
173
+ $(this).css('display', 'none');
174
+ }
175
+ } );
176
+ }
177
+ } );
178
+
179
+ // Destroy event handler
180
+ dt.on( 'destroy.dtr', function () {
181
+ dt.off( '.dtr' );
182
+ $( dt.table().body() ).off( '.dtr' );
183
+ $(window).off( 'resize.dtr orientationchange.dtr' );
184
+
185
+ // Restore the columns that we've hidden
186
+ $.each( that.s.current, function ( i, val ) {
187
+ if ( val === false ) {
188
+ that._setColumnVis( i, true );
189
+ }
190
+ } );
191
+ } );
192
+
193
+ // Reorder the breakpoints array here in case they have been added out
194
+ // of order
195
+ this.c.breakpoints.sort( function (a, b) {
196
+ return a.width < b.width ? 1 :
197
+ a.width > b.width ? -1 : 0;
198
+ } );
199
+
200
+ this._classLogic();
201
+ this._resizeAuto();
202
+
203
+ // Details handler
204
+ var details = this.c.details;
205
+
206
+ if ( details.type !== false ) {
207
+ that._detailsInit();
208
+
209
+ // DataTables will trigger this event on every column it shows and
210
+ // hides individually
211
+ dt.on( 'column-visibility.dtr', function (e, ctx, col, vis) {
212
+ that._classLogic();
213
+ that._resizeAuto();
214
+ that._resize();
215
+ } );
216
+
217
+ // Redraw the details box on each draw which will happen if the data
218
+ // has changed. This is used until DataTables implements a native
219
+ // `updated` event for rows
220
+ dt.on( 'draw.dtr', function () {
221
+ that._redrawChildren();
222
+ } );
223
+
224
+ $(dt.table().node()).addClass( 'dtr-'+details.type );
225
+ }
226
+
227
+ dt.on( 'column-reorder.dtr', function (e, settings, details) {
228
+ that._classLogic();
229
+ that._resizeAuto();
230
+ that._resize();
231
+ } );
232
+
233
+ // Change in column sizes means we need to calc
234
+ dt.on( 'column-sizing.dtr', function () {
235
+ that._resizeAuto();
236
+ that._resize();
237
+ });
238
+
239
+ dt.on( 'init.dtr', function (e, settings, details) {
240
+ that._resizeAuto();
241
+ that._resize();
242
+
243
+ // If columns were hidden, then DataTables needs to adjust the
244
+ // column sizing
245
+ if ( $.inArray( false, that.s.current ) ) {
246
+ dt.columns.adjust();
247
+ }
248
+ } );
249
+
250
+ // First pass - draw the table for the current viewport size
251
+ this._resize();
252
+ },
253
+
254
+
255
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
256
+ * Private methods
257
+ */
258
+
259
+ /**
260
+ * Calculate the visibility for the columns in a table for a given
261
+ * breakpoint. The result is pre-determined based on the class logic if
262
+ * class names are used to control all columns, but the width of the table
263
+ * is also used if there are columns which are to be automatically shown
264
+ * and hidden.
265
+ *
266
+ * @param {string} breakpoint Breakpoint name to use for the calculation
267
+ * @return {array} Array of boolean values initiating the visibility of each
268
+ * column.
269
+ * @private
270
+ */
271
+ _columnsVisiblity: function ( breakpoint )
272
+ {
273
+ var dt = this.s.dt;
274
+ var columns = this.s.columns;
275
+ var i, ien;
276
+
277
+ // Create an array that defines the column ordering based first on the
278
+ // column's priority, and secondly the column index. This allows the
279
+ // columns to be removed from the right if the priority matches
280
+ var order = columns
281
+ .map( function ( col, idx ) {
282
+ return {
283
+ columnIdx: idx,
284
+ priority: col.priority
285
+ };
286
+ } )
287
+ .sort( function ( a, b ) {
288
+ if ( a.priority !== b.priority ) {
289
+ return a.priority - b.priority;
290
+ }
291
+ return a.columnIdx - b.columnIdx;
292
+ } );
293
+
294
+ // Class logic - determine which columns are in this breakpoint based
295
+ // on the classes. If no class control (i.e. `auto`) then `-` is used
296
+ // to indicate this to the rest of the function
297
+ var display = $.map( columns, function ( col ) {
298
+ return col.auto && col.minWidth === null ?
299
+ false :
300
+ col.auto === true ?
301
+ '-' :
302
+ $.inArray( breakpoint, col.includeIn ) !== -1;
303
+ } );
304
+
305
+ // Auto column control - first pass: how much width is taken by the
306
+ // ones that must be included from the non-auto columns
307
+ var requiredWidth = 0;
308
+ for ( i=0, ien=display.length ; i<ien ; i++ ) {
309
+ if ( display[i] === true ) {
310
+ requiredWidth += columns[i].minWidth;
311
+ }
312
+ }
313
+
314
+ // Second pass, use up any remaining width for other columns. For
315
+ // scrolling tables we need to subtract the width of the scrollbar. It
316
+ // may not be requires which makes this sub-optimal, but it would
317
+ // require another full redraw to make complete use of those extra few
318
+ // pixels
319
+ var scrolling = dt.settings()[0].oScroll;
320
+ var bar = scrolling.sY || scrolling.sX ? scrolling.iBarWidth : 0;
321
+ var widthAvailable = dt.table().container().offsetWidth - bar;
322
+ var usedWidth = widthAvailable - requiredWidth;
323
+
324
+ // Control column needs to always be included. This makes it sub-
325
+ // optimal in terms of using the available with, but to stop layout
326
+ // thrashing or overflow. Also we need to account for the control column
327
+ // width first so we know how much width is available for the other
328
+ // columns, since the control column might not be the first one shown
329
+ for ( i=0, ien=display.length ; i<ien ; i++ ) {
330
+ if ( columns[i].control ) {
331
+ usedWidth -= columns[i].minWidth;
332
+ }
333
+ }
334
+
335
+ // Allow columns to be shown (counting by priority and then right to
336
+ // left) until we run out of room
337
+ var empty = false;
338
+ for ( i=0, ien=order.length ; i<ien ; i++ ) {
339
+ var colIdx = order[i].columnIdx;
340
+
341
+ if ( display[colIdx] === '-' && ! columns[colIdx].control && columns[colIdx].minWidth ) {
342
+ // Once we've found a column that won't fit we don't let any
343
+ // others display either, or columns might disappear in the
344
+ // middle of the table
345
+ if ( empty || usedWidth - columns[colIdx].minWidth < 0 ) {
346
+ empty = true;
347
+ display[colIdx] = false;
348
+ }
349
+ else {
350
+ display[colIdx] = true;
351
+ }
352
+
353
+ usedWidth -= columns[colIdx].minWidth;
354
+ }
355
+ }
356
+
357
+ // Determine if the 'control' column should be shown (if there is one).
358
+ // This is the case when there is a hidden column (that is not the
359
+ // control column). The two loops look inefficient here, but they are
360
+ // trivial and will fly through. We need to know the outcome from the
361
+ // first , before the action in the second can be taken
362
+ var showControl = false;
363
+
364
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
365
+ if ( ! columns[i].control && ! columns[i].never && ! display[i] ) {
366
+ showControl = true;
367
+ break;
368
+ }
369
+ }
370
+
371
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
372
+ if ( columns[i].control ) {
373
+ display[i] = showControl;
374
+ }
375
+ }
376
+
377
+ // Finally we need to make sure that there is at least one column that
378
+ // is visible
379
+ if ( $.inArray( true, display ) === -1 ) {
380
+ display[0] = true;
381
+ }
382
+
383
+ return display;
384
+ },
385
+
386
+
387
+ /**
388
+ * Create the internal `columns` array with information about the columns
389
+ * for the table. This includes determining which breakpoints the column
390
+ * will appear in, based upon class names in the column, which makes up the
391
+ * vast majority of this method.
392
+ *
393
+ * @private
394
+ */
395
+ _classLogic: function ()
396
+ {
397
+ var that = this;
398
+ var calc = {};
399
+ var breakpoints = this.c.breakpoints;
400
+ var dt = this.s.dt;
401
+ var columns = dt.columns().eq(0).map( function (i) {
402
+ var column = this.column(i);
403
+ var className = column.header().className;
404
+ var priority = dt.settings()[0].aoColumns[i].responsivePriority;
405
+
406
+ if ( priority === undefined ) {
407
+ var dataPriority = $(column.header()).data('priority');
408
+
409
+ priority = dataPriority !== undefined ?
410
+ dataPriority * 1 :
411
+ 10000;
412
+ }
413
+
414
+ return {
415
+ className: className,
416
+ includeIn: [],
417
+ auto: false,
418
+ control: false,
419
+ never: className.match(/\bnever\b/) ? true : false,
420
+ priority: priority
421
+ };
422
+ } );
423
+
424
+ // Simply add a breakpoint to `includeIn` array, ensuring that there are
425
+ // no duplicates
426
+ var add = function ( colIdx, name ) {
427
+ var includeIn = columns[ colIdx ].includeIn;
428
+
429
+ if ( $.inArray( name, includeIn ) === -1 ) {
430
+ includeIn.push( name );
431
+ }
432
+ };
433
+
434
+ var column = function ( colIdx, name, operator, matched ) {
435
+ var size, i, ien;
436
+
437
+ if ( ! operator ) {
438
+ columns[ colIdx ].includeIn.push( name );
439
+ }
440
+ else if ( operator === 'max-' ) {
441
+ // Add this breakpoint and all smaller
442
+ size = that._find( name ).width;
443
+
444
+ for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
445
+ if ( breakpoints[i].width <= size ) {
446
+ add( colIdx, breakpoints[i].name );
447
+ }
448
+ }
449
+ }
450
+ else if ( operator === 'min-' ) {
451
+ // Add this breakpoint and all larger
452
+ size = that._find( name ).width;
453
+
454
+ for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
455
+ if ( breakpoints[i].width >= size ) {
456
+ add( colIdx, breakpoints[i].name );
457
+ }
458
+ }
459
+ }
460
+ else if ( operator === 'not-' ) {
461
+ // Add all but this breakpoint
462
+ for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
463
+ if ( breakpoints[i].name.indexOf( matched ) === -1 ) {
464
+ add( colIdx, breakpoints[i].name );
465
+ }
466
+ }
467
+ }
468
+ };
469
+
470
+ // Loop over each column and determine if it has a responsive control
471
+ // class
472
+ columns.each( function ( col, i ) {
473
+ var classNames = col.className.split(' ');
474
+ var hasClass = false;
475
+
476
+ // Split the class name up so multiple rules can be applied if needed
477
+ for ( var k=0, ken=classNames.length ; k<ken ; k++ ) {
478
+ var className = $.trim( classNames[k] );
479
+
480
+ if ( className === 'all' ) {
481
+ // Include in all
482
+ hasClass = true;
483
+ col.includeIn = $.map( breakpoints, function (a) {
484
+ return a.name;
485
+ } );
486
+ return;
487
+ }
488
+ else if ( className === 'none' || col.never ) {
489
+ // Include in none (default) and no auto
490
+ hasClass = true;
491
+ return;
492
+ }
493
+ else if ( className === 'control' ) {
494
+ // Special column that is only visible, when one of the other
495
+ // columns is hidden. This is used for the details control
496
+ hasClass = true;
497
+ col.control = true;
498
+ return;
499
+ }
500
+
501
+ $.each( breakpoints, function ( j, breakpoint ) {
502
+ // Does this column have a class that matches this breakpoint?
503
+ var brokenPoint = breakpoint.name.split('-');
504
+ var re = new RegExp( '(min\\-|max\\-|not\\-)?('+brokenPoint[0]+')(\\-[_a-zA-Z0-9])?' );
505
+ var match = className.match( re );
506
+
507
+ if ( match ) {
508
+ hasClass = true;
509
+
510
+ if ( match[2] === brokenPoint[0] && match[3] === '-'+brokenPoint[1] ) {
511
+ // Class name matches breakpoint name fully
512
+ column( i, breakpoint.name, match[1], match[2]+match[3] );
513
+ }
514
+ else if ( match[2] === brokenPoint[0] && ! match[3] ) {
515
+ // Class name matched primary breakpoint name with no qualifier
516
+ column( i, breakpoint.name, match[1], match[2] );
517
+ }
518
+ }
519
+ } );
520
+ }
521
+
522
+ // If there was no control class, then automatic sizing is used
523
+ if ( ! hasClass ) {
524
+ col.auto = true;
525
+ }
526
+ } );
527
+
528
+ this.s.columns = columns;
529
+ },
530
+
531
+
532
+ /**
533
+ * Show the details for the child row
534
+ *
535
+ * @param {DataTables.Api} row API instance for the row
536
+ * @param {boolean} update Update flag
537
+ * @private
538
+ */
539
+ _detailsDisplay: function ( row, update )
540
+ {
541
+ var that = this;
542
+ var dt = this.s.dt;
543
+ var details = this.c.details;
544
+
545
+ if ( details && details.type !== false ) {
546
+ var res = details.display( row, update, function () {
547
+ return details.renderer(
548
+ dt, row[0], that._detailsObj(row[0])
549
+ );
550
+ } );
551
+
552
+ if ( res === true || res === false ) {
553
+ $(dt.table().node()).triggerHandler( 'responsive-display.dt', [dt, row, res, update] );
554
+ }
555
+ }
556
+ },
557
+
558
+
559
+ /**
560
+ * Initialisation for the details handler
561
+ *
562
+ * @private
563
+ */
564
+ _detailsInit: function ()
565
+ {
566
+ var that = this;
567
+ var dt = this.s.dt;
568
+ var details = this.c.details;
569
+
570
+ // The inline type always uses the first child as the target
571
+ if ( details.type === 'inline' ) {
572
+ details.target = 'td:first-child, th:first-child';
573
+ }
574
+
575
+ // Keyboard accessibility
576
+ dt.on( 'draw.dtr', function () {
577
+ that._tabIndexes();
578
+ } );
579
+ that._tabIndexes(); // Initial draw has already happened
580
+
581
+ $( dt.table().body() ).on( 'keyup.dtr', 'td, th', function (e) {
582
+ if ( e.keyCode === 13 && $(this).data('dtr-keyboard') ) {
583
+ $(this).click();
584
+ }
585
+ } );
586
+
587
+ // type.target can be a string jQuery selector or a column index
588
+ var target = details.target;
589
+ var selector = typeof target === 'string' ? target : 'td, th';
590
+
591
+ // Click handler to show / hide the details rows when they are available
592
+ $( dt.table().body() )
593
+ .on( 'click.dtr mousedown.dtr mouseup.dtr', selector, function (e) {
594
+ // If the table is not collapsed (i.e. there is no hidden columns)
595
+ // then take no action
596
+ if ( ! $(dt.table().node()).hasClass('collapsed' ) ) {
597
+ return;
598
+ }
599
+
600
+ // Check that the row is actually a DataTable's controlled node
601
+ if ( ! dt.row( $(this).closest('tr') ).length ) {
602
+ return;
603
+ }
604
+
605
+ // For column index, we determine if we should act or not in the
606
+ // handler - otherwise it is already okay
607
+ if ( typeof target === 'number' ) {
608
+ var targetIdx = target < 0 ?
609
+ dt.columns().eq(0).length + target :
610
+ target;
611
+
612
+ if ( dt.cell( this ).index().column !== targetIdx ) {
613
+ return;
614
+ }
615
+ }
616
+
617
+ // $().closest() includes itself in its check
618
+ var row = dt.row( $(this).closest('tr') );
619
+
620
+ // Check event type to do an action
621
+ if ( e.type === 'click' ) {
622
+ // The renderer is given as a function so the caller can execute it
623
+ // only when they need (i.e. if hiding there is no point is running
624
+ // the renderer)
625
+ that._detailsDisplay( row, false );
626
+ }
627
+ else if ( e.type === 'mousedown' ) {
628
+ // For mouse users, prevent the focus ring from showing
629
+ $(this).css('outline', 'none');
630
+ }
631
+ else if ( e.type === 'mouseup' ) {
632
+ // And then re-allow at the end of the click
633
+ $(this).blur().css('outline', '');
634
+ }
635
+ } );
636
+ },
637
+
638
+
639
+ /**
640
+ * Get the details to pass to a renderer for a row
641
+ * @param {int} rowIdx Row index
642
+ * @private
643
+ */
644
+ _detailsObj: function ( rowIdx )
645
+ {
646
+ var that = this;
647
+ var dt = this.s.dt;
648
+
649
+ return $.map( this.s.columns, function( col, i ) {
650
+ // Never and control columns should not be passed to the renderer
651
+ if ( col.never || col.control ) {
652
+ return;
653
+ }
654
+
655
+ return {
656
+ title: dt.settings()[0].aoColumns[ i ].sTitle,
657
+ data: dt.cell( rowIdx, i ).render( that.c.orthogonal ),
658
+ hidden: dt.column( i ).visible() && !that.s.current[ i ],
659
+ columnIndex: i,
660
+ rowIndex: rowIdx
661
+ };
662
+ } );
663
+ },
664
+
665
+
666
+ /**
667
+ * Find a breakpoint object from a name
668
+ *
669
+ * @param {string} name Breakpoint name to find
670
+ * @return {object} Breakpoint description object
671
+ * @private
672
+ */
673
+ _find: function ( name )
674
+ {
675
+ var breakpoints = this.c.breakpoints;
676
+
677
+ for ( var i=0, ien=breakpoints.length ; i<ien ; i++ ) {
678
+ if ( breakpoints[i].name === name ) {
679
+ return breakpoints[i];
680
+ }
681
+ }
682
+ },
683
+
684
+
685
+ /**
686
+ * Re-create the contents of the child rows as the display has changed in
687
+ * some way.
688
+ *
689
+ * @private
690
+ */
691
+ _redrawChildren: function ()
692
+ {
693
+ var that = this;
694
+ var dt = this.s.dt;
695
+
696
+ dt.rows( {page: 'current'} ).iterator( 'row', function ( settings, idx ) {
697
+ var row = dt.row( idx );
698
+
699
+ that._detailsDisplay( dt.row( idx ), true );
700
+ } );
701
+ },
702
+
703
+
704
+ /**
705
+ * Alter the table display for a resized viewport. This involves first
706
+ * determining what breakpoint the window currently is in, getting the
707
+ * column visibilities to apply and then setting them.
708
+ *
709
+ * @private
710
+ */
711
+ _resize: function ()
712
+ {
713
+ var that = this;
714
+ var dt = this.s.dt;
715
+ var width = $(window).width();
716
+ var breakpoints = this.c.breakpoints;
717
+ var breakpoint = breakpoints[0].name;
718
+ var columns = this.s.columns;
719
+ var i, ien;
720
+ var oldVis = this.s.current.slice();
721
+
722
+ // Determine what breakpoint we are currently at
723
+ for ( i=breakpoints.length-1 ; i>=0 ; i-- ) {
724
+ if ( width <= breakpoints[i].width ) {
725
+ breakpoint = breakpoints[i].name;
726
+ break;
727
+ }
728
+ }
729
+
730
+ // Show the columns for that break point
731
+ var columnsVis = this._columnsVisiblity( breakpoint );
732
+ this.s.current = columnsVis;
733
+
734
+ // Set the class before the column visibility is changed so event
735
+ // listeners know what the state is. Need to determine if there are
736
+ // any columns that are not visible but can be shown
737
+ var collapsedClass = false;
738
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
739
+ if ( columnsVis[i] === false && ! columns[i].never && ! columns[i].control ) {
740
+ collapsedClass = true;
741
+ break;
742
+ }
743
+ }
744
+
745
+ $( dt.table().node() ).toggleClass( 'collapsed', collapsedClass );
746
+
747
+ var changed = false;
748
+
749
+ dt.columns().eq(0).each( function ( colIdx, i ) {
750
+ if ( columnsVis[i] !== oldVis[i] ) {
751
+ changed = true;
752
+ that._setColumnVis( colIdx, columnsVis[i] );
753
+ }
754
+ } );
755
+
756
+ if ( changed ) {
757
+ this._redrawChildren();
758
+
759
+ // Inform listeners of the change
760
+ $(dt.table().node()).trigger( 'responsive-resize.dt', [dt, this.s.current] );
761
+ }
762
+ },
763
+
764
+
765
+ /**
766
+ * Determine the width of each column in the table so the auto column hiding
767
+ * has that information to work with. This method is never going to be 100%
768
+ * perfect since column widths can change slightly per page, but without
769
+ * seriously compromising performance this is quite effective.
770
+ *
771
+ * @private
772
+ */
773
+ _resizeAuto: function ()
774
+ {
775
+ var dt = this.s.dt;
776
+ var columns = this.s.columns;
777
+
778
+ // Are we allowed to do auto sizing?
779
+ if ( ! this.c.auto ) {
780
+ return;
781
+ }
782
+
783
+ // Are there any columns that actually need auto-sizing, or do they all
784
+ // have classes defined
785
+ if ( $.inArray( true, $.map( columns, function (c) { return c.auto; } ) ) === -1 ) {
786
+ return;
787
+ }
788
+
789
+ // Clone the table with the current data in it
790
+ var tableWidth = dt.table().node().offsetWidth;
791
+ var columnWidths = dt.columns;
792
+ var clonedTable = dt.table().node().cloneNode( false );
793
+ var clonedHeader = $( dt.table().header().cloneNode( false ) ).appendTo( clonedTable );
794
+ var clonedBody = $( dt.table().body() ).clone( false, false ).empty().appendTo( clonedTable ); // use jQuery because of IE8
795
+
796
+ // Header
797
+ var headerCells = dt.columns()
798
+ .header()
799
+ .filter( function (idx) {
800
+ return dt.column(idx).visible();
801
+ } )
802
+ .to$()
803
+ .clone( false )
804
+ .css( 'display', 'table-cell' );
805
+
806
+ // Body rows - we don't need to take account of DataTables' column
807
+ // visibility since we implement our own here (hence the `display` set)
808
+ $(clonedBody)
809
+ .append( $(dt.rows( { page: 'current' } ).nodes()).clone( false ) )
810
+ .find( 'th, td' ).css( 'display', '' );
811
+
812
+ // Footer
813
+ var footer = dt.table().footer();
814
+ if ( footer ) {
815
+ var clonedFooter = $( footer.cloneNode( false ) ).appendTo( clonedTable );
816
+ var footerCells = dt.columns()
817
+ .footer()
818
+ .filter( function (idx) {
819
+ return dt.column(idx).visible();
820
+ } )
821
+ .to$()
822
+ .clone( false )
823
+ .css( 'display', 'table-cell' );
824
+
825
+ $('<tr/>')
826
+ .append( footerCells )
827
+ .appendTo( clonedFooter );
828
+ }
829
+
830
+ $('<tr/>')
831
+ .append( headerCells )
832
+ .appendTo( clonedHeader );
833
+
834
+ // In the inline case extra padding is applied to the first column to
835
+ // give space for the show / hide icon. We need to use this in the
836
+ // calculation
837
+ if ( this.c.details.type === 'inline' ) {
838
+ $(clonedTable).addClass( 'dtr-inline collapsed' );
839
+ }
840
+
841
+ // It is unsafe to insert elements with the same name into the DOM
842
+ // multiple times. For example, cloning and inserting a checked radio
843
+ // clears the chcecked state of the original radio.
844
+ $( clonedTable ).find( '[name]' ).removeAttr( 'name' );
845
+
846
+ var inserted = $('<div/>')
847
+ .css( {
848
+ width: 1,
849
+ height: 1,
850
+ overflow: 'hidden'
851
+ } )
852
+ .append( clonedTable );
853
+
854
+ inserted.insertBefore( dt.table().node() );
855
+
856
+ // The cloned header now contains the smallest that each column can be
857
+ headerCells.each( function (i) {
858
+ var idx = dt.column.index( 'fromVisible', i );
859
+ columns[ idx ].minWidth = this.offsetWidth || 0;
860
+ } );
861
+
862
+ inserted.remove();
863
+ },
864
+
865
+ /**
866
+ * Set a column's visibility.
867
+ *
868
+ * We don't use DataTables' column visibility controls in order to ensure
869
+ * that column visibility can Responsive can no-exist. Since only IE8+ is
870
+ * supported (and all evergreen browsers of course) the control of the
871
+ * display attribute works well.
872
+ *
873
+ * @param {integer} col Column index
874
+ * @param {boolean} showHide Show or hide (true or false)
875
+ * @private
876
+ */
877
+ _setColumnVis: function ( col, showHide )
878
+ {
879
+ var dt = this.s.dt;
880
+ var display = showHide ? '' : 'none'; // empty string will remove the attr
881
+
882
+ $( dt.column( col ).header() ).css( 'display', display );
883
+ $( dt.column( col ).footer() ).css( 'display', display );
884
+ dt.column( col ).nodes().to$().css( 'display', display );
885
+ },
886
+
887
+
888
+ /**
889
+ * Update the cell tab indexes for keyboard accessibility. This is called on
890
+ * every table draw - that is potentially inefficient, but also the least
891
+ * complex option given that column visibility can change on the fly. Its a
892
+ * shame user-focus was removed from CSS 3 UI, as it would have solved this
893
+ * issue with a single CSS statement.
894
+ *
895
+ * @private
896
+ */
897
+ _tabIndexes: function ()
898
+ {
899
+ var dt = this.s.dt;
900
+ var cells = dt.cells( { page: 'current' } ).nodes().to$();
901
+ var ctx = dt.settings()[0];
902
+ var target = this.c.details.target;
903
+
904
+ cells.filter( '[data-dtr-keyboard]' ).removeData( '[data-dtr-keyboard]' );
905
+
906
+ var selector = typeof target === 'number' ?
907
+ ':eq('+target+')' :
908
+ target;
909
+
910
+ $( selector, dt.rows( { page: 'current' } ).nodes() )
911
+ .attr( 'tabIndex', ctx.iTabIndex )
912
+ .data( 'dtr-keyboard', 1 );
913
+ }
914
+ } );
915
+
916
+
917
+ /**
918
+ * List of default breakpoints. Each item in the array is an object with two
919
+ * properties:
920
+ *
921
+ * * `name` - the breakpoint name.
922
+ * * `width` - the breakpoint width
923
+ *
924
+ * @name Responsive.breakpoints
925
+ * @static
926
+ */
927
+ Responsive.breakpoints = [
928
+ { name: 'desktop', width: Infinity },
929
+ { name: 'tablet-l', width: 1024 },
930
+ { name: 'tablet-p', width: 768 },
931
+ { name: 'mobile-l', width: 480 },
932
+ { name: 'mobile-p', width: 320 }
933
+ ];
934
+
935
+
936
+ /**
937
+ * Display methods - functions which define how the hidden data should be shown
938
+ * in the table.
939
+ *
940
+ * @namespace
941
+ * @name Responsive.defaults
942
+ * @static
943
+ */
944
+ Responsive.display = {
945
+ childRow: function ( row, update, render ) {
946
+ if ( update ) {
947
+ if ( $(row.node()).hasClass('parent') ) {
948
+ row.child( render(), 'child' ).show();
949
+
950
+ return true;
951
+ }
952
+ }
953
+ else {
954
+ if ( ! row.child.isShown() ) {
955
+ row.child( render(), 'child' ).show();
956
+ $( row.node() ).addClass( 'parent' );
957
+
958
+ return true;
959
+ }
960
+ else {
961
+ row.child( false );
962
+ $( row.node() ).removeClass( 'parent' );
963
+
964
+ return false;
965
+ }
966
+ }
967
+ },
968
+
969
+ childRowImmediate: function ( row, update, render ) {
970
+ if ( (! update && row.child.isShown()) || ! row.responsive.hasHidden() ) {
971
+ // User interaction and the row is show, or nothing to show
972
+ row.child( false );
973
+ $( row.node() ).removeClass( 'parent' );
974
+
975
+ return false;
976
+ }
977
+ else {
978
+ // Display
979
+ row.child( render(), 'child' ).show();
980
+ $( row.node() ).addClass( 'parent' );
981
+
982
+ return true;
983
+ }
984
+ },
985
+
986
+ // This is a wrapper so the modal options for Bootstrap and jQuery UI can
987
+ // have options passed into them. This specific one doesn't need to be a
988
+ // function but it is for consistency in the `modal` name
989
+ modal: function ( options ) {
990
+ return function ( row, update, render ) {
991
+ if ( ! update ) {
992
+ // Show a modal
993
+ var close = function () {
994
+ modal.remove(); // will tidy events for us
995
+ $(document).off( 'keypress.dtr' );
996
+ };
997
+
998
+ var modal = $('<div class="dtr-modal"/>')
999
+ .append( $('<div class="dtr-modal-display"/>')
1000
+ .append( $('<div class="dtr-modal-content"/>')
1001
+ .append( render() )
1002
+ )
1003
+ .append( $('<div class="dtr-modal-close">&times;</div>' )
1004
+ .click( function () {
1005
+ close();
1006
+ } )
1007
+ )
1008
+ )
1009
+ .append( $('<div class="dtr-modal-background"/>')
1010
+ .click( function () {
1011
+ close();
1012
+ } )
1013
+ )
1014
+ .appendTo( 'body' );
1015
+
1016
+ $(document).on( 'keyup.dtr', function (e) {
1017
+ if ( e.keyCode === 27 ) {
1018
+ e.stopPropagation();
1019
+
1020
+ close();
1021
+ }
1022
+ } );
1023
+ }
1024
+ else {
1025
+ $('div.dtr-modal-content')
1026
+ .empty()
1027
+ .append( render() );
1028
+ }
1029
+
1030
+ if ( options && options.header ) {
1031
+ $('div.dtr-modal-content').prepend(
1032
+ '<h2>'+options.header( row )+'</h2>'
1033
+ );
1034
+ }
1035
+ };
1036
+ }
1037
+ };
1038
+
1039
+
1040
+ /**
1041
+ * Display methods - functions which define how the hidden data should be shown
1042
+ * in the table.
1043
+ *
1044
+ * @namespace
1045
+ * @name Responsive.defaults
1046
+ * @static
1047
+ */
1048
+ Responsive.renderer = {
1049
+ listHidden: function () {
1050
+ return function ( api, rowIdx, columns ) {
1051
+ var data = $.map( columns, function ( col ) {
1052
+ return col.hidden ?
1053
+ '<li data-dtr-index="'+col.columnIndex+'" data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
1054
+ '<span class="dtr-title">'+
1055
+ col.title+
1056
+ '</span> '+
1057
+ '<span class="dtr-data">'+
1058
+ col.data+
1059
+ '</span>'+
1060
+ '</li>' :
1061
+ '';
1062
+ } ).join('');
1063
+
1064
+ return data ?
1065
+ $('<ul data-dtr-index="'+rowIdx+'"/>').append( data ) :
1066
+ false;
1067
+ }
1068
+ },
1069
+
1070
+ tableAll: function ( options ) {
1071
+ options = $.extend( {
1072
+ tableClass: ''
1073
+ }, options );
1074
+
1075
+ return function ( api, rowIdx, columns ) {
1076
+ var data = $.map( columns, function ( col ) {
1077
+ return '<tr data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
1078
+ '<td>'+col.title+':'+'</td> '+
1079
+ '<td>'+col.data+'</td>'+
1080
+ '</tr>';
1081
+ } ).join('');
1082
+
1083
+ return $('<table class="'+options.tableClass+'" width="100%"/>').append( data );
1084
+ }
1085
+ }
1086
+ };
1087
+
1088
+ /**
1089
+ * Responsive default settings for initialisation
1090
+ *
1091
+ * @namespace
1092
+ * @name Responsive.defaults
1093
+ * @static
1094
+ */
1095
+ Responsive.defaults = {
1096
+ /**
1097
+ * List of breakpoints for the instance. Note that this means that each
1098
+ * instance can have its own breakpoints. Additionally, the breakpoints
1099
+ * cannot be changed once an instance has been creased.
1100
+ *
1101
+ * @type {Array}
1102
+ * @default Takes the value of `Responsive.breakpoints`
1103
+ */
1104
+ breakpoints: Responsive.breakpoints,
1105
+
1106
+ /**
1107
+ * Enable / disable auto hiding calculations. It can help to increase
1108
+ * performance slightly if you disable this option, but all columns would
1109
+ * need to have breakpoint classes assigned to them
1110
+ *
1111
+ * @type {Boolean}
1112
+ * @default `true`
1113
+ */
1114
+ auto: true,
1115
+
1116
+ /**
1117
+ * Details control. If given as a string value, the `type` property of the
1118
+ * default object is set to that value, and the defaults used for the rest
1119
+ * of the object - this is for ease of implementation.
1120
+ *
1121
+ * The object consists of the following properties:
1122
+ *
1123
+ * * `display` - A function that is used to show and hide the hidden details
1124
+ * * `renderer` - function that is called for display of the child row data.
1125
+ * The default function will show the data from the hidden columns
1126
+ * * `target` - Used as the selector for what objects to attach the child
1127
+ * open / close to
1128
+ * * `type` - `false` to disable the details display, `inline` or `column`
1129
+ * for the two control types
1130
+ *
1131
+ * @type {Object|string}
1132
+ */
1133
+ details: {
1134
+ display: Responsive.display.childRow,
1135
+
1136
+ renderer: Responsive.renderer.listHidden(),
1137
+
1138
+ target: 0,
1139
+
1140
+ type: 'inline'
1141
+ },
1142
+
1143
+ /**
1144
+ * Orthogonal data request option. This is used to define the data type
1145
+ * requested when Responsive gets the data to show in the child row.
1146
+ *
1147
+ * @type {String}
1148
+ */
1149
+ orthogonal: 'display'
1150
+ };
1151
+
1152
+
1153
+ /*
1154
+ * API
1155
+ */
1156
+ var Api = $.fn.dataTable.Api;
1157
+
1158
+ // Doesn't do anything - work around for a bug in DT... Not documented
1159
+ Api.register( 'responsive()', function () {
1160
+ return this;
1161
+ } );
1162
+
1163
+ Api.register( 'responsive.index()', function ( li ) {
1164
+ li = $(li);
1165
+
1166
+ return {
1167
+ column: li.data('dtr-index'),
1168
+ row: li.parent().data('dtr-index')
1169
+ };
1170
+ } );
1171
+
1172
+ Api.register( 'responsive.rebuild()', function () {
1173
+ return this.iterator( 'table', function ( ctx ) {
1174
+ if ( ctx._responsive ) {
1175
+ ctx._responsive._classLogic();
1176
+ }
1177
+ } );
1178
+ } );
1179
+
1180
+ Api.register( 'responsive.recalc()', function () {
1181
+ return this.iterator( 'table', function ( ctx ) {
1182
+ if ( ctx._responsive ) {
1183
+ ctx._responsive._resizeAuto();
1184
+ ctx._responsive._resize();
1185
+ }
1186
+ } );
1187
+ } );
1188
+
1189
+ Api.register( 'responsive.hasHidden()', function () {
1190
+ var ctx = this.context[0];
1191
+
1192
+ return ctx._responsive ?
1193
+ $.inArray( false, ctx._responsive.s.current ) !== -1 :
1194
+ false;
1195
+ } );
1196
+
1197
+
1198
+ /**
1199
+ * Version information
1200
+ *
1201
+ * @name Responsive.version
1202
+ * @static
1203
+ */
1204
+ Responsive.version = '2.1.0';
1205
+
1206
+
1207
+ $.fn.dataTable.Responsive = Responsive;
1208
+ $.fn.DataTable.Responsive = Responsive;
1209
+
1210
+ // Attach a listener to the document which listens for DataTables initialisation
1211
+ // events so we can automatically initialise
1212
+ $(document).on( 'preInit.dt.dtr', function (e, settings, json) {
1213
+ if ( e.namespace !== 'dt' ) {
1214
+ return;
1215
+ }
1216
+
1217
+ if ( $(settings.nTable).hasClass( 'responsive' ) ||
1218
+ $(settings.nTable).hasClass( 'dt-responsive' ) ||
1219
+ settings.oInit.responsive ||
1220
+ DataTable.defaults.responsive
1221
+ ) {
1222
+ var init = settings.oInit.responsive;
1223
+
1224
+ if ( init !== false ) {
1225
+ new Responsive( settings, $.isPlainObject( init ) ? init : {} );
1226
+ }
1227
+ }
1228
+ } );
1229
+
1230
+
1231
+ return Responsive;
1232
+ }));
common/vendor/datatables/extensions/Responsive/js/dataTables.responsive.min.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ Responsive 2.1.0
3
+ 2014-2016 SpryMedia Ltd - datatables.net/license
4
+ */
5
+ (function(c){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(l){return c(l,window,document)}):"object"===typeof exports?module.exports=function(l,k){l||(l=window);if(!k||!k.fn.dataTable)k=require("datatables.net")(l,k).$;return c(k,l,l.document)}:c(jQuery,window,document)})(function(c,l,k,p){var m=c.fn.dataTable,j=function(a,b){if(!m.versionCheck||!m.versionCheck("1.10.3"))throw"DataTables Responsive requires DataTables 1.10.3 or newer";this.s={dt:new m.Api(a),columns:[],
6
+ current:[]};this.s.dt.settings()[0].responsive||(b&&"string"===typeof b.details?b.details={type:b.details}:b&&!1===b.details?b.details={type:!1}:b&&!0===b.details&&(b.details={type:"inline"}),this.c=c.extend(!0,{},j.defaults,m.defaults.responsive,b),a.responsive=this,this._constructor())};c.extend(j.prototype,{_constructor:function(){var a=this,b=this.s.dt,d=b.settings()[0],e=c(l).width();b.settings()[0]._responsive=this;c(l).on("resize.dtr orientationchange.dtr",m.util.throttle(function(){var b=
7
+ c(l).width();b!==e&&(a._resize(),e=b)}));d.oApi._fnCallbackReg(d,"aoRowCreatedCallback",function(e){-1!==c.inArray(!1,a.s.current)&&c("td, th",e).each(function(e){e=b.column.index("toData",e);!1===a.s.current[e]&&c(this).css("display","none")})});b.on("destroy.dtr",function(){b.off(".dtr");c(b.table().body()).off(".dtr");c(l).off("resize.dtr orientationchange.dtr");c.each(a.s.current,function(b,e){!1===e&&a._setColumnVis(b,!0)})});this.c.breakpoints.sort(function(a,b){return a.width<b.width?1:a.width>
8
+ b.width?-1:0});this._classLogic();this._resizeAuto();d=this.c.details;!1!==d.type&&(a._detailsInit(),b.on("column-visibility.dtr",function(){a._classLogic();a._resizeAuto();a._resize()}),b.on("draw.dtr",function(){a._redrawChildren()}),c(b.table().node()).addClass("dtr-"+d.type));b.on("column-reorder.dtr",function(){a._classLogic();a._resizeAuto();a._resize()});b.on("column-sizing.dtr",function(){a._resizeAuto();a._resize()});b.on("init.dtr",function(){a._resizeAuto();a._resize();c.inArray(false,
9
+ a.s.current)&&b.columns.adjust()});this._resize()},_columnsVisiblity:function(a){var b=this.s.dt,d=this.s.columns,e,f,g=d.map(function(a,b){return{columnIdx:b,priority:a.priority}}).sort(function(a,b){return a.priority!==b.priority?a.priority-b.priority:a.columnIdx-b.columnIdx}),h=c.map(d,function(b){return b.auto&&null===b.minWidth?!1:!0===b.auto?"-":-1!==c.inArray(a,b.includeIn)}),n=0;e=0;for(f=h.length;e<f;e++)!0===h[e]&&(n+=d[e].minWidth);e=b.settings()[0].oScroll;e=e.sY||e.sX?e.iBarWidth:0;b=
10
+ b.table().container().offsetWidth-e-n;e=0;for(f=h.length;e<f;e++)d[e].control&&(b-=d[e].minWidth);n=!1;e=0;for(f=g.length;e<f;e++){var i=g[e].columnIdx;"-"===h[i]&&(!d[i].control&&d[i].minWidth)&&(n||0>b-d[i].minWidth?(n=!0,h[i]=!1):h[i]=!0,b-=d[i].minWidth)}g=!1;e=0;for(f=d.length;e<f;e++)if(!d[e].control&&!d[e].never&&!h[e]){g=!0;break}e=0;for(f=d.length;e<f;e++)d[e].control&&(h[e]=g);-1===c.inArray(!0,h)&&(h[0]=!0);return h},_classLogic:function(){var a=this,b=this.c.breakpoints,d=this.s.dt,e=
11
+ d.columns().eq(0).map(function(a){var b=this.column(a),e=b.header().className,a=d.settings()[0].aoColumns[a].responsivePriority;a===p&&(b=c(b.header()).data("priority"),a=b!==p?1*b:1E4);return{className:e,includeIn:[],auto:!1,control:!1,never:e.match(/\bnever\b/)?!0:!1,priority:a}}),f=function(a,b){var d=e[a].includeIn;-1===c.inArray(b,d)&&d.push(b)},g=function(c,d,i,g){if(i)if("max-"===i){g=a._find(d).width;d=0;for(i=b.length;d<i;d++)b[d].width<=g&&f(c,b[d].name)}else if("min-"===i){g=a._find(d).width;
12
+ d=0;for(i=b.length;d<i;d++)b[d].width>=g&&f(c,b[d].name)}else{if("not-"===i){d=0;for(i=b.length;d<i;d++)-1===b[d].name.indexOf(g)&&f(c,b[d].name)}}else e[c].includeIn.push(d)};e.each(function(a,e){for(var d=a.className.split(" "),f=!1,j=0,l=d.length;j<l;j++){var k=c.trim(d[j]);if("all"===k){f=!0;a.includeIn=c.map(b,function(a){return a.name});return}if("none"===k||a.never){f=!0;return}if("control"===k){f=!0;a.control=!0;return}c.each(b,function(a,b){var d=b.name.split("-"),c=k.match(RegExp("(min\\-|max\\-|not\\-)?("+
13
+ d[0]+")(\\-[_a-zA-Z0-9])?"));c&&(f=!0,c[2]===d[0]&&c[3]==="-"+d[1]?g(e,b.name,c[1],c[2]+c[3]):c[2]===d[0]&&!c[3]&&g(e,b.name,c[1],c[2]))})}f||(a.auto=!0)});this.s.columns=e},_detailsDisplay:function(a,b){var d=this,e=this.s.dt,f=this.c.details;if(f&&!1!==f.type){var g=f.display(a,b,function(){return f.renderer(e,a[0],d._detailsObj(a[0]))});(!0===g||!1===g)&&c(e.table().node()).triggerHandler("responsive-display.dt",[e,a,g,b])}},_detailsInit:function(){var a=this,b=this.s.dt,d=this.c.details;"inline"===
14
+ d.type&&(d.target="td:first-child, th:first-child");b.on("draw.dtr",function(){a._tabIndexes()});a._tabIndexes();c(b.table().body()).on("keyup.dtr","td, th",function(a){a.keyCode===13&&c(this).data("dtr-keyboard")&&c(this).click()});var e=d.target;c(b.table().body()).on("click.dtr mousedown.dtr mouseup.dtr","string"===typeof e?e:"td, th",function(d){if(c(b.table().node()).hasClass("collapsed")&&b.row(c(this).closest("tr")).length){if(typeof e==="number"){var g=e<0?b.columns().eq(0).length+e:e;if(b.cell(this).index().column!==
15
+ g)return}g=b.row(c(this).closest("tr"));d.type==="click"?a._detailsDisplay(g,false):d.type==="mousedown"?c(this).css("outline","none"):d.type==="mouseup"&&c(this).blur().css("outline","")}})},_detailsObj:function(a){var b=this,d=this.s.dt;return c.map(this.s.columns,function(e,c){if(!e.never&&!e.control)return{title:d.settings()[0].aoColumns[c].sTitle,data:d.cell(a,c).render(b.c.orthogonal),hidden:d.column(c).visible()&&!b.s.current[c],columnIndex:c,rowIndex:a}})},_find:function(a){for(var b=this.c.breakpoints,
16
+ d=0,c=b.length;d<c;d++)if(b[d].name===a)return b[d]},_redrawChildren:function(){var a=this,b=this.s.dt;b.rows({page:"current"}).iterator("row",function(c,e){b.row(e);a._detailsDisplay(b.row(e),!0)})},_resize:function(){var a=this,b=this.s.dt,d=c(l).width(),e=this.c.breakpoints,f=e[0].name,g=this.s.columns,h,j=this.s.current.slice();for(h=e.length-1;0<=h;h--)if(d<=e[h].width){f=e[h].name;break}var i=this._columnsVisiblity(f);this.s.current=i;e=!1;h=0;for(d=g.length;h<d;h++)if(!1===i[h]&&!g[h].never&&
17
+ !g[h].control){e=!0;break}c(b.table().node()).toggleClass("collapsed",e);var k=!1;b.columns().eq(0).each(function(b,c){i[c]!==j[c]&&(k=!0,a._setColumnVis(b,i[c]))});k&&(this._redrawChildren(),c(b.table().node()).trigger("responsive-resize.dt",[b,this.s.current]))},_resizeAuto:function(){var a=this.s.dt,b=this.s.columns;if(this.c.auto&&-1!==c.inArray(!0,c.map(b,function(a){return a.auto}))){a.table().node();var d=a.table().node().cloneNode(!1),e=c(a.table().header().cloneNode(!1)).appendTo(d),f=c(a.table().body()).clone(!1,
18
+ !1).empty().appendTo(d),g=a.columns().header().filter(function(b){return a.column(b).visible()}).to$().clone(!1).css("display","table-cell");c(f).append(c(a.rows({page:"current"}).nodes()).clone(!1)).find("th, td").css("display","");if(f=a.table().footer()){var f=c(f.cloneNode(!1)).appendTo(d),h=a.columns().footer().filter(function(b){return a.column(b).visible()}).to$().clone(!1).css("display","table-cell");c("<tr/>").append(h).appendTo(f)}c("<tr/>").append(g).appendTo(e);"inline"===this.c.details.type&&
19
+ c(d).addClass("dtr-inline collapsed");c(d).find("[name]").removeAttr("name");d=c("<div/>").css({width:1,height:1,overflow:"hidden"}).append(d);d.insertBefore(a.table().node());g.each(function(c){c=a.column.index("fromVisible",c);b[c].minWidth=this.offsetWidth||0});d.remove()}},_setColumnVis:function(a,b){var d=this.s.dt,e=b?"":"none";c(d.column(a).header()).css("display",e);c(d.column(a).footer()).css("display",e);d.column(a).nodes().to$().css("display",e)},_tabIndexes:function(){var a=this.s.dt,
20
+ b=a.cells({page:"current"}).nodes().to$(),d=a.settings()[0],e=this.c.details.target;b.filter("[data-dtr-keyboard]").removeData("[data-dtr-keyboard]");c("number"===typeof e?":eq("+e+")":e,a.rows({page:"current"}).nodes()).attr("tabIndex",d.iTabIndex).data("dtr-keyboard",1)}});j.breakpoints=[{name:"desktop",width:Infinity},{name:"tablet-l",width:1024},{name:"tablet-p",width:768},{name:"mobile-l",width:480},{name:"mobile-p",width:320}];j.display={childRow:function(a,b,d){if(b){if(c(a.node()).hasClass("parent"))return a.child(d(),
21
+ "child").show(),!0}else{if(a.child.isShown())return a.child(!1),c(a.node()).removeClass("parent"),!1;a.child(d(),"child").show();c(a.node()).addClass("parent");return!0}},childRowImmediate:function(a,b,d){if(!b&&a.child.isShown()||!a.responsive.hasHidden())return a.child(!1),c(a.node()).removeClass("parent"),!1;a.child(d(),"child").show();c(a.node()).addClass("parent");return!0},modal:function(a){return function(b,d,e){if(d)c("div.dtr-modal-content").empty().append(e());else{var f=function(){g.remove();
22
+ c(k).off("keypress.dtr")},g=c('<div class="dtr-modal"/>').append(c('<div class="dtr-modal-display"/>').append(c('<div class="dtr-modal-content"/>').append(e())).append(c('<div class="dtr-modal-close">&times;</div>').click(function(){f()}))).append(c('<div class="dtr-modal-background"/>').click(function(){f()})).appendTo("body");c(k).on("keyup.dtr",function(a){27===a.keyCode&&(a.stopPropagation(),f())})}a&&a.header&&c("div.dtr-modal-content").prepend("<h2>"+a.header(b)+"</h2>")}}};j.renderer={listHidden:function(){return function(a,
23
+ b,d){return(a=c.map(d,function(a){return a.hidden?'<li data-dtr-index="'+a.columnIndex+'" data-dt-row="'+a.rowIndex+'" data-dt-column="'+a.columnIndex+'"><span class="dtr-title">'+a.title+'</span> <span class="dtr-data">'+a.data+"</span></li>":""}).join(""))?c('<ul data-dtr-index="'+b+'"/>').append(a):!1}},tableAll:function(a){a=c.extend({tableClass:""},a);return function(b,d,e){b=c.map(e,function(a){return'<tr data-dt-row="'+a.rowIndex+'" data-dt-column="'+a.columnIndex+'"><td>'+a.title+":</td> <td>"+
24
+ a.data+"</td></tr>"}).join("");return c('<table class="'+a.tableClass+'" width="100%"/>').append(b)}}};j.defaults={breakpoints:j.breakpoints,auto:!0,details:{display:j.display.childRow,renderer:j.renderer.listHidden(),target:0,type:"inline"},orthogonal:"display"};var o=c.fn.dataTable.Api;o.register("responsive()",function(){return this});o.register("responsive.index()",function(a){a=c(a);return{column:a.data("dtr-index"),row:a.parent().data("dtr-index")}});o.register("responsive.rebuild()",function(){return this.iterator("table",
25
+ function(a){a._responsive&&a._responsive._classLogic()})});o.register("responsive.recalc()",function(){return this.iterator("table",function(a){a._responsive&&(a._responsive._resizeAuto(),a._responsive._resize())})});o.register("responsive.hasHidden()",function(){var a=this.context[0];return a._responsive?-1!==c.inArray(!1,a._responsive.s.current):!1});j.version="2.1.0";c.fn.dataTable.Responsive=j;c.fn.DataTable.Responsive=j;c(k).on("preInit.dt.dtr",function(a,b){if("dt"===a.namespace&&(c(b.nTable).hasClass("responsive")||
26
+ c(b.nTable).hasClass("dt-responsive")||b.oInit.responsive||m.defaults.responsive)){var d=b.oInit.responsive;!1!==d&&new j(b,c.isPlainObject(d)?d:{})}});return j});
common/vendor/datatables/extensions/Scroller/css/scroller.dataTables.css ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ div.DTS {
2
+ display: block !important;
3
+ }
4
+ div.DTS tbody th,
5
+ div.DTS tbody td {
6
+ white-space: nowrap;
7
+ }
8
+ div.DTS div.DTS_Loading {
9
+ z-index: 1;
10
+ }
11
+ div.DTS div.dataTables_scrollBody {
12
+ background: repeating-linear-gradient(45deg, #edeeff, #edeeff 10px, white 10px, white 20px);
13
+ }
14
+ div.DTS div.dataTables_scrollBody table {
15
+ z-index: 2;
16
+ }
17
+ div.DTS div.dataTables_paginate,
18
+ div.DTS div.dataTables_length {
19
+ display: none;
20
+ }
common/vendor/datatables/extensions/Scroller/css/scroller.dataTables.min.css ADDED
@@ -0,0 +1 @@
 
1
+ div.DTS{display:block !important}div.DTS tbody th,div.DTS tbody td{white-space:nowrap}div.DTS div.DTS_Loading{z-index:1}div.DTS div.dataTables_scrollBody{background:repeating-linear-gradient(45deg, #edeeff, #edeeff 10px, #fff 10px, #fff 20px)}div.DTS div.dataTables_scrollBody table{z-index:2}div.DTS div.dataTables_paginate,div.DTS div.dataTables_length{display:none}
common/vendor/datatables/extensions/Scroller/js/dataTables.scroller.js ADDED
@@ -0,0 +1,1349 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*! Scroller 1.4.2
2
+ * ©2011-2016 SpryMedia Ltd - datatables.net/license
3
+ */
4
+
5
+ /**
6
+ * @summary Scroller
7
+ * @description Virtual rendering for DataTables
8
+ * @version 1.4.2
9
+ * @file dataTables.scroller.js
10
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
11
+ * @contact www.sprymedia.co.uk/contact
12
+ * @copyright Copyright 2011-2016 SpryMedia Ltd.
13
+ *
14
+ * This source file is free software, available under the following license:
15
+ * MIT license - http://datatables.net/license/mit
16
+ *
17
+ * This source file is distributed in the hope that it will be useful, but
18
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
20
+ *
21
+ * For details please refer to: http://www.datatables.net
22
+ */
23
+
24
+ (function( factory ){
25
+ if ( typeof define === 'function' && define.amd ) {
26
+ // AMD
27
+ define( ['jquery', 'datatables.net'], function ( $ ) {
28
+ return factory( $, window, document );
29
+ } );
30
+ }
31
+ else if ( typeof exports === 'object' ) {
32
+ // CommonJS
33
+ module.exports = function (root, $) {
34
+ if ( ! root ) {
35
+ root = window;
36
+ }
37
+
38
+ if ( ! $ || ! $.fn.dataTable ) {
39
+ $ = require('datatables.net')(root, $).$;
40
+ }
41
+
42
+ return factory( $, root, root.document );
43
+ };
44
+ }
45
+ else {
46
+ // Browser
47
+ factory( jQuery, window, document );
48
+ }
49
+ }(function( $, window, document, undefined ) {
50
+ 'use strict';
51
+ var DataTable = $.fn.dataTable;
52
+
53
+
54
+ /**
55
+ * Scroller is a virtual rendering plug-in for DataTables which allows large
56
+ * datasets to be drawn on screen every quickly. What the virtual rendering means
57
+ * is that only the visible portion of the table (and a bit to either side to make
58
+ * the scrolling smooth) is drawn, while the scrolling container gives the
59
+ * visual impression that the whole table is visible. This is done by making use
60
+ * of the pagination abilities of DataTables and moving the table around in the
61
+ * scrolling container DataTables adds to the page. The scrolling container is
62
+ * forced to the height it would be for the full table display using an extra
63
+ * element.
64
+ *
65
+ * Note that rows in the table MUST all be the same height. Information in a cell
66
+ * which expands on to multiple lines will cause some odd behaviour in the scrolling.
67
+ *
68
+ * Scroller is initialised by simply including the letter 'S' in the sDom for the
69
+ * table you want to have this feature enabled on. Note that the 'S' must come
70
+ * AFTER the 't' parameter in `dom`.
71
+ *
72
+ * Key features include:
73
+ * <ul class="limit_length">
74
+ * <li>Speed! The aim of Scroller for DataTables is to make rendering large data sets fast</li>
75
+ * <li>Full compatibility with deferred rendering in DataTables for maximum speed</li>
76
+ * <li>Display millions of rows</li>
77
+ * <li>Integration with state saving in DataTables (scrolling position is saved)</li>
78
+ * <li>Easy to use</li>
79
+ * </ul>
80
+ *
81
+ * @class
82
+ * @constructor
83
+ * @global
84
+ * @param {object} dt DataTables settings object or API instance
85
+ * @param {object} [opts={}] Configuration object for FixedColumns. Options
86
+ * are defined by {@link Scroller.defaults}
87
+ *
88
+ * @requires jQuery 1.7+
89
+ * @requires DataTables 1.10.0+
90
+ *
91
+ * @example
92
+ * $(document).ready(function() {
93
+ * $('#example').DataTable( {
94
+ * "scrollY": "200px",
95
+ * "ajax": "media/dataset/large.txt",
96
+ * "dom": "frtiS",
97
+ * "deferRender": true
98
+ * } );
99
+ * } );
100
+ */
101
+ var Scroller = function ( dt, opts ) {
102
+ /* Sanity check - you just know it will happen */
103
+ if ( ! (this instanceof Scroller) ) {
104
+ alert( "Scroller warning: Scroller must be initialised with the 'new' keyword." );
105
+ return;
106
+ }
107
+
108
+ if ( opts === undefined ) {
109
+ opts = {};
110
+ }
111
+
112
+ /**
113
+ * Settings object which contains customisable information for the Scroller instance
114
+ * @namespace
115
+ * @private
116
+ * @extends Scroller.defaults
117
+ */
118
+ this.s = {
119
+ /**
120
+ * DataTables settings object
121
+ * @type object
122
+ * @default Passed in as first parameter to constructor
123
+ */
124
+ "dt": $.fn.dataTable.Api( dt ).settings()[0],
125
+
126
+ /**
127
+ * Pixel location of the top of the drawn table in the viewport
128
+ * @type int
129
+ * @default 0
130
+ */
131
+ "tableTop": 0,
132
+
133
+ /**
134
+ * Pixel location of the bottom of the drawn table in the viewport
135
+ * @type int
136
+ * @default 0
137
+ */
138
+ "tableBottom": 0,
139
+
140
+ /**
141
+ * Pixel location of the boundary for when the next data set should be loaded and drawn
142
+ * when scrolling up the way.
143
+ * @type int
144
+ * @default 0
145
+ * @private
146
+ */
147
+ "redrawTop": 0,
148
+
149
+ /**
150
+ * Pixel location of the boundary for when the next data set should be loaded and drawn
151
+ * when scrolling down the way. Note that this is actually calculated as the offset from
152
+ * the top.
153
+ * @type int
154
+ * @default 0
155
+ * @private
156
+ */
157
+ "redrawBottom": 0,
158
+
159
+ /**
160
+ * Auto row height or not indicator
161
+ * @type bool
162
+ * @default 0
163
+ */
164
+ "autoHeight": true,
165
+
166
+ /**
167
+ * Number of rows calculated as visible in the visible viewport
168
+ * @type int
169
+ * @default 0
170
+ */
171
+ "viewportRows": 0,
172
+
173
+ /**
174
+ * setTimeout reference for state saving, used when state saving is enabled in the DataTable
175
+ * and when the user scrolls the viewport in order to stop the cookie set taking too much
176
+ * CPU!
177
+ * @type int
178
+ * @default 0
179
+ */
180
+ "stateTO": null,
181
+
182
+ /**
183
+ * setTimeout reference for the redraw, used when server-side processing is enabled in the
184
+ * DataTables in order to prevent DoSing the server
185
+ * @type int
186
+ * @default null
187
+ */
188
+ "drawTO": null,
189
+
190
+ heights: {
191
+ jump: null,
192
+ page: null,
193
+ virtual: null,
194
+ scroll: null,
195
+
196
+ /**
197
+ * Height of rows in the table
198
+ * @type int
199
+ * @default 0
200
+ */
201
+ row: null,
202
+
203
+ /**
204
+ * Pixel height of the viewport
205
+ * @type int
206
+ * @default 0
207
+ */
208
+ viewport: null
209
+ },
210
+
211
+ topRowFloat: 0,
212
+ scrollDrawDiff: null,
213
+ loaderVisible: false
214
+ };
215
+
216
+ // @todo The defaults should extend a `c` property and the internal settings
217
+ // only held in the `s` property. At the moment they are mixed
218
+ this.s = $.extend( this.s, Scroller.oDefaults, opts );
219
+
220
+ // Workaround for row height being read from height object (see above comment)
221
+ this.s.heights.row = this.s.rowHeight;
222
+
223
+ /**
224
+ * DOM elements used by the class instance
225
+ * @private
226
+ * @namespace
227
+ *
228
+ */
229
+ this.dom = {
230
+ "force": document.createElement('div'),
231
+ "scroller": null,
232
+ "table": null,
233
+ "loader": null
234
+ };
235
+
236
+ // Attach the instance to the DataTables instance so it can be accessed in
237
+ // future. Don't initialise Scroller twice on the same table
238
+ if ( this.s.dt.oScroller ) {
239
+ return;
240
+ }
241
+
242
+ this.s.dt.oScroller = this;
243
+
244
+ /* Let's do it */
245
+ this._fnConstruct();
246
+ };
247
+
248
+
249
+
250
+ $.extend( Scroller.prototype, {
251
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
252
+ * Public methods
253
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
254
+
255
+ /**
256
+ * Calculate the pixel position from the top of the scrolling container for
257
+ * a given row
258
+ * @param {int} iRow Row number to calculate the position of
259
+ * @returns {int} Pixels
260
+ * @example
261
+ * $(document).ready(function() {
262
+ * $('#example').dataTable( {
263
+ * "sScrollY": "200px",
264
+ * "sAjaxSource": "media/dataset/large.txt",
265
+ * "sDom": "frtiS",
266
+ * "bDeferRender": true,
267
+ * "fnInitComplete": function (o) {
268
+ * // Find where row 25 is
269
+ * alert( o.oScroller.fnRowToPixels( 25 ) );
270
+ * }
271
+ * } );
272
+ * } );
273
+ */
274
+ "fnRowToPixels": function ( rowIdx, intParse, virtual )
275
+ {
276
+ var pixels;
277
+
278
+ if ( virtual ) {
279
+ pixels = this._domain( 'virtualToPhysical', rowIdx * this.s.heights.row );
280
+ }
281
+ else {
282
+ var diff = rowIdx - this.s.baseRowTop;
283
+ pixels = this.s.baseScrollTop + (diff * this.s.heights.row);
284
+ }
285
+
286
+ return intParse || intParse === undefined ?
287
+ parseInt( pixels, 10 ) :
288
+ pixels;
289
+ },
290
+
291
+
292
+ /**
293
+ * Calculate the row number that will be found at the given pixel position
294
+ * (y-scroll).
295
+ *
296
+ * Please note that when the height of the full table exceeds 1 million
297
+ * pixels, Scroller switches into a non-linear mode for the scrollbar to fit
298
+ * all of the records into a finite area, but this function returns a linear
299
+ * value (relative to the last non-linear positioning).
300
+ * @param {int} iPixels Offset from top to calculate the row number of
301
+ * @param {int} [intParse=true] If an integer value should be returned
302
+ * @param {int} [virtual=false] Perform the calculations in the virtual domain
303
+ * @returns {int} Row index
304
+ * @example
305
+ * $(document).ready(function() {
306
+ * $('#example').dataTable( {
307
+ * "sScrollY": "200px",
308
+ * "sAjaxSource": "media/dataset/large.txt",
309
+ * "sDom": "frtiS",
310
+ * "bDeferRender": true,
311
+ * "fnInitComplete": function (o) {
312
+ * // Find what row number is at 500px
313
+ * alert( o.oScroller.fnPixelsToRow( 500 ) );
314
+ * }
315
+ * } );
316
+ * } );
317
+ */
318
+ "fnPixelsToRow": function ( pixels, intParse, virtual )
319
+ {
320
+ var diff = pixels - this.s.baseScrollTop;
321
+ var row = virtual ?
322
+ this._domain( 'physicalToVirtual', pixels ) / this.s.heights.row :
323
+ ( diff / this.s.heights.row ) + this.s.baseRowTop;
324
+
325
+ return intParse || intParse === undefined ?
326
+ parseInt( row, 10 ) :
327
+ row;
328
+ },
329
+
330
+
331
+ /**
332
+ * Calculate the row number that will be found at the given pixel position (y-scroll)
333
+ * @param {int} iRow Row index to scroll to
334
+ * @param {bool} [bAnimate=true] Animate the transition or not
335
+ * @returns {void}
336
+ * @example
337
+ * $(document).ready(function() {
338
+ * $('#example').dataTable( {
339
+ * "sScrollY": "200px",
340
+ * "sAjaxSource": "media/dataset/large.txt",
341
+ * "sDom": "frtiS",
342
+ * "bDeferRender": true,
343
+ * "fnInitComplete": function (o) {
344
+ * // Immediately scroll to row 1000
345
+ * o.oScroller.fnScrollToRow( 1000 );
346
+ * }
347
+ * } );
348
+ *
349
+ * // Sometime later on use the following to scroll to row 500...
350
+ * var oSettings = $('#example').dataTable().fnSettings();
351
+ * oSettings.oScroller.fnScrollToRow( 500 );
352
+ * } );
353
+ */
354
+ "fnScrollToRow": function ( iRow, bAnimate )
355
+ {
356
+ var that = this;
357
+ var ani = false;
358
+ var px = this.fnRowToPixels( iRow );
359
+
360
+ // We need to know if the table will redraw or not before doing the
361
+ // scroll. If it will not redraw, then we need to use the currently
362
+ // displayed table, and scroll with the physical pixels. Otherwise, we
363
+ // need to calculate the table's new position from the virtual
364
+ // transform.
365
+ var preRows = ((this.s.displayBuffer-1)/2) * this.s.viewportRows;
366
+ var drawRow = iRow - preRows;
367
+ if ( drawRow < 0 ) {
368
+ drawRow = 0;
369
+ }
370
+
371
+ if ( (px > this.s.redrawBottom || px < this.s.redrawTop) && this.s.dt._iDisplayStart !== drawRow ) {
372
+ ani = true;
373
+ px = this.fnRowToPixels( iRow, false, true );
374
+ }
375
+
376
+ if ( typeof bAnimate == 'undefined' || bAnimate )
377
+ {
378
+ this.s.ani = ani;
379
+ $(this.dom.scroller).animate( {
380
+ "scrollTop": px
381
+ }, function () {
382
+ // This needs to happen after the animation has completed and
383
+ // the final scroll event fired
384
+ setTimeout( function () {
385
+ that.s.ani = false;
386
+ }, 25 );
387
+ } );
388
+ }
389
+ else
390
+ {
391
+ $(this.dom.scroller).scrollTop( px );
392
+ }
393
+ },
394
+
395
+
396
+ /**
397
+ * Calculate and store information about how many rows are to be displayed
398
+ * in the scrolling viewport, based on current dimensions in the browser's
399
+ * rendering. This can be particularly useful if the table is initially
400
+ * drawn in a hidden element - for example in a tab.
401
+ * @param {bool} [bRedraw=true] Redraw the table automatically after the recalculation, with
402
+ * the new dimensions forming the basis for the draw.
403
+ * @returns {void}
404
+ * @example
405
+ * $(document).ready(function() {
406
+ * // Make the example container hidden to throw off the browser's sizing
407
+ * document.getElementById('container').style.display = "none";
408
+ * var oTable = $('#example').dataTable( {
409
+ * "sScrollY": "200px",
410
+ * "sAjaxSource": "media/dataset/large.txt",
411
+ * "sDom": "frtiS",
412
+ * "bDeferRender": true,
413
+ * "fnInitComplete": function (o) {
414
+ * // Immediately scroll to row 1000
415
+ * o.oScroller.fnScrollToRow( 1000 );
416
+ * }
417
+ * } );
418
+ *
419
+ * setTimeout( function () {
420
+ * // Make the example container visible and recalculate the scroller sizes
421
+ * document.getElementById('container').style.display = "block";
422
+ * oTable.fnSettings().oScroller.fnMeasure();
423
+ * }, 3000 );
424
+ */
425
+ "fnMeasure": function ( bRedraw )
426
+ {
427
+ if ( this.s.autoHeight )
428
+ {
429
+ this._fnCalcRowHeight();
430
+ }
431
+
432
+ var heights = this.s.heights;
433
+
434
+ if ( heights.row ) {
435
+ heights.viewport = $(this.dom.scroller).height();
436
+ this.s.viewportRows = parseInt( heights.viewport / heights.row, 10 )+1;
437
+ this.s.dt._iDisplayLength = this.s.viewportRows * this.s.displayBuffer;
438
+ }
439
+
440
+ if ( bRedraw === undefined || bRedraw )
441
+ {
442
+ this.s.dt.oInstance.fnDraw( false );
443
+ }
444
+ },
445
+
446
+
447
+ /**
448
+ * Get information about current displayed record range. This corresponds to
449
+ * the information usually displayed in the "Info" block of the table.
450
+ *
451
+ * @returns {object} info as an object:
452
+ * {
453
+ * start: {int}, // the 0-indexed record at the top of the viewport
454
+ * end: {int}, // the 0-indexed record at the bottom of the viewport
455
+ * }
456
+ */
457
+ "fnPageInfo": function()
458
+ {
459
+ var
460
+ dt = this.s.dt,
461
+ iScrollTop = this.dom.scroller.scrollTop,
462
+ iTotal = dt.fnRecordsDisplay(),
463
+ iPossibleEnd = Math.ceil(this.fnPixelsToRow(iScrollTop + this.s.heights.viewport, false, this.s.ani));
464
+
465
+ return {
466
+ start: Math.floor(this.fnPixelsToRow(iScrollTop, false, this.s.ani)),
467
+ end: iTotal < iPossibleEnd ? iTotal-1 : iPossibleEnd-1
468
+ };
469
+ },
470
+
471
+
472
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
473
+ * Private methods (they are of course public in JS, but recommended as private)
474
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
475
+
476
+ /**
477
+ * Initialisation for Scroller
478
+ * @returns {void}
479
+ * @private
480
+ */
481
+ "_fnConstruct": function ()
482
+ {
483
+ var that = this;
484
+
485
+ /* Sanity check */
486
+ if ( !this.s.dt.oFeatures.bPaginate ) {
487
+ this.s.dt.oApi._fnLog( this.s.dt, 0, 'Pagination must be enabled for Scroller' );
488
+ return;
489
+ }
490
+
491
+ /* Insert a div element that we can use to force the DT scrolling container to
492
+ * the height that would be required if the whole table was being displayed
493
+ */
494
+ this.dom.force.style.position = "relative";
495
+ this.dom.force.style.top = "0px";
496
+ this.dom.force.style.left = "0px";
497
+ this.dom.force.style.width = "1px";
498
+
499
+ this.dom.scroller = $('div.'+this.s.dt.oClasses.sScrollBody, this.s.dt.nTableWrapper)[0];
500
+ this.dom.scroller.appendChild( this.dom.force );
501
+ this.dom.scroller.style.position = "relative";
502
+
503
+ this.dom.table = $('>table', this.dom.scroller)[0];
504
+ this.dom.table.style.position = "absolute";
505
+ this.dom.table.style.top = "0px";
506
+ this.dom.table.style.left = "0px";
507
+
508
+ // Add class to 'announce' that we are a Scroller table
509
+ $(this.s.dt.nTableWrapper).addClass('DTS');
510
+
511
+ // Add a 'loading' indicator
512
+ if ( this.s.loadingIndicator )
513
+ {
514
+ this.dom.loader = $('<div class="dataTables_processing DTS_Loading">'+this.s.dt.oLanguage.sLoadingRecords+'</div>')
515
+ .css('display', 'none');
516
+
517
+ $(this.dom.scroller.parentNode)
518
+ .css('position', 'relative')
519
+ .append( this.dom.loader );
520
+ }
521
+
522
+ /* Initial size calculations */
523
+ if ( this.s.heights.row && this.s.heights.row != 'auto' )
524
+ {
525
+ this.s.autoHeight = false;
526
+ }
527
+ this.fnMeasure( false );
528
+
529
+ /* Scrolling callback to see if a page change is needed - use a throttled
530
+ * function for the save save callback so we aren't hitting it on every
531
+ * scroll
532
+ */
533
+ this.s.ingnoreScroll = true;
534
+ this.s.stateSaveThrottle = this.s.dt.oApi._fnThrottle( function () {
535
+ that.s.dt.oApi._fnSaveState( that.s.dt );
536
+ }, 500 );
537
+ $(this.dom.scroller).on( 'scroll.DTS', function (e) {
538
+ that._fnScroll.call( that );
539
+ } );
540
+
541
+ /* In iOS we catch the touchstart event in case the user tries to scroll
542
+ * while the display is already scrolling
543
+ */
544
+ $(this.dom.scroller).on('touchstart.DTS', function () {
545
+ that._fnScroll.call( that );
546
+ } );
547
+
548
+ /* Update the scroller when the DataTable is redrawn */
549
+ this.s.dt.aoDrawCallback.push( {
550
+ "fn": function () {
551
+ if ( that.s.dt.bInitialised ) {
552
+ that._fnDrawCallback.call( that );
553
+ }
554
+ },
555
+ "sName": "Scroller"
556
+ } );
557
+
558
+ /* On resize, update the information element, since the number of rows shown might change */
559
+ $(window).on( 'resize.DTS', function () {
560
+ that.fnMeasure( false );
561
+ that._fnInfo();
562
+ } );
563
+
564
+ /* Add a state saving parameter to the DT state saving so we can restore the exact
565
+ * position of the scrolling
566
+ */
567
+ var initialStateSave = true;
568
+ this.s.dt.oApi._fnCallbackReg( this.s.dt, 'aoStateSaveParams', function (oS, oData) {
569
+ /* Set iScroller to saved scroll position on initialization.
570
+ */
571
+ if(initialStateSave && that.s.dt.oLoadedState){
572
+ oData.iScroller = that.s.dt.oLoadedState.iScroller;
573
+ oData.iScrollerTopRow = that.s.dt.oLoadedState.iScrollerTopRow;
574
+ initialStateSave = false;
575
+ } else {
576
+ oData.iScroller = that.dom.scroller.scrollTop;
577
+ oData.iScrollerTopRow = that.s.topRowFloat;
578
+ }
579
+ }, "Scroller_State" );
580
+
581
+ if ( this.s.dt.oLoadedState ) {
582
+ this.s.topRowFloat = this.s.dt.oLoadedState.iScrollerTopRow || 0;
583
+ }
584
+
585
+ // Measure immediately. Scroller will have been added using preInit, so
586
+ // we can reliably do this here. We could potentially also measure on
587
+ // init complete, which would be useful for cases where the data is Ajax
588
+ // loaded and longer than a single line.
589
+ $(this.s.dt.nTable).one( 'init.dt', function () {
590
+ that.fnMeasure();
591
+ } );
592
+
593
+ /* Destructor */
594
+ this.s.dt.aoDestroyCallback.push( {
595
+ "sName": "Scroller",
596
+ "fn": function () {
597
+ $(window).off( 'resize.DTS' );
598
+ $(that.dom.scroller).off('touchstart.DTS scroll.DTS');
599
+ $(that.s.dt.nTableWrapper).removeClass('DTS');
600
+ $('div.DTS_Loading', that.dom.scroller.parentNode).remove();
601
+ $(that.s.dt.nTable).off( 'init.dt' );
602
+
603
+ that.dom.table.style.position = "";
604
+ that.dom.table.style.top = "";
605
+ that.dom.table.style.left = "";
606
+ }
607
+ } );
608
+ },
609
+
610
+
611
+ /**
612
+ * Scrolling function - fired whenever the scrolling position is changed.
613
+ * This method needs to use the stored values to see if the table should be
614
+ * redrawn as we are moving towards the end of the information that is
615
+ * currently drawn or not. If needed, then it will redraw the table based on
616
+ * the new position.
617
+ * @returns {void}
618
+ * @private
619
+ */
620
+ "_fnScroll": function ()
621
+ {
622
+ var
623
+ that = this,
624
+ heights = this.s.heights,
625
+ iScrollTop = this.dom.scroller.scrollTop,
626
+ iTopRow;
627
+
628
+ if ( this.s.skip ) {
629
+ return;
630
+ }
631
+
632
+ if ( this.s.ingnoreScroll ) {
633
+ return;
634
+ }
635
+
636
+ /* If the table has been sorted or filtered, then we use the redraw that
637
+ * DataTables as done, rather than performing our own
638
+ */
639
+ if ( this.s.dt.bFiltered || this.s.dt.bSorted ) {
640
+ this.s.lastScrollTop = 0;
641
+ return;
642
+ }
643
+
644
+ /* Update the table's information display for what is now in the viewport */
645
+ this._fnInfo();
646
+
647
+ /* We don't want to state save on every scroll event - that's heavy
648
+ * handed, so use a timeout to update the state saving only when the
649
+ * scrolling has finished
650
+ */
651
+ clearTimeout( this.s.stateTO );
652
+ this.s.stateTO = setTimeout( function () {
653
+ that.s.dt.oApi._fnSaveState( that.s.dt );
654
+ }, 250 );
655
+
656
+ /* Check if the scroll point is outside the trigger boundary which would required
657
+ * a DataTables redraw
658
+ */
659
+ if ( iScrollTop < this.s.redrawTop || iScrollTop > this.s.redrawBottom ) {
660
+ var preRows = Math.ceil( ((this.s.displayBuffer-1)/2) * this.s.viewportRows );
661
+
662
+ if ( Math.abs( iScrollTop - this.s.lastScrollTop ) > heights.viewport || this.s.ani ) {
663
+ iTopRow = parseInt(this._domain( 'physicalToVirtual', iScrollTop ) / heights.row, 10) - preRows;
664
+ this.s.topRowFloat = this._domain( 'physicalToVirtual', iScrollTop ) / heights.row;
665
+ }
666
+ else {
667
+ iTopRow = this.fnPixelsToRow( iScrollTop ) - preRows;
668
+ this.s.topRowFloat = this.fnPixelsToRow( iScrollTop, false );
669
+ }
670
+
671
+ if ( iTopRow <= 0 ) {
672
+ /* At the start of the table */
673
+ iTopRow = 0;
674
+ }
675
+ else if ( iTopRow + this.s.dt._iDisplayLength > this.s.dt.fnRecordsDisplay() ) {
676
+ /* At the end of the table */
677
+ iTopRow = this.s.dt.fnRecordsDisplay() - this.s.dt._iDisplayLength;
678
+ if ( iTopRow < 0 ) {
679
+ iTopRow = 0;
680
+ }
681
+ }
682
+ else if ( iTopRow % 2 !== 0 ) {
683
+ // For the row-striping classes (odd/even) we want only to start
684
+ // on evens otherwise the stripes will change between draws and
685
+ // look rubbish
686
+ iTopRow++;
687
+ }
688
+
689
+ if ( iTopRow != this.s.dt._iDisplayStart ) {
690
+ /* Cache the new table position for quick lookups */
691
+ this.s.tableTop = $(this.s.dt.nTable).offset().top;
692
+ this.s.tableBottom = $(this.s.dt.nTable).height() + this.s.tableTop;
693
+
694
+ var draw = function () {
695
+ if ( that.s.scrollDrawReq === null ) {
696
+ that.s.scrollDrawReq = iScrollTop;
697
+ }
698
+
699
+ that.s.dt._iDisplayStart = iTopRow;
700
+ that.s.dt.oApi._fnDraw( that.s.dt );
701
+ };
702
+
703
+ /* Do the DataTables redraw based on the calculated start point - note that when
704
+ * using server-side processing we introduce a small delay to not DoS the server...
705
+ */
706
+ if ( this.s.dt.oFeatures.bServerSide ) {
707
+ clearTimeout( this.s.drawTO );
708
+ this.s.drawTO = setTimeout( draw, this.s.serverWait );
709
+ }
710
+ else {
711
+ draw();
712
+ }
713
+
714
+ if ( this.dom.loader && ! this.s.loaderVisible ) {
715
+ this.dom.loader.css( 'display', 'block' );
716
+ this.s.loaderVisible = true;
717
+ }
718
+ }
719
+ }
720
+ else {
721
+ this.s.topRowFloat = this._domain( 'physicalToVirtual', iScrollTop ) / heights.row;
722
+ }
723
+
724
+ this.s.lastScrollTop = iScrollTop;
725
+ this.s.stateSaveThrottle();
726
+ },
727
+
728
+
729
+ /**
730
+ * Convert from one domain to another. The physical domain is the actual
731
+ * pixel count on the screen, while the virtual is if we had browsers which
732
+ * had scrolling containers of infinite height (i.e. the absolute value)
733
+ *
734
+ * @param {string} dir Domain transform direction, `virtualToPhysical` or
735
+ * `physicalToVirtual`
736
+ * @returns {number} Calculated transform
737
+ * @private
738
+ */
739
+ _domain: function ( dir, val )
740
+ {
741
+ var heights = this.s.heights;
742
+ var coeff;
743
+
744
+ // If the virtual and physical height match, then we use a linear
745
+ // transform between the two, allowing the scrollbar to be linear
746
+ if ( heights.virtual === heights.scroll ) {
747
+ return val;
748
+ }
749
+
750
+ // Otherwise, we want a non-linear scrollbar to take account of the
751
+ // redrawing regions at the start and end of the table, otherwise these
752
+ // can stutter badly - on large tables 30px (for example) scroll might
753
+ // be hundreds of rows, so the table would be redrawing every few px at
754
+ // the start and end. Use a simple quadratic to stop this. It does mean
755
+ // the scrollbar is non-linear, but with such massive data sets, the
756
+ // scrollbar is going to be a best guess anyway
757
+ var xMax = (heights.scroll - heights.viewport) / 2;
758
+ var yMax = (heights.virtual - heights.viewport) / 2;
759
+
760
+ coeff = yMax / ( xMax * xMax );
761
+
762
+ if ( dir === 'virtualToPhysical' ) {
763
+ if ( val < yMax ) {
764
+ return Math.pow(val / coeff, 0.5);
765
+ }
766
+ else {
767
+ val = (yMax*2) - val;
768
+ return val < 0 ?
769
+ heights.scroll :
770
+ (xMax*2) - Math.pow(val / coeff, 0.5);
771
+ }
772
+ }
773
+ else if ( dir === 'physicalToVirtual' ) {
774
+ if ( val < xMax ) {
775
+ return val * val * coeff;
776
+ }
777
+ else {
778
+ val = (xMax*2) - val;
779
+ return val < 0 ?
780
+ heights.virtual :
781
+ (yMax*2) - (val * val * coeff);
782
+ }
783
+ }
784
+ },
785
+
786
+
787
+ /**
788
+ * Draw callback function which is fired when the DataTable is redrawn. The main function of
789
+ * this method is to position the drawn table correctly the scrolling container for the rows
790
+ * that is displays as a result of the scrolling position.
791
+ * @returns {void}
792
+ * @private
793
+ */
794
+ "_fnDrawCallback": function ()
795
+ {
796
+ var
797
+ that = this,
798
+ heights = this.s.heights,
799
+ iScrollTop = this.dom.scroller.scrollTop,
800
+ iActualScrollTop = iScrollTop,
801
+ iScrollBottom = iScrollTop + heights.viewport,
802
+ iTableHeight = $(this.s.dt.nTable).height(),
803
+ displayStart = this.s.dt._iDisplayStart,
804
+ displayLen = this.s.dt._iDisplayLength,
805
+ displayEnd = this.s.dt.fnRecordsDisplay();
806
+
807
+ // Disable the scroll event listener while we are updating the DOM
808
+ this.s.skip = true;
809
+
810
+ // Resize the scroll forcing element
811
+ this._fnScrollForce();
812
+
813
+ // Reposition the scrolling for the updated virtual position if needed
814
+ if ( displayStart === 0 ) {
815
+ // Linear calculation at the top of the table
816
+ iScrollTop = this.s.topRowFloat * heights.row;
817
+ }
818
+ else if ( displayStart + displayLen >= displayEnd ) {
819
+ // Linear calculation that the bottom as well
820
+ iScrollTop = heights.scroll - ((displayEnd - this.s.topRowFloat) * heights.row);
821
+ }
822
+ else {
823
+ // Domain scaled in the middle
824
+ iScrollTop = this._domain( 'virtualToPhysical', this.s.topRowFloat * heights.row );
825
+ }
826
+
827
+ this.dom.scroller.scrollTop = iScrollTop;
828
+
829
+ // Store positional information so positional calculations can be based
830
+ // upon the current table draw position
831
+ this.s.baseScrollTop = iScrollTop;
832
+ this.s.baseRowTop = this.s.topRowFloat;
833
+
834
+ // Position the table in the virtual scroller
835
+ var tableTop = iScrollTop - ((this.s.topRowFloat - displayStart) * heights.row);
836
+ if ( displayStart === 0 ) {
837
+ tableTop = 0;
838
+ }
839
+ else if ( displayStart + displayLen >= displayEnd ) {
840
+ tableTop = heights.scroll - iTableHeight;
841
+ }
842
+
843
+ this.dom.table.style.top = tableTop+'px';
844
+
845
+ /* Cache some information for the scroller */
846
+ this.s.tableTop = tableTop;
847
+ this.s.tableBottom = iTableHeight + this.s.tableTop;
848
+
849
+ // Calculate the boundaries for where a redraw will be triggered by the
850
+ // scroll event listener
851
+ var boundaryPx = (iScrollTop - this.s.tableTop) * this.s.boundaryScale;
852
+ this.s.redrawTop = iScrollTop - boundaryPx;
853
+ this.s.redrawBottom = iScrollTop + boundaryPx;
854
+
855
+ this.s.skip = false;
856
+
857
+ // Restore the scrolling position that was saved by DataTable's state
858
+ // saving Note that this is done on the second draw when data is Ajax
859
+ // sourced, and the first draw when DOM soured
860
+ if ( this.s.dt.oFeatures.bStateSave && this.s.dt.oLoadedState !== null &&
861
+ typeof this.s.dt.oLoadedState.iScroller != 'undefined' )
862
+ {
863
+ // A quirk of DataTables is that the draw callback will occur on an
864
+ // empty set if Ajax sourced, but not if server-side processing.
865
+ var ajaxSourced = (this.s.dt.sAjaxSource || that.s.dt.ajax) && ! this.s.dt.oFeatures.bServerSide ?
866
+ true :
867
+ false;
868
+
869
+ if ( ( ajaxSourced && this.s.dt.iDraw == 2) ||
870
+ (!ajaxSourced && this.s.dt.iDraw == 1) )
871
+ {
872
+ setTimeout( function () {
873
+ $(that.dom.scroller).scrollTop( that.s.dt.oLoadedState.iScroller );
874
+ that.s.redrawTop = that.s.dt.oLoadedState.iScroller - (heights.viewport/2);
875
+
876
+ // In order to prevent layout thrashing we need another
877
+ // small delay
878
+ setTimeout( function () {
879
+ that.s.ingnoreScroll = false;
880
+ }, 0 );
881
+ }, 0 );
882
+ }
883
+ }
884
+ else {
885
+ that.s.ingnoreScroll = false;
886
+ }
887
+
888
+ // Because of the order of the DT callbacks, the info update will
889
+ // take precedence over the one we want here. So a 'thread' break is
890
+ // needed. Only add the thread break if bInfo is set
891
+ if ( this.s.dt.oFeatures.bInfo ) {
892
+ setTimeout( function () {
893
+ that._fnInfo.call( that );
894
+ }, 0 );
895
+ }
896
+
897
+ // Hide the loading indicator
898
+ if ( this.dom.loader && this.s.loaderVisible ) {
899
+ this.dom.loader.css( 'display', 'none' );
900
+ this.s.loaderVisible = false;
901
+ }
902
+ },
903
+
904
+
905
+ /**
906
+ * Force the scrolling container to have height beyond that of just the
907
+ * table that has been drawn so the user can scroll the whole data set.
908
+ *
909
+ * Note that if the calculated required scrolling height exceeds a maximum
910
+ * value (1 million pixels - hard-coded) the forcing element will be set
911
+ * only to that maximum value and virtual / physical domain transforms will
912
+ * be used to allow Scroller to display tables of any number of records.
913
+ * @returns {void}
914
+ * @private
915
+ */
916
+ _fnScrollForce: function ()
917
+ {
918
+ var heights = this.s.heights;
919
+ var max = 1000000;
920
+
921
+ heights.virtual = heights.row * this.s.dt.fnRecordsDisplay();
922
+ heights.scroll = heights.virtual;
923
+
924
+ if ( heights.scroll > max ) {
925
+ heights.scroll = max;
926
+ }
927
+
928
+ // Minimum height so there is always a row visible (the 'no rows found'
929
+ // if reduced to zero filtering)
930
+ this.dom.force.style.height = heights.scroll > this.s.heights.row ?
931
+ heights.scroll+'px' :
932
+ this.s.heights.row+'px';
933
+ },
934
+
935
+
936
+ /**
937
+ * Automatic calculation of table row height. This is just a little tricky here as using
938
+ * initialisation DataTables has tale the table out of the document, so we need to create
939
+ * a new table and insert it into the document, calculate the row height and then whip the
940
+ * table out.
941
+ * @returns {void}
942
+ * @private
943
+ */
944
+ "_fnCalcRowHeight": function ()
945
+ {
946
+ var dt = this.s.dt;
947
+ var origTable = dt.nTable;
948
+ var nTable = origTable.cloneNode( false );
949
+ var tbody = $('<tbody/>').appendTo( nTable );
950
+ var container = $(
951
+ '<div class="'+dt.oClasses.sWrapper+' DTS">'+
952
+ '<div class="'+dt.oClasses.sScrollWrapper+'">'+
953
+ '<div class="'+dt.oClasses.sScrollBody+'"></div>'+
954
+ '</div>'+
955
+ '</div>'
956
+ );
957
+
958
+ // Want 3 rows in the sizing table so :first-child and :last-child
959
+ // CSS styles don't come into play - take the size of the middle row
960
+ $('tbody tr:lt(4)', origTable).clone().appendTo( tbody );
961
+ while( $('tr', tbody).length < 3 ) {
962
+ tbody.append( '<tr><td>&nbsp;</td></tr>' );
963
+ }
964
+
965
+ $('div.'+dt.oClasses.sScrollBody, container).append( nTable );
966
+
967
+ // If initialised using `dom`, use the holding element as the insert point
968
+ var insertEl = this.s.dt.nHolding || origTable.parentNode;
969
+
970
+ if ( ! $(insertEl).is(':visible') ) {
971
+ insertEl = 'body';
972
+ }
973
+
974
+ container.appendTo( insertEl );
975
+ this.s.heights.row = $('tr', tbody).eq(1).outerHeight();
976
+
977
+ container.remove();
978
+ },
979
+
980
+
981
+ /**
982
+ * Update any information elements that are controlled by the DataTable based on the scrolling
983
+ * viewport and what rows are visible in it. This function basically acts in the same way as
984
+ * _fnUpdateInfo in DataTables, and effectively replaces that function.
985
+ * @returns {void}
986
+ * @private
987
+ */
988
+ "_fnInfo": function ()
989
+ {
990
+ if ( !this.s.dt.oFeatures.bInfo )
991
+ {
992
+ return;
993
+ }
994
+
995
+ var
996
+ dt = this.s.dt,
997
+ language = dt.oLanguage,
998
+ iScrollTop = this.dom.scroller.scrollTop,
999
+ iStart = Math.floor( this.fnPixelsToRow(iScrollTop, false, this.s.ani)+1 ),
1000
+ iMax = dt.fnRecordsTotal(),
1001
+ iTotal = dt.fnRecordsDisplay(),
1002
+ iPossibleEnd = Math.ceil( this.fnPixelsToRow(iScrollTop+this.s.heights.viewport, false, this.s.ani) ),
1003
+ iEnd = iTotal < iPossibleEnd ? iTotal : iPossibleEnd,
1004
+ sStart = dt.fnFormatNumber( iStart ),
1005
+ sEnd = dt.fnFormatNumber( iEnd ),
1006
+ sMax = dt.fnFormatNumber( iMax ),
1007
+ sTotal = dt.fnFormatNumber( iTotal ),
1008
+ sOut;
1009
+
1010
+ if ( dt.fnRecordsDisplay() === 0 &&
1011
+ dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
1012
+ {
1013
+ /* Empty record set */
1014
+ sOut = language.sInfoEmpty+ language.sInfoPostFix;
1015
+ }
1016
+ else if ( dt.fnRecordsDisplay() === 0 )
1017
+ {
1018
+ /* Empty record set after filtering */
1019
+ sOut = language.sInfoEmpty +' '+
1020
+ language.sInfoFiltered.replace('_MAX_', sMax)+
1021
+ language.sInfoPostFix;
1022
+ }
1023
+ else if ( dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
1024
+ {
1025
+ /* Normal record set */
1026
+ sOut = language.sInfo.
1027
+ replace('_START_', sStart).
1028
+ replace('_END_', sEnd).
1029
+ replace('_MAX_', sMax).
1030
+ replace('_TOTAL_', sTotal)+
1031
+ language.sInfoPostFix;
1032
+ }
1033
+ else
1034
+ {
1035
+ /* Record set after filtering */
1036
+ sOut = language.sInfo.
1037
+ replace('_START_', sStart).
1038
+ replace('_END_', sEnd).
1039
+ replace('_MAX_', sMax).
1040
+ replace('_TOTAL_', sTotal) +' '+
1041
+ language.sInfoFiltered.replace(
1042
+ '_MAX_',
1043
+ dt.fnFormatNumber(dt.fnRecordsTotal())
1044
+ )+
1045
+ language.sInfoPostFix;
1046
+ }
1047
+
1048
+ var callback = language.fnInfoCallback;
1049
+ if ( callback ) {
1050
+ sOut = callback.call( dt.oInstance,
1051
+ dt, iStart, iEnd, iMax, iTotal, sOut
1052
+ );
1053
+ }
1054
+
1055
+ var n = dt.aanFeatures.i;
1056
+ if ( typeof n != 'undefined' )
1057
+ {
1058
+ for ( var i=0, iLen=n.length ; i<iLen ; i++ )
1059
+ {
1060
+ $(n[i]).html( sOut );
1061
+ }
1062
+ }
1063
+
1064
+ // DT doesn't actually (yet) trigger this event, but it will in future
1065
+ $(dt.nTable).triggerHandler( 'info.dt' );
1066
+ }
1067
+ } );
1068
+
1069
+
1070
+
1071
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1072
+ * Statics
1073
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1074
+
1075
+
1076
+ /**
1077
+ * Scroller default settings for initialisation
1078
+ * @namespace
1079
+ * @name Scroller.defaults
1080
+ * @static
1081
+ */
1082
+ Scroller.defaults = /** @lends Scroller.defaults */{
1083
+ /**
1084
+ * Indicate if Scroller show show trace information on the console or not. This can be
1085
+ * useful when debugging Scroller or if just curious as to what it is doing, but should
1086
+ * be turned off for production.
1087
+ * @type bool
1088
+ * @default false
1089
+ * @static
1090
+ * @example
1091
+ * var oTable = $('#example').dataTable( {
1092
+ * "sScrollY": "200px",
1093
+ * "sDom": "frtiS",
1094
+ * "bDeferRender": true,
1095
+ * "oScroller": {
1096
+ * "trace": true
1097
+ * }
1098
+ * } );
1099
+ */
1100
+ "trace": false,
1101
+
1102
+ /**
1103
+ * Scroller will attempt to automatically calculate the height of rows for it's internal
1104
+ * calculations. However the height that is used can be overridden using this parameter.
1105
+ * @type int|string
1106
+ * @default auto
1107
+ * @static
1108
+ * @example
1109
+ * var oTable = $('#example').dataTable( {
1110
+ * "sScrollY": "200px",
1111
+ * "sDom": "frtiS",
1112
+ * "bDeferRender": true,
1113
+ * "oScroller": {
1114
+ * "rowHeight": 30
1115
+ * }
1116
+ * } );
1117
+ */
1118
+ "rowHeight": "auto",
1119
+
1120
+ /**
1121
+ * When using server-side processing, Scroller will wait a small amount of time to allow
1122
+ * the scrolling to finish before requesting more data from the server. This prevents
1123
+ * you from DoSing your own server! The wait time can be configured by this parameter.
1124
+ * @type int
1125
+ * @default 200
1126
+ * @static
1127
+ * @example
1128
+ * var oTable = $('#example').dataTable( {
1129
+ * "sScrollY": "200px",
1130
+ * "sDom": "frtiS",
1131
+ * "bDeferRender": true,
1132
+ * "oScroller": {
1133
+ * "serverWait": 100
1134
+ * }
1135
+ * } );
1136
+ */
1137
+ "serverWait": 200,
1138
+
1139
+ /**
1140
+ * The display buffer is what Scroller uses to calculate how many rows it should pre-fetch
1141
+ * for scrolling. Scroller automatically adjusts DataTables' display length to pre-fetch
1142
+ * rows that will be shown in "near scrolling" (i.e. just beyond the current display area).
1143
+ * The value is based upon the number of rows that can be displayed in the viewport (i.e.
1144
+ * a value of 1), and will apply the display range to records before before and after the
1145
+ * current viewport - i.e. a factor of 3 will allow Scroller to pre-fetch 1 viewport's worth
1146
+ * of rows before the current viewport, the current viewport's rows and 1 viewport's worth
1147
+ * of rows after the current viewport. Adjusting this value can be useful for ensuring
1148
+ * smooth scrolling based on your data set.
1149
+ * @type int
1150
+ * @default 7
1151
+ * @static
1152
+ * @example
1153
+ * var oTable = $('#example').dataTable( {
1154
+ * "sScrollY": "200px",
1155
+ * "sDom": "frtiS",
1156
+ * "bDeferRender": true,
1157
+ * "oScroller": {
1158
+ * "displayBuffer": 10
1159
+ * }
1160
+ * } );
1161
+ */
1162
+ "displayBuffer": 9,
1163
+
1164
+ /**
1165
+ * Scroller uses the boundary scaling factor to decide when to redraw the table - which it
1166
+ * typically does before you reach the end of the currently loaded data set (in order to
1167
+ * allow the data to look continuous to a user scrolling through the data). If given as 0
1168
+ * then the table will be redrawn whenever the viewport is scrolled, while 1 would not
1169
+ * redraw the table until the currently loaded data has all been shown. You will want
1170
+ * something in the middle - the default factor of 0.5 is usually suitable.
1171
+ * @type float
1172
+ * @default 0.5
1173
+ * @static
1174
+ * @example
1175
+ * var oTable = $('#example').dataTable( {
1176
+ * "sScrollY": "200px",
1177
+ * "sDom": "frtiS",
1178
+ * "bDeferRender": true,
1179
+ * "oScroller": {
1180
+ * "boundaryScale": 0.75
1181
+ * }
1182
+ * } );
1183
+ */
1184
+ "boundaryScale": 0.5,
1185
+
1186
+ /**
1187
+ * Show (or not) the loading element in the background of the table. Note that you should
1188
+ * include the dataTables.scroller.css file for this to be displayed correctly.
1189
+ * @type boolean
1190
+ * @default false
1191
+ * @static
1192
+ * @example
1193
+ * var oTable = $('#example').dataTable( {
1194
+ * "sScrollY": "200px",
1195
+ * "sDom": "frtiS",
1196
+ * "bDeferRender": true,
1197
+ * "oScroller": {
1198
+ * "loadingIndicator": true
1199
+ * }
1200
+ * } );
1201
+ */
1202
+ "loadingIndicator": false
1203
+ };
1204
+
1205
+ Scroller.oDefaults = Scroller.defaults;
1206
+
1207
+
1208
+
1209
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1210
+ * Constants
1211
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1212
+
1213
+ /**
1214
+ * Scroller version
1215
+ * @type String
1216
+ * @default See code
1217
+ * @name Scroller.version
1218
+ * @static
1219
+ */
1220
+ Scroller.version = "1.4.2";
1221
+
1222
+
1223
+
1224
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1225
+ * Initialisation
1226
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1227
+
1228
+ // Legacy `dom` parameter initialisation support
1229
+ if ( typeof $.fn.dataTable == "function" &&
1230
+ typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
1231
+ $.fn.dataTableExt.fnVersionCheck('1.10.0') )
1232
+ {
1233
+ $.fn.dataTableExt.aoFeatures.push( {
1234
+ "fnInit": function( oDTSettings ) {
1235
+ var init = oDTSettings.oInit;
1236
+ var opts = init.scroller || init.oScroller || {};
1237
+
1238
+ new Scroller( oDTSettings, opts );
1239
+ },
1240
+ "cFeature": "S",
1241
+ "sFeature": "Scroller"
1242
+ } );
1243
+ }
1244
+ else
1245
+ {
1246
+ alert( "Warning: Scroller requires DataTables 1.10.0 or greater - www.datatables.net/download");
1247
+ }
1248
+
1249
+ // Attach a listener to the document which listens for DataTables initialisation
1250
+ // events so we can automatically initialise
1251
+ $(document).on( 'preInit.dt.dtscroller', function (e, settings) {
1252
+ if ( e.namespace !== 'dt' ) {
1253
+ return;
1254
+ }
1255
+
1256
+ var init = settings.oInit.scroller;
1257
+ var defaults = DataTable.defaults.scroller;
1258
+
1259
+ if ( init || defaults ) {
1260
+ var opts = $.extend( {}, init, defaults );
1261
+
1262
+ if ( init !== false ) {
1263
+ new Scroller( settings, opts );
1264
+ }
1265
+ }
1266
+ } );
1267
+
1268
+
1269
+ // Attach Scroller to DataTables so it can be accessed as an 'extra'
1270
+ $.fn.dataTable.Scroller = Scroller;
1271
+ $.fn.DataTable.Scroller = Scroller;
1272
+
1273
+
1274
+ // DataTables 1.10 API method aliases
1275
+ var Api = $.fn.dataTable.Api;
1276
+
1277
+ Api.register( 'scroller()', function () {
1278
+ return this;
1279
+ } );
1280
+
1281
+ // Undocumented and deprecated - is it actually useful at all?
1282
+ Api.register( 'scroller().rowToPixels()', function ( rowIdx, intParse, virtual ) {
1283
+ var ctx = this.context;
1284
+
1285
+ if ( ctx.length && ctx[0].oScroller ) {
1286
+ return ctx[0].oScroller.fnRowToPixels( rowIdx, intParse, virtual );
1287
+ }
1288
+ // undefined
1289
+ } );
1290
+
1291
+ // Undocumented and deprecated - is it actually useful at all?
1292
+ Api.register( 'scroller().pixelsToRow()', function ( pixels, intParse, virtual ) {
1293
+ var ctx = this.context;
1294
+
1295
+ if ( ctx.length && ctx[0].oScroller ) {
1296
+ return ctx[0].oScroller.fnPixelsToRow( pixels, intParse, virtual );
1297
+ }
1298
+ // undefined
1299
+ } );
1300
+
1301
+ // Undocumented and deprecated - use `row().scrollTo()` instead
1302
+ Api.register( 'scroller().scrollToRow()', function ( row, ani ) {
1303
+ this.iterator( 'table', function ( ctx ) {
1304
+ if ( ctx.oScroller ) {
1305
+ ctx.oScroller.fnScrollToRow( row, ani );
1306
+ }
1307
+ } );
1308
+
1309
+ return this;
1310
+ } );
1311
+
1312
+ Api.register( 'row().scrollTo()', function ( ani ) {
1313
+ var that = this;
1314
+
1315
+ this.iterator( 'row', function ( ctx, rowIdx ) {
1316
+ if ( ctx.oScroller ) {
1317
+ var displayIdx = that
1318
+ .rows( { order: 'applied', search: 'applied' } )
1319
+ .indexes()
1320
+ .indexOf( rowIdx );
1321
+
1322
+ ctx.oScroller.fnScrollToRow( displayIdx, ani );
1323
+ }
1324
+ } );
1325
+
1326
+ return this;
1327
+ } );
1328
+
1329
+ Api.register( 'scroller.measure()', function ( redraw ) {
1330
+ this.iterator( 'table', function ( ctx ) {
1331
+ if ( ctx.oScroller ) {
1332
+ ctx.oScroller.fnMeasure( redraw );
1333
+ }
1334
+ } );
1335
+
1336
+ return this;
1337
+ } );
1338
+
1339
+ Api.register( 'scroller.page()', function() {
1340
+ var ctx = this.context;
1341
+
1342
+ if ( ctx.length && ctx[0].oScroller ) {
1343
+ return ctx[0].oScroller.fnPageInfo();
1344
+ }
1345
+ // undefined
1346
+ } );
1347
+
1348
+ return Scroller;
1349
+ }));
common/vendor/datatables/extensions/Scroller/js/dataTables.scroller.min.js ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ Scroller 1.4.2
3
+ ©2011-2016 SpryMedia Ltd - datatables.net/license
4
+ */
5
+ (function(e){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(h){return e(h,window,document)}):"object"===typeof exports?module.exports=function(h,j){h||(h=window);if(!j||!j.fn.dataTable)j=require("datatables.net")(h,j).$;return e(j,h,h.document)}:e(jQuery,window,document)})(function(e,h,j,l){var m=e.fn.dataTable,g=function(a,b){this instanceof g?(b===l&&(b={}),this.s={dt:e.fn.dataTable.Api(a).settings()[0],tableTop:0,tableBottom:0,redrawTop:0,redrawBottom:0,autoHeight:!0,
6
+ viewportRows:0,stateTO:null,drawTO:null,heights:{jump:null,page:null,virtual:null,scroll:null,row:null,viewport:null},topRowFloat:0,scrollDrawDiff:null,loaderVisible:!1},this.s=e.extend(this.s,g.oDefaults,b),this.s.heights.row=this.s.rowHeight,this.dom={force:j.createElement("div"),scroller:null,table:null,loader:null},this.s.dt.oScroller||(this.s.dt.oScroller=this,this._fnConstruct())):alert("Scroller warning: Scroller must be initialised with the 'new' keyword.")};e.extend(g.prototype,{fnRowToPixels:function(a,
7
+ b,c){a=c?this._domain("virtualToPhysical",a*this.s.heights.row):this.s.baseScrollTop+(a-this.s.baseRowTop)*this.s.heights.row;return b||b===l?parseInt(a,10):a},fnPixelsToRow:function(a,b,c){var d=a-this.s.baseScrollTop,a=c?this._domain("physicalToVirtual",a)/this.s.heights.row:d/this.s.heights.row+this.s.baseRowTop;return b||b===l?parseInt(a,10):a},fnScrollToRow:function(a,b){var c=this,d=!1,f=this.fnRowToPixels(a),i=a-(this.s.displayBuffer-1)/2*this.s.viewportRows;0>i&&(i=0);if((f>this.s.redrawBottom||
8
+ f<this.s.redrawTop)&&this.s.dt._iDisplayStart!==i)d=!0,f=this.fnRowToPixels(a,!1,!0);"undefined"==typeof b||b?(this.s.ani=d,e(this.dom.scroller).animate({scrollTop:f},function(){setTimeout(function(){c.s.ani=!1},25)})):e(this.dom.scroller).scrollTop(f)},fnMeasure:function(a){this.s.autoHeight&&this._fnCalcRowHeight();var b=this.s.heights;b.row&&(b.viewport=e(this.dom.scroller).height(),this.s.viewportRows=parseInt(b.viewport/b.row,10)+1,this.s.dt._iDisplayLength=this.s.viewportRows*this.s.displayBuffer);
9
+ (a===l||a)&&this.s.dt.oInstance.fnDraw(!1)},fnPageInfo:function(){var a=this.dom.scroller.scrollTop,b=this.s.dt.fnRecordsDisplay(),c=Math.ceil(this.fnPixelsToRow(a+this.s.heights.viewport,!1,this.s.ani));return{start:Math.floor(this.fnPixelsToRow(a,!1,this.s.ani)),end:b<c?b-1:c-1}},_fnConstruct:function(){var a=this;if(this.s.dt.oFeatures.bPaginate){this.dom.force.style.position="relative";this.dom.force.style.top="0px";this.dom.force.style.left="0px";this.dom.force.style.width="1px";this.dom.scroller=
10
+ e("div."+this.s.dt.oClasses.sScrollBody,this.s.dt.nTableWrapper)[0];this.dom.scroller.appendChild(this.dom.force);this.dom.scroller.style.position="relative";this.dom.table=e(">table",this.dom.scroller)[0];this.dom.table.style.position="absolute";this.dom.table.style.top="0px";this.dom.table.style.left="0px";e(this.s.dt.nTableWrapper).addClass("DTS");this.s.loadingIndicator&&(this.dom.loader=e('<div class="dataTables_processing DTS_Loading">'+this.s.dt.oLanguage.sLoadingRecords+"</div>").css("display",
11
+ "none"),e(this.dom.scroller.parentNode).css("position","relative").append(this.dom.loader));this.s.heights.row&&"auto"!=this.s.heights.row&&(this.s.autoHeight=!1);this.fnMeasure(!1);this.s.ingnoreScroll=!0;this.s.stateSaveThrottle=this.s.dt.oApi._fnThrottle(function(){a.s.dt.oApi._fnSaveState(a.s.dt)},500);e(this.dom.scroller).on("scroll.DTS",function(){a._fnScroll.call(a)});e(this.dom.scroller).on("touchstart.DTS",function(){a._fnScroll.call(a)});this.s.dt.aoDrawCallback.push({fn:function(){a.s.dt.bInitialised&&
12
+ a._fnDrawCallback.call(a)},sName:"Scroller"});e(h).on("resize.DTS",function(){a.fnMeasure(false);a._fnInfo()});var b=!0;this.s.dt.oApi._fnCallbackReg(this.s.dt,"aoStateSaveParams",function(c,d){if(b&&a.s.dt.oLoadedState){d.iScroller=a.s.dt.oLoadedState.iScroller;d.iScrollerTopRow=a.s.dt.oLoadedState.iScrollerTopRow;b=false}else{d.iScroller=a.dom.scroller.scrollTop;d.iScrollerTopRow=a.s.topRowFloat}},"Scroller_State");this.s.dt.oLoadedState&&(this.s.topRowFloat=this.s.dt.oLoadedState.iScrollerTopRow||
13
+ 0);e(this.s.dt.nTable).one("init.dt",function(){a.fnMeasure()});this.s.dt.aoDestroyCallback.push({sName:"Scroller",fn:function(){e(h).off("resize.DTS");e(a.dom.scroller).off("touchstart.DTS scroll.DTS");e(a.s.dt.nTableWrapper).removeClass("DTS");e("div.DTS_Loading",a.dom.scroller.parentNode).remove();e(a.s.dt.nTable).off("init.dt");a.dom.table.style.position="";a.dom.table.style.top="";a.dom.table.style.left=""}})}else this.s.dt.oApi._fnLog(this.s.dt,0,"Pagination must be enabled for Scroller")},
14
+ _fnScroll:function(){var a=this,b=this.s.heights,c=this.dom.scroller.scrollTop,d;if(!this.s.skip&&!this.s.ingnoreScroll)if(this.s.dt.bFiltered||this.s.dt.bSorted)this.s.lastScrollTop=0;else{this._fnInfo();clearTimeout(this.s.stateTO);this.s.stateTO=setTimeout(function(){a.s.dt.oApi._fnSaveState(a.s.dt)},250);if(c<this.s.redrawTop||c>this.s.redrawBottom){var f=Math.ceil((this.s.displayBuffer-1)/2*this.s.viewportRows);Math.abs(c-this.s.lastScrollTop)>b.viewport||this.s.ani?(d=parseInt(this._domain("physicalToVirtual",
15
+ c)/b.row,10)-f,this.s.topRowFloat=this._domain("physicalToVirtual",c)/b.row):(d=this.fnPixelsToRow(c)-f,this.s.topRowFloat=this.fnPixelsToRow(c,!1));0>=d?d=0:d+this.s.dt._iDisplayLength>this.s.dt.fnRecordsDisplay()?(d=this.s.dt.fnRecordsDisplay()-this.s.dt._iDisplayLength,0>d&&(d=0)):0!==d%2&&d++;if(d!=this.s.dt._iDisplayStart&&(this.s.tableTop=e(this.s.dt.nTable).offset().top,this.s.tableBottom=e(this.s.dt.nTable).height()+this.s.tableTop,b=function(){if(a.s.scrollDrawReq===null)a.s.scrollDrawReq=
16
+ c;a.s.dt._iDisplayStart=d;a.s.dt.oApi._fnDraw(a.s.dt)},this.s.dt.oFeatures.bServerSide?(clearTimeout(this.s.drawTO),this.s.drawTO=setTimeout(b,this.s.serverWait)):b(),this.dom.loader&&!this.s.loaderVisible))this.dom.loader.css("display","block"),this.s.loaderVisible=!0}else this.s.topRowFloat=this._domain("physicalToVirtual",c)/b.row;this.s.lastScrollTop=c;this.s.stateSaveThrottle()}},_domain:function(a,b){var c=this.s.heights,d;if(c.virtual===c.scroll)return b;var e=(c.scroll-c.viewport)/2,i=(c.virtual-
17
+ c.viewport)/2;d=i/(e*e);if("virtualToPhysical"===a){if(b<i)return Math.pow(b/d,0.5);b=2*i-b;return 0>b?c.scroll:2*e-Math.pow(b/d,0.5)}if("physicalToVirtual"===a){if(b<e)return b*b*d;b=2*e-b;return 0>b?c.virtual:2*i-b*b*d}},_fnDrawCallback:function(){var a=this,b=this.s.heights,c=this.dom.scroller.scrollTop,d=e(this.s.dt.nTable).height(),f=this.s.dt._iDisplayStart,i=this.s.dt._iDisplayLength,g=this.s.dt.fnRecordsDisplay();this.s.skip=!0;this._fnScrollForce();c=0===f?this.s.topRowFloat*b.row:f+i>=g?
18
+ b.scroll-(g-this.s.topRowFloat)*b.row:this._domain("virtualToPhysical",this.s.topRowFloat*b.row);this.dom.scroller.scrollTop=c;this.s.baseScrollTop=c;this.s.baseRowTop=this.s.topRowFloat;var h=c-(this.s.topRowFloat-f)*b.row;0===f?h=0:f+i>=g&&(h=b.scroll-d);this.dom.table.style.top=h+"px";this.s.tableTop=h;this.s.tableBottom=d+this.s.tableTop;d=(c-this.s.tableTop)*this.s.boundaryScale;this.s.redrawTop=c-d;this.s.redrawBottom=c+d;this.s.skip=!1;this.s.dt.oFeatures.bStateSave&&null!==this.s.dt.oLoadedState&&
19
+ "undefined"!=typeof this.s.dt.oLoadedState.iScroller?((c=(this.s.dt.sAjaxSource||a.s.dt.ajax)&&!this.s.dt.oFeatures.bServerSide?!0:!1)&&2==this.s.dt.iDraw||!c&&1==this.s.dt.iDraw)&&setTimeout(function(){e(a.dom.scroller).scrollTop(a.s.dt.oLoadedState.iScroller);a.s.redrawTop=a.s.dt.oLoadedState.iScroller-b.viewport/2;setTimeout(function(){a.s.ingnoreScroll=!1},0)},0):a.s.ingnoreScroll=!1;this.s.dt.oFeatures.bInfo&&setTimeout(function(){a._fnInfo.call(a)},0);this.dom.loader&&this.s.loaderVisible&&
20
+ (this.dom.loader.css("display","none"),this.s.loaderVisible=!1)},_fnScrollForce:function(){var a=this.s.heights;a.virtual=a.row*this.s.dt.fnRecordsDisplay();a.scroll=a.virtual;1E6<a.scroll&&(a.scroll=1E6);this.dom.force.style.height=a.scroll>this.s.heights.row?a.scroll+"px":this.s.heights.row+"px"},_fnCalcRowHeight:function(){var a=this.s.dt,b=a.nTable,c=b.cloneNode(!1),d=e("<tbody/>").appendTo(c),f=e('<div class="'+a.oClasses.sWrapper+' DTS"><div class="'+a.oClasses.sScrollWrapper+'"><div class="'+
21
+ a.oClasses.sScrollBody+'"></div></div></div>');for(e("tbody tr:lt(4)",b).clone().appendTo(d);3>e("tr",d).length;)d.append("<tr><td>&nbsp;</td></tr>");e("div."+a.oClasses.sScrollBody,f).append(c);a=this.s.dt.nHolding||b.parentNode;e(a).is(":visible")||(a="body");f.appendTo(a);this.s.heights.row=e("tr",d).eq(1).outerHeight();f.remove()},_fnInfo:function(){if(this.s.dt.oFeatures.bInfo){var a=this.s.dt,b=a.oLanguage,c=this.dom.scroller.scrollTop,d=Math.floor(this.fnPixelsToRow(c,!1,this.s.ani)+1),f=a.fnRecordsTotal(),
22
+ i=a.fnRecordsDisplay(),c=Math.ceil(this.fnPixelsToRow(c+this.s.heights.viewport,!1,this.s.ani)),c=i<c?i:c,g=a.fnFormatNumber(d),h=a.fnFormatNumber(c),j=a.fnFormatNumber(f),k=a.fnFormatNumber(i),g=0===a.fnRecordsDisplay()&&a.fnRecordsDisplay()==a.fnRecordsTotal()?b.sInfoEmpty+b.sInfoPostFix:0===a.fnRecordsDisplay()?b.sInfoEmpty+" "+b.sInfoFiltered.replace("_MAX_",j)+b.sInfoPostFix:a.fnRecordsDisplay()==a.fnRecordsTotal()?b.sInfo.replace("_START_",g).replace("_END_",h).replace("_MAX_",j).replace("_TOTAL_",
23
+ k)+b.sInfoPostFix:b.sInfo.replace("_START_",g).replace("_END_",h).replace("_MAX_",j).replace("_TOTAL_",k)+" "+b.sInfoFiltered.replace("_MAX_",a.fnFormatNumber(a.fnRecordsTotal()))+b.sInfoPostFix;(b=b.fnInfoCallback)&&(g=b.call(a.oInstance,a,d,c,f,i,g));d=a.aanFeatures.i;if("undefined"!=typeof d){f=0;for(i=d.length;f<i;f++)e(d[f]).html(g)}e(a.nTable).triggerHandler("info.dt")}}});g.defaults={trace:!1,rowHeight:"auto",serverWait:200,displayBuffer:9,boundaryScale:0.5,loadingIndicator:!1};g.oDefaults=
24
+ g.defaults;g.version="1.4.2";"function"==typeof e.fn.dataTable&&"function"==typeof e.fn.dataTableExt.fnVersionCheck&&e.fn.dataTableExt.fnVersionCheck("1.10.0")?e.fn.dataTableExt.aoFeatures.push({fnInit:function(a){var b=a.oInit;new g(a,b.scroller||b.oScroller||{})},cFeature:"S",sFeature:"Scroller"}):alert("Warning: Scroller requires DataTables 1.10.0 or greater - www.datatables.net/download");e(j).on("preInit.dt.dtscroller",function(a,b){if("dt"===a.namespace){var c=b.oInit.scroller,d=m.defaults.scroller;
25
+ if(c||d)d=e.extend({},c,d),!1!==c&&new g(b,d)}});e.fn.dataTable.Scroller=g;e.fn.DataTable.Scroller=g;var k=e.fn.dataTable.Api;k.register("scroller()",function(){return this});k.register("scroller().rowToPixels()",function(a,b,c){var d=this.context;if(d.length&&d[0].oScroller)return d[0].oScroller.fnRowToPixels(a,b,c)});k.register("scroller().pixelsToRow()",function(a,b,c){var d=this.context;if(d.length&&d[0].oScroller)return d[0].oScroller.fnPixelsToRow(a,b,c)});k.register("scroller().scrollToRow()",
26
+ function(a,b){this.iterator("table",function(c){c.oScroller&&c.oScroller.fnScrollToRow(a,b)});return this});k.register("row().scrollTo()",function(a){var b=this;this.iterator("row",function(c,d){if(c.oScroller){var e=b.rows({order:"applied",search:"applied"}).indexes().indexOf(d);c.oScroller.fnScrollToRow(e,a)}});return this});k.register("scroller.measure()",function(a){this.iterator("table",function(b){b.oScroller&&b.oScroller.fnMeasure(a)});return this});k.register("scroller.page()",function(){var a=
27
+ this.context;if(a.length&&a[0].oScroller)return a[0].oScroller.fnPageInfo()});return g});
common/vendor/datatables/extensions/Select/css/select.dataTables.css ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ table.dataTable tbody > tr.selected,
2
+ table.dataTable tbody > tr > .selected {
3
+ background-color: #B0BED9;
4
+ }
5
+ table.dataTable.stripe tbody > tr.odd.selected,
6
+ table.dataTable.stripe tbody > tr.odd > .selected, table.dataTable.display tbody > tr.odd.selected,
7
+ table.dataTable.display tbody > tr.odd > .selected {
8
+ background-color: #acbad4;
9
+ }
10
+ table.dataTable.hover tbody > tr.selected:hover,
11
+ table.dataTable.hover tbody > tr > .selected:hover, table.dataTable.display tbody > tr.selected:hover,
12
+ table.dataTable.display tbody > tr > .selected:hover {
13
+ background-color: #aab7d1;
14
+ }
15
+ table.dataTable.order-column tbody > tr.selected > .sorting_1,
16
+ table.dataTable.order-column tbody > tr.selected > .sorting_2,
17
+ table.dataTable.order-column tbody > tr.selected > .sorting_3,
18
+ table.dataTable.order-column tbody > tr > .selected, table.dataTable.display tbody > tr.selected > .sorting_1,
19
+ table.dataTable.display tbody > tr.selected > .sorting_2,
20
+ table.dataTable.display tbody > tr.selected > .sorting_3,
21
+ table.dataTable.display tbody > tr > .selected {
22
+ background-color: #acbad5;
23
+ }
24
+ table.dataTable.display tbody > tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody > tr.odd.selected > .sorting_1 {
25
+ background-color: #a6b4cd;
26
+ }
27
+ table.dataTable.display tbody > tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody > tr.odd.selected > .sorting_2 {
28
+ background-color: #a8b5cf;
29
+ }
30
+ table.dataTable.display tbody > tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody > tr.odd.selected > .sorting_3 {
31
+ background-color: #a9b7d1;
32
+ }
33
+ table.dataTable.display tbody > tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody > tr.even.selected > .sorting_1 {
34
+ background-color: #acbad5;
35
+ }
36
+ table.dataTable.display tbody > tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody > tr.even.selected > .sorting_2 {
37
+ background-color: #aebcd6;
38
+ }
39
+ table.dataTable.display tbody > tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody > tr.even.selected > .sorting_3 {
40
+ background-color: #afbdd8;
41
+ }
42
+ table.dataTable.display tbody > tr.odd > .selected, table.dataTable.order-column.stripe tbody > tr.odd > .selected {
43
+ background-color: #a6b4cd;
44
+ }
45
+ table.dataTable.display tbody > tr.even > .selected, table.dataTable.order-column.stripe tbody > tr.even > .selected {
46
+ background-color: #acbad5;
47
+ }
48
+ table.dataTable.display tbody > tr.selected:hover > .sorting_1, table.dataTable.order-column.hover tbody > tr.selected:hover > .sorting_1 {
49
+ background-color: #a2aec7;
50
+ }
51
+ table.dataTable.display tbody > tr.selected:hover > .sorting_2, table.dataTable.order-column.hover tbody > tr.selected:hover > .sorting_2 {
52
+ background-color: #a3b0c9;
53
+ }
54
+ table.dataTable.display tbody > tr.selected:hover > .sorting_3, table.dataTable.order-column.hover tbody > tr.selected:hover > .sorting_3 {
55
+ background-color: #a5b2cb;
56
+ }
57
+ table.dataTable.display tbody > tr:hover > .selected,
58
+ table.dataTable.display tbody > tr > .selected:hover, table.dataTable.order-column.hover tbody > tr:hover > .selected,
59
+ table.dataTable.order-column.hover tbody > tr > .selected:hover {
60
+ background-color: #a2aec7;
61
+ }
62
+ table.dataTable td.select-checkbox {
63
+ position: relative;
64
+ }
65
+ table.dataTable td.select-checkbox:before, table.dataTable td.select-checkbox:after {
66
+ display: block;
67
+ position: absolute;
68
+ top: 1.2em;
69
+ left: 50%;
70
+ width: 12px;
71
+ height: 12px;
72
+ box-sizing: border-box;
73
+ }
74
+ table.dataTable td.select-checkbox:before {
75
+ content: ' ';
76
+ margin-top: -6px;
77
+ margin-left: -6px;
78
+ border: 1px solid black;
79
+ border-radius: 3px;
80
+ }
81
+ table.dataTable tr.selected td.select-checkbox:after {
82
+ content: '\2714';
83
+ margin-top: -11px;
84
+ margin-left: -4px;
85
+ text-align: center;
86
+ text-shadow: 1px 1px #B0BED9, -1px -1px #B0BED9, 1px -1px #B0BED9, -1px 1px #B0BED9;
87
+ }
88
+
89
+ div.dataTables_wrapper span.select-info,
90
+ div.dataTables_wrapper span.select-item {
91
+ margin-left: 0.5em;
92
+ }
93
+
94
+ @media screen and (max-width: 640px) {
95
+ div.dataTables_wrapper span.select-info,
96
+ div.dataTables_wrapper span.select-item {
97
+ margin-left: 0;
98
+ display: block;
99
+ }
100
+ }
common/vendor/datatables/extensions/Select/css/select.dataTables.min.css ADDED
@@ -0,0 +1 @@
 
1
+ table.dataTable tbody>tr.selected,table.dataTable tbody>tr>.selected{background-color:#B0BED9}table.dataTable.stripe tbody>tr.odd.selected,table.dataTable.stripe tbody>tr.odd>.selected,table.dataTable.display tbody>tr.odd.selected,table.dataTable.display tbody>tr.odd>.selected{background-color:#acbad4}table.dataTable.hover tbody>tr.selected:hover,table.dataTable.hover tbody>tr>.selected:hover,table.dataTable.display tbody>tr.selected:hover,table.dataTable.display tbody>tr>.selected:hover{background-color:#aab7d1}table.dataTable.order-column tbody>tr.selected>.sorting_1,table.dataTable.order-column tbody>tr.selected>.sorting_2,table.dataTable.order-column tbody>tr.selected>.sorting_3,table.dataTable.order-column tbody>tr>.selected,table.dataTable.display tbody>tr.selected>.sorting_1,table.dataTable.display tbody>tr.selected>.sorting_2,table.dataTable.display tbody>tr.selected>.sorting_3,table.dataTable.display tbody>tr>.selected{background-color:#acbad5}table.dataTable.display tbody>tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_1{background-color:#a6b4cd}table.dataTable.display tbody>tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody>tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody>tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_1{background-color:#acbad5}table.dataTable.display tbody>tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody>tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody>tr.odd>.selected,table.dataTable.order-column.stripe tbody>tr.odd>.selected{background-color:#a6b4cd}table.dataTable.display tbody>tr.even>.selected,table.dataTable.order-column.stripe tbody>tr.even>.selected{background-color:#acbad5}table.dataTable.display tbody>tr.selected:hover>.sorting_1,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_1{background-color:#a2aec7}table.dataTable.display tbody>tr.selected:hover>.sorting_2,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody>tr.selected:hover>.sorting_3,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_3{background-color:#a5b2cb}table.dataTable.display tbody>tr:hover>.selected,table.dataTable.display tbody>tr>.selected:hover,table.dataTable.order-column.hover tbody>tr:hover>.selected,table.dataTable.order-column.hover tbody>tr>.selected:hover{background-color:#a2aec7}table.dataTable td.select-checkbox{position:relative}table.dataTable td.select-checkbox:before,table.dataTable td.select-checkbox:after{display:block;position:absolute;top:1.2em;left:50%;width:12px;height:12px;box-sizing:border-box}table.dataTable td.select-checkbox:before{content:' ';margin-top:-6px;margin-left:-6px;border:1px solid black;border-radius:3px}table.dataTable tr.selected td.select-checkbox:after{content:'\2714';margin-top:-11px;margin-left:-4px;text-align:center;text-shadow:1px 1px #B0BED9, -1px -1px #B0BED9, 1px -1px #B0BED9, -1px 1px #B0BED9}div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:0.5em}@media screen and (max-width: 640px){div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:0;display:block}}
common/vendor/datatables/extensions/Select/js/dataTables.select.js ADDED
@@ -0,0 +1,1109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*! Select for DataTables 1.2.0
2
+ * 2015-2016 SpryMedia Ltd - datatables.net/license/mit
3
+ */
4
+
5
+ /**
6
+ * @summary Select for DataTables
7
+ * @description A collection of API methods, events and buttons for DataTables
8
+ * that provides selection options of the items in a DataTable
9
+ * @version 1.2.0
10
+ * @file dataTables.select.js
11
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
12
+ * @contact datatables.net/forums
13
+ * @copyright Copyright 2015-2016 SpryMedia Ltd.
14
+ *
15
+ * This source file is free software, available under the following license:
16
+ * MIT license - http://datatables.net/license/mit
17
+ *
18
+ * This source file is distributed in the hope that it will be useful, but
19
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
21
+ *
22
+ * For details please refer to: http://www.datatables.net/extensions/select
23
+ */
24
+ (function( factory ){
25
+ if ( typeof define === 'function' && define.amd ) {
26
+ // AMD
27
+ define( ['jquery', 'datatables.net'], function ( $ ) {
28
+ return factory( $, window, document );
29
+ } );
30
+ }
31
+ else if ( typeof exports === 'object' ) {
32
+ // CommonJS
33
+ module.exports = function (root, $) {
34
+ if ( ! root ) {
35
+ root = window;
36
+ }
37
+
38
+ if ( ! $ || ! $.fn.dataTable ) {
39
+ $ = require('datatables.net')(root, $).$;
40
+ }
41
+
42
+ return factory( $, root, root.document );
43
+ };
44
+ }
45
+ else {
46
+ // Browser
47
+ factory( jQuery, window, document );
48
+ }
49
+ }(function( $, window, document, undefined ) {
50
+ 'use strict';
51
+ var DataTable = $.fn.dataTable;
52
+
53
+
54
+ // Version information for debugger
55
+ DataTable.select = {};
56
+
57
+ DataTable.select.version = '1.2.0';
58
+
59
+ DataTable.select.init = function ( dt ) {
60
+ var ctx = dt.settings()[0];
61
+ var init = ctx.oInit.select;
62
+ var defaults = DataTable.defaults.select;
63
+ var opts = init === undefined ?
64
+ defaults :
65
+ init;
66
+
67
+ // Set defaults
68
+ var items = 'row';
69
+ var style = 'api';
70
+ var blurable = false;
71
+ var info = true;
72
+ var selector = 'td, th';
73
+ var className = 'selected';
74
+
75
+ ctx._select = {};
76
+
77
+ // Initialisation customisations
78
+ if ( opts === true ) {
79
+ style = 'os';
80
+ }
81
+ else if ( typeof opts === 'string' ) {
82
+ style = opts;
83
+ }
84
+ else if ( $.isPlainObject( opts ) ) {
85
+ if ( opts.blurable !== undefined ) {
86
+ blurable = opts.blurable;
87
+ }
88
+
89
+ if ( opts.info !== undefined ) {
90
+ info = opts.info;
91
+ }
92
+
93
+ if ( opts.items !== undefined ) {
94
+ items = opts.items;
95
+ }
96
+
97
+ if ( opts.style !== undefined ) {
98
+ style = opts.style;
99
+ }
100
+
101
+ if ( opts.selector !== undefined ) {
102
+ selector = opts.selector;
103
+ }
104
+
105
+ if ( opts.className !== undefined ) {
106
+ className = opts.className;
107
+ }
108
+ }
109
+
110
+ dt.select.selector( selector );
111
+ dt.select.items( items );
112
+ dt.select.style( style );
113
+ dt.select.blurable( blurable );
114
+ dt.select.info( info );
115
+ ctx._select.className = className;
116
+
117
+
118
+ // Sort table based on selected rows. Requires Select Datatables extension
119
+ $.fn.dataTable.ext.order['select-checkbox'] = function ( settings, col ) {
120
+ return this.api().column( col, {order: 'index'} ).nodes().map( function ( td ) {
121
+ if ( settings._select.items === 'row' ) {
122
+ return $( td ).parent().hasClass( settings._select.className );
123
+ } else if ( settings._select.items === 'cell' ) {
124
+ return $( td ).hasClass( settings._select.className );
125
+ }
126
+ return false;
127
+ });
128
+ };
129
+
130
+ // If the init options haven't enabled select, but there is a selectable
131
+ // class name, then enable
132
+ if ( $( dt.table().node() ).hasClass( 'selectable' ) ) {
133
+ dt.select.style( 'os' );
134
+ }
135
+ };
136
+
137
+ /*
138
+
139
+ Select is a collection of API methods, event handlers, event emitters and
140
+ buttons (for the `Buttons` extension) for DataTables. It provides the following
141
+ features, with an overview of how they are implemented:
142
+
143
+ ## Selection of rows, columns and cells. Whether an item is selected or not is
144
+ stored in:
145
+
146
+ * rows: a `_select_selected` property which contains a boolean value of the
147
+ DataTables' `aoData` object for each row
148
+ * columns: a `_select_selected` property which contains a boolean value of the
149
+ DataTables' `aoColumns` object for each column
150
+ * cells: a `_selected_cells` property which contains an array of boolean values
151
+ of the `aoData` object for each row. The array is the same length as the
152
+ columns array, with each element of it representing a cell.
153
+
154
+ This method of using boolean flags allows Select to operate when nodes have not
155
+ been created for rows / cells (DataTables' defer rendering feature).
156
+
157
+ ## API methods
158
+
159
+ A range of API methods are available for triggering selection and de-selection
160
+ of rows. Methods are also available to configure the selection events that can
161
+ be triggered by an end user (such as which items are to be selected). To a large
162
+ extent, these of API methods *is* Select. It is basically a collection of helper
163
+ functions that can be used to select items in a DataTable.
164
+
165
+ Configuration of select is held in the object `_select` which is attached to the
166
+ DataTables settings object on initialisation. Select being available on a table
167
+ is not optional when Select is loaded, but its default is for selection only to
168
+ be available via the API - so the end user wouldn't be able to select rows
169
+ without additional configuration.
170
+
171
+ The `_select` object contains the following properties:
172
+
173
+ ```
174
+ {
175
+ items:string - Can be `rows`, `columns` or `cells`. Defines what item
176
+ will be selected if the user is allowed to activate row
177
+ selection using the mouse.
178
+ style:string - Can be `none`, `single`, `multi` or `os`. Defines the
179
+ interaction style when selecting items
180
+ blurable:boolean - If row selection can be cleared by clicking outside of
181
+ the table
182
+ info:boolean - If the selection summary should be shown in the table
183
+ information elements
184
+ }
185
+ ```
186
+
187
+ In addition to the API methods, Select also extends the DataTables selector
188
+ options for rows, columns and cells adding a `selected` option to the selector
189
+ options object, allowing the developer to select only selected items or
190
+ unselected items.
191
+
192
+ ## Mouse selection of items
193
+
194
+ Clicking on items can be used to select items. This is done by a simple event
195
+ handler that will select the items using the API methods.
196
+
197
+ */
198
+
199
+
200
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
201
+ * Local functions
202
+ */
203
+
204
+ /**
205
+ * Add one or more cells to the selection when shift clicking in OS selection
206
+ * style cell selection.
207
+ *
208
+ * Cell range is more complicated than row and column as we want to select
209
+ * in the visible grid rather than by index in sequence. For example, if you
210
+ * click first in cell 1-1 and then shift click in 2-2 - cells 1-2 and 2-1
211
+ * should also be selected (and not 1-3, 1-4. etc)
212
+ *
213
+ * @param {DataTable.Api} dt DataTable
214
+ * @param {object} idx Cell index to select to
215
+ * @param {object} last Cell index to select from
216
+ * @private
217
+ */
218
+ function cellRange( dt, idx, last )
219
+ {
220
+ var indexes;
221
+ var columnIndexes;
222
+ var rowIndexes;
223
+ var selectColumns = function ( start, end ) {
224
+ if ( start > end ) {
225
+ var tmp = end;
226
+ end = start;
227
+ start = tmp;
228
+ }
229
+
230
+ var record = false;
231
+ return dt.columns( ':visible' ).indexes().filter( function (i) {
232
+ if ( i === start ) {
233
+ record = true;
234
+ }
235
+
236
+ if ( i === end ) { // not else if, as start might === end
237
+ record = false;
238
+ return true;
239
+ }
240
+
241
+ return record;
242
+ } );
243
+ };
244
+
245
+ var selectRows = function ( start, end ) {
246
+ var indexes = dt.rows( { search: 'applied' } ).indexes();
247
+
248
+ // Which comes first - might need to swap
249
+ if ( indexes.indexOf( start ) > indexes.indexOf( end ) ) {
250
+ var tmp = end;
251
+ end = start;
252
+ start = tmp;
253
+ }
254
+
255
+ var record = false;
256
+ return indexes.filter( function (i) {
257
+ if ( i === start ) {
258
+ record = true;
259
+ }
260
+
261
+ if ( i === end ) {
262
+ record = false;
263
+ return true;
264
+ }
265
+
266
+ return record;
267
+ } );
268
+ };
269
+
270
+ if ( ! dt.cells( { selected: true } ).any() && ! last ) {
271
+ // select from the top left cell to this one
272
+ columnIndexes = selectColumns( 0, idx.column );
273
+ rowIndexes = selectRows( 0 , idx.row );
274
+ }
275
+ else {
276
+ // Get column indexes between old and new
277
+ columnIndexes = selectColumns( last.column, idx.column );
278
+ rowIndexes = selectRows( last.row , idx.row );
279
+ }
280
+
281
+ indexes = dt.cells( rowIndexes, columnIndexes ).flatten();
282
+
283
+ if ( ! dt.cells( idx, { selected: true } ).any() ) {
284
+ // Select range
285
+ dt.cells( indexes ).select();
286
+ }
287
+ else {
288
+ // Deselect range
289
+ dt.cells( indexes ).deselect();
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Disable mouse selection by removing the selectors
295
+ *
296
+ * @param {DataTable.Api} dt DataTable to remove events from
297
+ * @private
298
+ */
299
+ function disableMouseSelection( dt )
300
+ {
301
+ var ctx = dt.settings()[0];
302
+ var selector = ctx._select.selector;
303
+
304
+ $( dt.table().body() )
305
+ .off( 'mousedown.dtSelect', selector )
306
+ .off( 'mouseup.dtSelect', selector )
307
+ .off( 'click.dtSelect', selector );
308
+
309
+ $('body').off( 'click.dtSelect' );
310
+ }
311
+
312
+ /**
313
+ * Attach mouse listeners to the table to allow mouse selection of items
314
+ *
315
+ * @param {DataTable.Api} dt DataTable to remove events from
316
+ * @private
317
+ */
318
+ function enableMouseSelection ( dt )
319
+ {
320
+ var body = $( dt.table().body() );
321
+ var ctx = dt.settings()[0];
322
+ var selector = ctx._select.selector;
323
+
324
+ body
325
+ .on( 'mousedown.dtSelect', selector, function(e) {
326
+ // Disallow text selection for shift clicking on the table so multi
327
+ // element selection doesn't look terrible!
328
+ if ( e.shiftKey || e.metaKey || e.ctrlKey ) {
329
+ body
330
+ .css( '-moz-user-select', 'none' )
331
+ .one('selectstart.dtSelect', selector, function () {
332
+ return false;
333
+ } );
334
+ }
335
+ } )
336
+ .on( 'mouseup.dtSelect', selector, function() {
337
+ // Allow text selection to occur again, Mozilla style (tested in FF
338
+ // 35.0.1 - still required)
339
+ body.css( '-moz-user-select', '' );
340
+ } )
341
+ .on( 'click.dtSelect', selector, function ( e ) {
342
+ var items = dt.select.items();
343
+ var idx;
344
+
345
+ // If text was selected (click and drag), then we shouldn't change
346
+ // the row's selected state
347
+ if ( window.getSelection && window.getSelection().toString() ) {
348
+ return;
349
+ }
350
+
351
+ var ctx = dt.settings()[0];
352
+
353
+ // Ignore clicks inside a sub-table
354
+ if ( $(e.target).closest('div.dataTables_wrapper')[0] != dt.table().container() ) {
355
+ return;
356
+ }
357
+
358
+ var cell = dt.cell( $(e.target).closest('td, th') );
359
+
360
+ // Check the cell actually belongs to the host DataTable (so child
361
+ // rows, etc, are ignored)
362
+ if ( ! cell.any() ) {
363
+ return;
364
+ }
365
+
366
+ var event = $.Event('user-select.dt');
367
+ eventTrigger( dt, event, [ items, cell, e ] );
368
+
369
+ if ( event.isDefaultPrevented() ) {
370
+ return;
371
+ }
372
+
373
+ var cellIndex = cell.index();
374
+ if ( items === 'row' ) {
375
+ idx = cellIndex.row;
376
+ typeSelect( e, dt, ctx, 'row', idx );
377
+ }
378
+ else if ( items === 'column' ) {
379
+ idx = cell.index().column;
380
+ typeSelect( e, dt, ctx, 'column', idx );
381
+ }
382
+ else if ( items === 'cell' ) {
383
+ idx = cell.index();
384
+ typeSelect( e, dt, ctx, 'cell', idx );
385
+ }
386
+
387
+ ctx._select_lastCell = cellIndex;
388
+ } );
389
+
390
+ // Blurable
391
+ $('body').on( 'click.dtSelect', function ( e ) {
392
+ if ( ctx._select.blurable ) {
393
+ // If the click was inside the DataTables container, don't blur
394
+ if ( $(e.target).parents().filter( dt.table().container() ).length ) {
395
+ return;
396
+ }
397
+
398
+ // Don't blur in Editor form
399
+ if ( $(e.target).parents('div.DTE').length ) {
400
+ return;
401
+ }
402
+
403
+ clear( ctx, true );
404
+ }
405
+ } );
406
+ }
407
+
408
+ /**
409
+ * Trigger an event on a DataTable
410
+ *
411
+ * @param {DataTable.Api} api DataTable to trigger events on
412
+ * @param {boolean} selected true if selected, false if deselected
413
+ * @param {string} type Item type acting on
414
+ * @param {boolean} any Require that there are values before
415
+ * triggering
416
+ * @private
417
+ */
418
+ function eventTrigger ( api, type, args, any )
419
+ {
420
+ if ( any && ! api.flatten().length ) {
421
+ return;
422
+ }
423
+
424
+ if ( typeof type === 'string' ) {
425
+ type = type +'.dt';
426
+ }
427
+
428
+ args.unshift( api );
429
+
430
+ $(api.table().node()).triggerHandler( type, args );
431
+ }
432
+
433
+ /**
434
+ * Update the information element of the DataTable showing information about the
435
+ * items selected. This is done by adding tags to the existing text
436
+ *
437
+ * @param {DataTable.Api} api DataTable to update
438
+ * @private
439
+ */
440
+ function info ( api )
441
+ {
442
+ var ctx = api.settings()[0];
443
+
444
+ if ( ! ctx._select.info || ! ctx.aanFeatures.i ) {
445
+ return;
446
+ }
447
+
448
+ var output = $('<span class="select-info"/>');
449
+ var add = function ( name, num ) {
450
+ output.append( $('<span class="select-item"/>').append( api.i18n(
451
+ 'select.'+name+'s',
452
+ { _: '%d '+name+'s selected', 0: '', 1: '1 '+name+' selected' },
453
+ num
454
+ ) ) );
455
+ };
456
+
457
+ add( 'row', api.rows( { selected: true } ).flatten().length );
458
+ add( 'column', api.columns( { selected: true } ).flatten().length );
459
+ add( 'cell', api.cells( { selected: true } ).flatten().length );
460
+
461
+ // Internal knowledge of DataTables to loop over all information elements
462
+ $.each( ctx.aanFeatures.i, function ( i, el ) {
463
+ el = $(el);
464
+
465
+ var exisiting = el.children('span.select-info');
466
+ if ( exisiting.length ) {
467
+ exisiting.remove();
468
+ }
469
+
470
+ if ( output.text() !== '' ) {
471
+ el.append( output );
472
+ }
473
+ } );
474
+ }
475
+
476
+ /**
477
+ * Initialisation of a new table. Attach event handlers and callbacks to allow
478
+ * Select to operate correctly.
479
+ *
480
+ * This will occur _after_ the initial DataTables initialisation, although
481
+ * before Ajax data is rendered, if there is ajax data
482
+ *
483
+ * @param {DataTable.settings} ctx Settings object to operate on
484
+ * @private
485
+ */
486
+ function init ( ctx ) {
487
+ var api = new DataTable.Api( ctx );
488
+
489
+ // Row callback so that classes can be added to rows and cells if the item
490
+ // was selected before the element was created. This will happen with the
491
+ // `deferRender` option enabled.
492
+ //
493
+ // This method of attaching to `aoRowCreatedCallback` is a hack until
494
+ // DataTables has proper events for row manipulation If you are reviewing
495
+ // this code to create your own plug-ins, please do not do this!
496
+ ctx.aoRowCreatedCallback.push( {
497
+ fn: function ( row, data, index ) {
498
+ var i, ien;
499
+ var d = ctx.aoData[ index ];
500
+
501
+ // Row
502
+ if ( d._select_selected ) {
503
+ $( row ).addClass( ctx._select.className );
504
+ }
505
+
506
+ // Cells and columns - if separated out, we would need to do two
507
+ // loops, so it makes sense to combine them into a single one
508
+ for ( i=0, ien=ctx.aoColumns.length ; i<ien ; i++ ) {
509
+ if ( ctx.aoColumns[i]._select_selected || (d._selected_cells && d._selected_cells[i]) ) {
510
+ $(d.anCells[i]).addClass( ctx._select.className );
511
+ }
512
+ }
513
+ },
514
+ sName: 'select-deferRender'
515
+ } );
516
+
517
+ // On Ajax reload we want to reselect all rows which are currently selected,
518
+ // if there is an rowId (i.e. a unique value to identify each row with)
519
+ api.on( 'preXhr.dt.dtSelect', function () {
520
+ // note that column selection doesn't need to be cached and then
521
+ // reselected, as they are already selected
522
+ var rows = api.rows( { selected: true } ).ids( true ).filter( function ( d ) {
523
+ return d !== undefined;
524
+ } );
525
+
526
+ var cells = api.cells( { selected: true } ).eq(0).map( function ( cellIdx ) {
527
+ var id = api.row( cellIdx.row ).id( true );
528
+ return id ?
529
+ { row: id, column: cellIdx.column } :
530
+ undefined;
531
+ } ).filter( function ( d ) {
532
+ return d !== undefined;
533
+ } );
534
+
535
+ // On the next draw, reselect the currently selected items
536
+ api.one( 'draw.dt.dtSelect', function () {
537
+ api.rows( rows ).select();
538
+
539
+ // `cells` is not a cell index selector, so it needs a loop
540
+ if ( cells.any() ) {
541
+ cells.each( function ( id ) {
542
+ api.cells( id.row, id.column ).select();
543
+ } );
544
+ }
545
+ } );
546
+ } );
547
+
548
+ // Update the table information element with selected item summary
549
+ api.on( 'draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt info.dt', function () {
550
+ info( api );
551
+ } );
552
+
553
+ // Clean up and release
554
+ api.on( 'destroy.dtSelect', function () {
555
+ disableMouseSelection( api );
556
+ api.off( '.dtSelect' );
557
+ } );
558
+ }
559
+
560
+ /**
561
+ * Add one or more items (rows or columns) to the selection when shift clicking
562
+ * in OS selection style
563
+ *
564
+ * @param {DataTable.Api} dt DataTable
565
+ * @param {string} type Row or column range selector
566
+ * @param {object} idx Item index to select to
567
+ * @param {object} last Item index to select from
568
+ * @private
569
+ */
570
+ function rowColumnRange( dt, type, idx, last )
571
+ {
572
+ // Add a range of rows from the last selected row to this one
573
+ var indexes = dt[type+'s']( { search: 'applied' } ).indexes();
574
+ var idx1 = $.inArray( last, indexes );
575
+ var idx2 = $.inArray( idx, indexes );
576
+
577
+ if ( ! dt[type+'s']( { selected: true } ).any() && idx1 === -1 ) {
578
+ // select from top to here - slightly odd, but both Windows and Mac OS
579
+ // do this
580
+ indexes.splice( $.inArray( idx, indexes )+1, indexes.length );
581
+ }
582
+ else {
583
+ // reverse so we can shift click 'up' as well as down
584
+ if ( idx1 > idx2 ) {
585
+ var tmp = idx2;
586
+ idx2 = idx1;
587
+ idx1 = tmp;
588
+ }
589
+
590
+ indexes.splice( idx2+1, indexes.length );
591
+ indexes.splice( 0, idx1 );
592
+ }
593
+
594
+ if ( ! dt[type]( idx, { selected: true } ).any() ) {
595
+ // Select range
596
+ dt[type+'s']( indexes ).select();
597
+ }
598
+ else {
599
+ // Deselect range - need to keep the clicked on row selected
600
+ indexes.splice( $.inArray( idx, indexes ), 1 );
601
+ dt[type+'s']( indexes ).deselect();
602
+ }
603
+ }
604
+
605
+ /**
606
+ * Clear all selected items
607
+ *
608
+ * @param {DataTable.settings} ctx Settings object of the host DataTable
609
+ * @param {boolean} [force=false] Force the de-selection to happen, regardless
610
+ * of selection style
611
+ * @private
612
+ */
613
+ function clear( ctx, force )
614
+ {
615
+ if ( force || ctx._select.style === 'single' ) {
616
+ var api = new DataTable.Api( ctx );
617
+
618
+ api.rows( { selected: true } ).deselect();
619
+ api.columns( { selected: true } ).deselect();
620
+ api.cells( { selected: true } ).deselect();
621
+ }
622
+ }
623
+
624
+ /**
625
+ * Select items based on the current configuration for style and items.
626
+ *
627
+ * @param {object} e Mouse event object
628
+ * @param {DataTables.Api} dt DataTable
629
+ * @param {DataTable.settings} ctx Settings object of the host DataTable
630
+ * @param {string} type Items to select
631
+ * @param {int|object} idx Index of the item to select
632
+ * @private
633
+ */
634
+ function typeSelect ( e, dt, ctx, type, idx )
635
+ {
636
+ var style = dt.select.style();
637
+ var isSelected = dt[type]( idx, { selected: true } ).any();
638
+
639
+ if ( style === 'os' ) {
640
+ if ( e.ctrlKey || e.metaKey ) {
641
+ // Add or remove from the selection
642
+ dt[type]( idx ).select( ! isSelected );
643
+ }
644
+ else if ( e.shiftKey ) {
645
+ if ( type === 'cell' ) {
646
+ cellRange( dt, idx, ctx._select_lastCell || null );
647
+ }
648
+ else {
649
+ rowColumnRange( dt, type, idx, ctx._select_lastCell ?
650
+ ctx._select_lastCell[type] :
651
+ null
652
+ );
653
+ }
654
+ }
655
+ else {
656
+ // No cmd or shift click - deselect if selected, or select
657
+ // this row only
658
+ var selected = dt[type+'s']( { selected: true } );
659
+
660
+ if ( isSelected && selected.flatten().length === 1 ) {
661
+ dt[type]( idx ).deselect();
662
+ }
663
+ else {
664
+ selected.deselect();
665
+ dt[type]( idx ).select();
666
+ }
667
+ }
668
+ } else if ( style == 'multi+shift' ) {
669
+ if ( e.shiftKey ) {
670
+ if ( type === 'cell' ) {
671
+ cellRange( dt, idx, ctx._select_lastCell || null );
672
+ }
673
+ else {
674
+ rowColumnRange( dt, type, idx, ctx._select_lastCell ?
675
+ ctx._select_lastCell[type] :
676
+ null
677
+ );
678
+ }
679
+ }
680
+ else {
681
+ dt[ type ]( idx ).select( ! isSelected );
682
+ }
683
+ }
684
+ else {
685
+ dt[ type ]( idx ).select( ! isSelected );
686
+ }
687
+ }
688
+
689
+
690
+
691
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
692
+ * DataTables selectors
693
+ */
694
+
695
+ // row and column are basically identical just assigned to different properties
696
+ // and checking a different array, so we can dynamically create the functions to
697
+ // reduce the code size
698
+ $.each( [
699
+ { type: 'row', prop: 'aoData' },
700
+ { type: 'column', prop: 'aoColumns' }
701
+ ], function ( i, o ) {
702
+ DataTable.ext.selector[ o.type ].push( function ( settings, opts, indexes ) {
703
+ var selected = opts.selected;
704
+ var data;
705
+ var out = [];
706
+
707
+ if ( selected === undefined ) {
708
+ return indexes;
709
+ }
710
+
711
+ for ( var i=0, ien=indexes.length ; i<ien ; i++ ) {
712
+ data = settings[ o.prop ][ indexes[i] ];
713
+
714
+ if ( (selected === true && data._select_selected === true) ||
715
+ (selected === false && ! data._select_selected )
716
+ ) {
717
+ out.push( indexes[i] );
718
+ }
719
+ }
720
+
721
+ return out;
722
+ } );
723
+ } );
724
+
725
+ DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {
726
+ var selected = opts.selected;
727
+ var rowData;
728
+ var out = [];
729
+
730
+ if ( selected === undefined ) {
731
+ return cells;
732
+ }
733
+
734
+ for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
735
+ rowData = settings.aoData[ cells[i].row ];
736
+
737
+ if ( (selected === true && rowData._selected_cells && rowData._selected_cells[ cells[i].column ] === true) ||
738
+ (selected === false && ( ! rowData._selected_cells || ! rowData._selected_cells[ cells[i].column ] ) )
739
+ ) {
740
+ out.push( cells[i] );
741
+ }
742
+ }
743
+
744
+ return out;
745
+ } );
746
+
747
+
748
+
749
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
750
+ * DataTables API
751
+ *
752
+ * For complete documentation, please refer to the docs/api directory or the
753
+ * DataTables site
754
+ */
755
+
756
+ // Local variables to improve compression
757
+ var apiRegister = DataTable.Api.register;
758
+ var apiRegisterPlural = DataTable.Api.registerPlural;
759
+
760
+ apiRegister( 'select()', function () {
761
+ return this.iterator( 'table', function ( ctx ) {
762
+ DataTable.select.init( new DataTable.Api( ctx ) );
763
+ } );
764
+ } );
765
+
766
+ apiRegister( 'select.blurable()', function ( flag ) {
767
+ if ( flag === undefined ) {
768
+ return this.context[0]._select.blurable;
769
+ }
770
+
771
+ return this.iterator( 'table', function ( ctx ) {
772
+ ctx._select.blurable = flag;
773
+ } );
774
+ } );
775
+
776
+ apiRegister( 'select.info()', function ( flag ) {
777
+ if ( info === undefined ) {
778
+ return this.context[0]._select.info;
779
+ }
780
+
781
+ return this.iterator( 'table', function ( ctx ) {
782
+ ctx._select.info = flag;
783
+ } );
784
+ } );
785
+
786
+ apiRegister( 'select.items()', function ( items ) {
787
+ if ( items === undefined ) {
788
+ return this.context[0]._select.items;
789
+ }
790
+
791
+ return this.iterator( 'table', function ( ctx ) {
792
+ ctx._select.items = items;
793
+
794
+ eventTrigger( new DataTable.Api( ctx ), 'selectItems', [ items ] );
795
+ } );
796
+ } );
797
+
798
+ // Takes effect from the _next_ selection. None disables future selection, but
799
+ // does not clear the current selection. Use the `deselect` methods for that
800
+ apiRegister( 'select.style()', function ( style ) {
801
+ if ( style === undefined ) {
802
+ return this.context[0]._select.style;
803
+ }
804
+
805
+ return this.iterator( 'table', function ( ctx ) {
806
+ ctx._select.style = style;
807
+
808
+ if ( ! ctx._select_init ) {
809
+ init( ctx );
810
+ }
811
+
812
+ // Add / remove mouse event handlers. They aren't required when only
813
+ // API selection is available
814
+ var dt = new DataTable.Api( ctx );
815
+ disableMouseSelection( dt );
816
+
817
+ if ( style !== 'api' ) {
818
+ enableMouseSelection( dt );
819
+ }
820
+
821
+ eventTrigger( new DataTable.Api( ctx ), 'selectStyle', [ style ] );
822
+ } );
823
+ } );
824
+
825
+ apiRegister( 'select.selector()', function ( selector ) {
826
+ if ( selector === undefined ) {
827
+ return this.context[0]._select.selector;
828
+ }
829
+
830
+ return this.iterator( 'table', function ( ctx ) {
831
+ disableMouseSelection( new DataTable.Api( ctx ) );
832
+
833
+ ctx._select.selector = selector;
834
+
835
+ if ( ctx._select.style !== 'api' ) {
836
+ enableMouseSelection( new DataTable.Api( ctx ) );
837
+ }
838
+ } );
839
+ } );
840
+
841
+
842
+
843
+ apiRegisterPlural( 'rows().select()', 'row().select()', function ( select ) {
844
+ var api = this;
845
+
846
+ if ( select === false ) {
847
+ return this.deselect();
848
+ }
849
+
850
+ this.iterator( 'row', function ( ctx, idx ) {
851
+ clear( ctx );
852
+
853
+ ctx.aoData[ idx ]._select_selected = true;
854
+ $( ctx.aoData[ idx ].nTr ).addClass( ctx._select.className );
855
+ } );
856
+
857
+ this.iterator( 'table', function ( ctx, i ) {
858
+ eventTrigger( api, 'select', [ 'row', api[i] ], true );
859
+ } );
860
+
861
+ return this;
862
+ } );
863
+
864
+ apiRegisterPlural( 'columns().select()', 'column().select()', function ( select ) {
865
+ var api = this;
866
+
867
+ if ( select === false ) {
868
+ return this.deselect();
869
+ }
870
+
871
+ this.iterator( 'column', function ( ctx, idx ) {
872
+ clear( ctx );
873
+
874
+ ctx.aoColumns[ idx ]._select_selected = true;
875
+
876
+ var column = new DataTable.Api( ctx ).column( idx );
877
+
878
+ $( column.header() ).addClass( ctx._select.className );
879
+ $( column.footer() ).addClass( ctx._select.className );
880
+
881
+ column.nodes().to$().addClass( ctx._select.className );
882
+ } );
883
+
884
+ this.iterator( 'table', function ( ctx, i ) {
885
+ eventTrigger( api, 'select', [ 'column', api[i] ], true );
886
+ } );
887
+
888
+ return this;
889
+ } );
890
+
891
+ apiRegisterPlural( 'cells().select()', 'cell().select()', function ( select ) {
892
+ var api = this;
893
+
894
+ if ( select === false ) {
895
+ return this.deselect();
896
+ }
897
+
898
+ this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
899
+ clear( ctx );
900
+
901
+ var data = ctx.aoData[ rowIdx ];
902
+
903
+ if ( data._selected_cells === undefined ) {
904
+ data._selected_cells = [];
905
+ }
906
+
907
+ data._selected_cells[ colIdx ] = true;
908
+
909
+ if ( data.anCells ) {
910
+ $( data.anCells[ colIdx ] ).addClass( ctx._select.className );
911
+ }
912
+ } );
913
+
914
+ this.iterator( 'table', function ( ctx, i ) {
915
+ eventTrigger( api, 'select', [ 'cell', api[i] ], true );
916
+ } );
917
+
918
+ return this;
919
+ } );
920
+
921
+
922
+ apiRegisterPlural( 'rows().deselect()', 'row().deselect()', function () {
923
+ var api = this;
924
+
925
+ this.iterator( 'row', function ( ctx, idx ) {
926
+ ctx.aoData[ idx ]._select_selected = false;
927
+ $( ctx.aoData[ idx ].nTr ).removeClass( ctx._select.className );
928
+ } );
929
+
930
+ this.iterator( 'table', function ( ctx, i ) {
931
+ eventTrigger( api, 'deselect', [ 'row', api[i] ], true );
932
+ } );
933
+
934
+ return this;
935
+ } );
936
+
937
+ apiRegisterPlural( 'columns().deselect()', 'column().deselect()', function () {
938
+ var api = this;
939
+
940
+ this.iterator( 'column', function ( ctx, idx ) {
941
+ ctx.aoColumns[ idx ]._select_selected = false;
942
+
943
+ var api = new DataTable.Api( ctx );
944
+ var column = api.column( idx );
945
+
946
+ $( column.header() ).removeClass( ctx._select.className );
947
+ $( column.footer() ).removeClass( ctx._select.className );
948
+
949
+ // Need to loop over each cell, rather than just using
950
+ // `column().nodes()` as cells which are individually selected should
951
+ // not have the `selected` class removed from them
952
+ api.cells( null, idx ).indexes().each( function (cellIdx) {
953
+ var data = ctx.aoData[ cellIdx.row ];
954
+ var cellSelected = data._selected_cells;
955
+
956
+ if ( data.anCells && (! cellSelected || ! cellSelected[ cellIdx.column ]) ) {
957
+ $( data.anCells[ cellIdx.column ] ).removeClass( ctx._select.className );
958
+ }
959
+ } );
960
+ } );
961
+
962
+ this.iterator( 'table', function ( ctx, i ) {
963
+ eventTrigger( api, 'deselect', [ 'column', api[i] ], true );
964
+ } );
965
+
966
+ return this;
967
+ } );
968
+
969
+ apiRegisterPlural( 'cells().deselect()', 'cell().deselect()', function () {
970
+ var api = this;
971
+
972
+ this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
973
+ var data = ctx.aoData[ rowIdx ];
974
+
975
+ data._selected_cells[ colIdx ] = false;
976
+
977
+ // Remove class only if the cells exist, and the cell is not column
978
+ // selected, in which case the class should remain (since it is selected
979
+ // in the column)
980
+ if ( data.anCells && ! ctx.aoColumns[ colIdx ]._select_selected ) {
981
+ $( data.anCells[ colIdx ] ).removeClass( ctx._select.className );
982
+ }
983
+ } );
984
+
985
+ this.iterator( 'table', function ( ctx, i ) {
986
+ eventTrigger( api, 'deselect', [ 'cell', api[i] ], true );
987
+ } );
988
+
989
+ return this;
990
+ } );
991
+
992
+
993
+
994
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
995
+ * Buttons
996
+ */
997
+ function i18n( label, def ) {
998
+ return function (dt) {
999
+ return dt.i18n( 'buttons.'+label, def );
1000
+ };
1001
+ }
1002
+
1003
+ $.extend( DataTable.ext.buttons, {
1004
+ selected: {
1005
+ text: i18n( 'selected', 'Selected' ),
1006
+ className: 'buttons-selected',
1007
+ init: function ( dt ) {
1008
+ var that = this;
1009
+
1010
+ // .DT namespace listeners are removed by DataTables automatically
1011
+ // on table destroy
1012
+ dt.on( 'draw.dt.DT select.dt.DT deselect.dt.DT', function () {
1013
+ var enable = that.rows( { selected: true } ).any() ||
1014
+ that.columns( { selected: true } ).any() ||
1015
+ that.cells( { selected: true } ).any();
1016
+
1017
+ that.enable( enable );
1018
+ } );
1019
+
1020
+ this.disable();
1021
+ }
1022
+ },
1023
+ selectedSingle: {
1024
+ text: i18n( 'selectedSingle', 'Selected single' ),
1025
+ className: 'buttons-selected-single',
1026
+ init: function ( dt ) {
1027
+ var that = this;
1028
+
1029
+ dt.on( 'draw.dt.DT select.dt.DT deselect.dt.DT', function () {
1030
+ var count = dt.rows( { selected: true } ).flatten().length +
1031
+ dt.columns( { selected: true } ).flatten().length +
1032
+ dt.cells( { selected: true } ).flatten().length;
1033
+
1034
+ that.enable( count === 1 );
1035
+ } );
1036
+
1037
+ this.disable();
1038
+ }
1039
+ },
1040
+ selectAll: {
1041
+ text: i18n( 'selectAll', 'Select all' ),
1042
+ className: 'buttons-select-all',
1043
+ action: function () {
1044
+ var items = this.select.items();
1045
+ this[ items+'s' ]().select();
1046
+ }
1047
+ },
1048
+ selectNone: {
1049
+ text: i18n( 'selectNone', 'Deselect all' ),
1050
+ className: 'buttons-select-none',
1051
+ action: function () {
1052
+ clear( this.settings()[0], true );
1053
+ },
1054
+ init: function ( dt ) {
1055
+ var that = this;
1056
+
1057
+ dt.on( 'draw.dt.DT select.dt.DT deselect.dt.DT', function () {
1058
+ var count = dt.rows( { selected: true } ).flatten().length +
1059
+ dt.columns( { selected: true } ).flatten().length +
1060
+ dt.cells( { selected: true } ).flatten().length;
1061
+
1062
+ that.enable( count > 0 );
1063
+ } );
1064
+
1065
+ this.disable();
1066
+ }
1067
+ }
1068
+ } );
1069
+
1070
+ $.each( [ 'Row', 'Column', 'Cell' ], function ( i, item ) {
1071
+ var lc = item.toLowerCase();
1072
+
1073
+ DataTable.ext.buttons[ 'select'+item+'s' ] = {
1074
+ text: i18n( 'select'+item+'s', 'Select '+lc+'s' ),
1075
+ className: 'buttons-select-'+lc+'s',
1076
+ action: function () {
1077
+ this.select.items( lc );
1078
+ },
1079
+ init: function ( dt ) {
1080
+ var that = this;
1081
+
1082
+ dt.on( 'selectItems.dt.DT', function ( e, ctx, items ) {
1083
+ that.active( items === lc );
1084
+ } );
1085
+ }
1086
+ };
1087
+ } );
1088
+
1089
+
1090
+
1091
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1092
+ * Initialisation
1093
+ */
1094
+
1095
+ // DataTables creation - check if select has been defined in the options. Note
1096
+ // this required that the table be in the document! If it isn't then something
1097
+ // needs to trigger this method unfortunately. The next major release of
1098
+ // DataTables will rework the events and address this.
1099
+ $(document).on( 'preInit.dt.dtSelect', function (e, ctx) {
1100
+ if ( e.namespace !== 'dt' ) {
1101
+ return;
1102
+ }
1103
+
1104
+ DataTable.select.init( new DataTable.Api( ctx ) );
1105
+ } );
1106
+
1107
+
1108
+ return DataTable.select;
1109
+ }));
common/vendor/datatables/extensions/Select/js/dataTables.select.min.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ Select for DataTables 1.2.0
3
+ 2015-2016 SpryMedia Ltd - datatables.net/license/mit
4
+ */
5
+ (function(e){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(i){return e(i,window,document)}):"object"===typeof exports?module.exports=function(i,l){i||(i=window);if(!l||!l.fn.dataTable)l=require("datatables.net")(i,l).$;return e(l,i,i.document)}:e(jQuery,window,document)})(function(e,i,l,h){function t(b,a,c){var d;d=function(c,a){if(c>a)var d=a,a=c,c=d;var f=!1;return b.columns(":visible").indexes().filter(function(b){b===c&&(f=!0);return b===a?(f=!1,!0):f})};var f=
6
+ function(c,a){var d=b.rows({search:"applied"}).indexes();if(d.indexOf(c)>d.indexOf(a))var f=a,a=c,c=f;var e=!1;return d.filter(function(b){b===c&&(e=!0);return b===a?(e=!1,!0):e})};!b.cells({selected:!0}).any()&&!c?(d=d(0,a.column),c=f(0,a.row)):(d=d(c.column,a.column),c=f(c.row,a.row));c=b.cells(c,d).flatten();b.cells(a,{selected:!0}).any()?b.cells(c).deselect():b.cells(c).select()}function r(b){var a=b.settings()[0]._select.selector;e(b.table().body()).off("mousedown.dtSelect",a).off("mouseup.dtSelect",
7
+ a).off("click.dtSelect",a);e("body").off("click.dtSelect")}function v(b){var a=e(b.table().body()),c=b.settings()[0],d=c._select.selector;a.on("mousedown.dtSelect",d,function(c){if(c.shiftKey||c.metaKey||c.ctrlKey)a.css("-moz-user-select","none").one("selectstart.dtSelect",d,function(){return!1})}).on("mouseup.dtSelect",d,function(){a.css("-moz-user-select","")}).on("click.dtSelect",d,function(c){var a=b.select.items();if(!i.getSelection||!i.getSelection().toString()){var d=b.settings()[0];if(e(c.target).closest("div.dataTables_wrapper")[0]==
8
+ b.table().container()){var g=b.cell(e(c.target).closest("td, th"));if(g.any()){var h=e.Event("user-select.dt");k(b,h,[a,g,c]);h.isDefaultPrevented()||(h=g.index(),"row"===a?(a=h.row,s(c,b,d,"row",a)):"column"===a?(a=g.index().column,s(c,b,d,"column",a)):"cell"===a&&(a=g.index(),s(c,b,d,"cell",a)),d._select_lastCell=h)}}}});e("body").on("click.dtSelect",function(a){c._select.blurable&&!e(a.target).parents().filter(b.table().container()).length&&(e(a.target).parents("div.DTE").length||p(c,!0))})}function k(b,
9
+ a,c,d){if(!d||b.flatten().length)"string"===typeof a&&(a+=".dt"),c.unshift(b),e(b.table().node()).triggerHandler(a,c)}function w(b){var a=b.settings()[0];if(a._select.info&&a.aanFeatures.i){var c=e('<span class="select-info"/>'),d=function(a,d){c.append(e('<span class="select-item"/>').append(b.i18n("select."+a+"s",{_:"%d "+a+"s selected","0":"",1:"1 "+a+" selected"},d)))};d("row",b.rows({selected:!0}).flatten().length);d("column",b.columns({selected:!0}).flatten().length);d("cell",b.cells({selected:!0}).flatten().length);
10
+ e.each(a.aanFeatures.i,function(a,b){var b=e(b),d=b.children("span.select-info");d.length&&d.remove();""!==c.text()&&b.append(c)})}}function x(b,a,c,d){var f=b[a+"s"]({search:"applied"}).indexes(),d=e.inArray(d,f),m=e.inArray(c,f);if(!b[a+"s"]({selected:!0}).any()&&-1===d)f.splice(e.inArray(c,f)+1,f.length);else{if(d>m)var j=m,m=d,d=j;f.splice(m+1,f.length);f.splice(0,d)}b[a](c,{selected:!0}).any()?(f.splice(e.inArray(c,f),1),b[a+"s"](f).deselect()):b[a+"s"](f).select()}function p(b,a){if(a||"single"===
11
+ b._select.style){var c=new g.Api(b);c.rows({selected:!0}).deselect();c.columns({selected:!0}).deselect();c.cells({selected:!0}).deselect()}}function s(b,a,c,d,f){var e=a.select.style(),j=a[d](f,{selected:!0}).any();"os"===e?b.ctrlKey||b.metaKey?a[d](f).select(!j):b.shiftKey?"cell"===d?t(a,f,c._select_lastCell||null):x(a,d,f,c._select_lastCell?c._select_lastCell[d]:null):(b=a[d+"s"]({selected:!0}),j&&1===b.flatten().length?a[d](f).deselect():(b.deselect(),a[d](f).select())):"multi+shift"==e?b.shiftKey?
12
+ "cell"===d?t(a,f,c._select_lastCell||null):x(a,d,f,c._select_lastCell?c._select_lastCell[d]:null):a[d](f).select(!j):a[d](f).select(!j)}function q(b,a){return function(c){return c.i18n("buttons."+b,a)}}var g=e.fn.dataTable;g.select={};g.select.version="1.2.0";g.select.init=function(b){var a=b.settings()[0],c=a.oInit.select,d=g.defaults.select,c=c===h?d:c,d="row",f="api",m=!1,j=!0,u="td, th",i="selected";a._select={};if(!0===c)f="os";else if("string"===typeof c)f=c;else if(e.isPlainObject(c)&&(c.blurable!==
13
+ h&&(m=c.blurable),c.info!==h&&(j=c.info),c.items!==h&&(d=c.items),c.style!==h&&(f=c.style),c.selector!==h&&(u=c.selector),c.className!==h))i=c.className;b.select.selector(u);b.select.items(d);b.select.style(f);b.select.blurable(m);b.select.info(j);a._select.className=i;e.fn.dataTable.ext.order["select-checkbox"]=function(a,c){return this.api().column(c,{order:"index"}).nodes().map(function(c){return"row"===a._select.items?e(c).parent().hasClass(a._select.className):"cell"===a._select.items?e(c).hasClass(a._select.className):
14
+ !1})};e(b.table().node()).hasClass("selectable")&&b.select.style("os")};e.each([{type:"row",prop:"aoData"},{type:"column",prop:"aoColumns"}],function(b,a){g.ext.selector[a.type].push(function(c,b,f){var b=b.selected,e,j=[];if(b===h)return f;for(var g=0,i=f.length;g<i;g++)e=c[a.prop][f[g]],(!0===b&&!0===e._select_selected||!1===b&&!e._select_selected)&&j.push(f[g]);return j})});g.ext.selector.cell.push(function(b,a,c){var a=a.selected,d,f=[];if(a===h)return c;for(var e=0,g=c.length;e<g;e++)d=b.aoData[c[e].row],
15
+ (!0===a&&d._selected_cells&&!0===d._selected_cells[c[e].column]||!1===a&&(!d._selected_cells||!d._selected_cells[c[e].column]))&&f.push(c[e]);return f});var n=g.Api.register,o=g.Api.registerPlural;n("select()",function(){return this.iterator("table",function(b){g.select.init(new g.Api(b))})});n("select.blurable()",function(b){return b===h?this.context[0]._select.blurable:this.iterator("table",function(a){a._select.blurable=b})});n("select.info()",function(b){return w===h?this.context[0]._select.info:
16
+ this.iterator("table",function(a){a._select.info=b})});n("select.items()",function(b){return b===h?this.context[0]._select.items:this.iterator("table",function(a){a._select.items=b;k(new g.Api(a),"selectItems",[b])})});n("select.style()",function(b){return b===h?this.context[0]._select.style:this.iterator("table",function(a){a._select.style=b;if(!a._select_init){var c=new g.Api(a);a.aoRowCreatedCallback.push({fn:function(c,b,d){b=a.aoData[d];b._select_selected&&e(c).addClass(a._select.className);
17
+ c=0;for(d=a.aoColumns.length;c<d;c++)(a.aoColumns[c]._select_selected||b._selected_cells&&b._selected_cells[c])&&e(b.anCells[c]).addClass(a._select.className)},sName:"select-deferRender"});c.on("preXhr.dt.dtSelect",function(){var a=c.rows({selected:!0}).ids(!0).filter(function(c){return c!==h}),b=c.cells({selected:!0}).eq(0).map(function(a){var b=c.row(a.row).id(!0);return b?{row:b,column:a.column}:h}).filter(function(c){return c!==h});c.one("draw.dt.dtSelect",function(){c.rows(a).select();b.any()&&
18
+ b.each(function(a){c.cells(a.row,a.column).select()})})});c.on("draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt info.dt",function(){w(c)});c.on("destroy.dtSelect",function(){r(c);c.off(".dtSelect")})}var d=new g.Api(a);r(d);"api"!==b&&v(d);k(new g.Api(a),"selectStyle",[b])})});n("select.selector()",function(b){return b===h?this.context[0]._select.selector:this.iterator("table",function(a){r(new g.Api(a));a._select.selector=b;"api"!==a._select.style&&v(new g.Api(a))})});o("rows().select()",
19
+ "row().select()",function(b){var a=this;if(!1===b)return this.deselect();this.iterator("row",function(c,a){p(c);c.aoData[a]._select_selected=!0;e(c.aoData[a].nTr).addClass(c._select.className)});this.iterator("table",function(c,b){k(a,"select",["row",a[b]],!0)});return this});o("columns().select()","column().select()",function(b){var a=this;if(!1===b)return this.deselect();this.iterator("column",function(a,b){p(a);a.aoColumns[b]._select_selected=!0;var f=(new g.Api(a)).column(b);e(f.header()).addClass(a._select.className);
20
+ e(f.footer()).addClass(a._select.className);f.nodes().to$().addClass(a._select.className)});this.iterator("table",function(c,b){k(a,"select",["column",a[b]],!0)});return this});o("cells().select()","cell().select()",function(b){var a=this;if(!1===b)return this.deselect();this.iterator("cell",function(a,b,f){p(a);b=a.aoData[b];b._selected_cells===h&&(b._selected_cells=[]);b._selected_cells[f]=!0;b.anCells&&e(b.anCells[f]).addClass(a._select.className)});this.iterator("table",function(b,d){k(a,"select",
21
+ ["cell",a[d]],!0)});return this});o("rows().deselect()","row().deselect()",function(){var b=this;this.iterator("row",function(a,b){a.aoData[b]._select_selected=!1;e(a.aoData[b].nTr).removeClass(a._select.className)});this.iterator("table",function(a,c){k(b,"deselect",["row",b[c]],!0)});return this});o("columns().deselect()","column().deselect()",function(){var b=this;this.iterator("column",function(a,b){a.aoColumns[b]._select_selected=!1;var d=new g.Api(a),f=d.column(b);e(f.header()).removeClass(a._select.className);
22
+ e(f.footer()).removeClass(a._select.className);d.cells(null,b).indexes().each(function(b){var c=a.aoData[b.row],d=c._selected_cells;c.anCells&&(!d||!d[b.column])&&e(c.anCells[b.column]).removeClass(a._select.className)})});this.iterator("table",function(a,c){k(b,"deselect",["column",b[c]],!0)});return this});o("cells().deselect()","cell().deselect()",function(){var b=this;this.iterator("cell",function(a,b,d){b=a.aoData[b];b._selected_cells[d]=!1;b.anCells&&!a.aoColumns[d]._select_selected&&e(b.anCells[d]).removeClass(a._select.className)});
23
+ this.iterator("table",function(a,c){k(b,"deselect",["cell",b[c]],!0)});return this});e.extend(g.ext.buttons,{selected:{text:q("selected","Selected"),className:"buttons-selected",init:function(b){var a=this;b.on("draw.dt.DT select.dt.DT deselect.dt.DT",function(){var b=a.rows({selected:!0}).any()||a.columns({selected:!0}).any()||a.cells({selected:!0}).any();a.enable(b)});this.disable()}},selectedSingle:{text:q("selectedSingle","Selected single"),className:"buttons-selected-single",init:function(b){var a=
24
+ this;b.on("draw.dt.DT select.dt.DT deselect.dt.DT",function(){var c=b.rows({selected:!0}).flatten().length+b.columns({selected:!0}).flatten().length+b.cells({selected:!0}).flatten().length;a.enable(1===c)});this.disable()}},selectAll:{text:q("selectAll","Select all"),className:"buttons-select-all",action:function(){this[this.select.items()+"s"]().select()}},selectNone:{text:q("selectNone","Deselect all"),className:"buttons-select-none",action:function(){p(this.settings()[0],!0)},init:function(b){var a=
25
+ this;b.on("draw.dt.DT select.dt.DT deselect.dt.DT",function(){var c=b.rows({selected:!0}).flatten().length+b.columns({selected:!0}).flatten().length+b.cells({selected:!0}).flatten().length;a.enable(0<c)});this.disable()}}});e.each(["Row","Column","Cell"],function(b,a){var c=a.toLowerCase();g.ext.buttons["select"+a+"s"]={text:q("select"+a+"s","Select "+c+"s"),className:"buttons-select-"+c+"s",action:function(){this.select.items(c)},init:function(a){var b=this;a.on("selectItems.dt.DT",function(a,d,
26
+ e){b.active(e===c)})}}});e(l).on("preInit.dt.dtSelect",function(b,a){"dt"===b.namespace&&g.select.init(new g.Api(a))});return g.select});
common/vendor/datatables/media/css/jquery.dataTables.css ADDED
@@ -0,0 +1,452 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Table styles
3
+ */
4
+ table.dataTable {
5
+ width: 100%;
6
+ margin: 0 auto;
7
+ clear: both;
8
+ border-collapse: separate;
9
+ border-spacing: 0;
10
+ /*
11
+ * Header and footer styles
12
+ */
13
+ /*
14
+ * Body styles
15
+ */
16
+ }
17
+ table.dataTable thead th,
18
+ table.dataTable tfoot th {
19
+ font-weight: bold;
20
+ }
21
+ table.dataTable thead th,
22
+ table.dataTable thead td {
23
+ padding: 10px 18px;
24
+ border-bottom: 1px solid #111;
25
+ }
26
+ table.dataTable thead th:active,
27
+ table.dataTable thead td:active {
28
+ outline: none;
29
+ }
30
+ table.dataTable tfoot th,
31
+ table.dataTable tfoot td {
32
+ padding: 10px 18px 6px 18px;
33
+ border-top: 1px solid #111;
34
+ }
35
+ table.dataTable thead .sorting,
36
+ table.dataTable thead .sorting_asc,
37
+ table.dataTable thead .sorting_desc {
38
+ cursor: pointer;
39
+ *cursor: hand;
40
+ }
41
+ table.dataTable thead .sorting,
42
+ table.dataTable thead .sorting_asc,
43
+ table.dataTable thead .sorting_desc,
44
+ table.dataTable thead .sorting_asc_disabled,
45
+ table.dataTable thead .sorting_desc_disabled {
46
+ background-repeat: no-repeat;
47
+ background-position: center right;
48
+ }
49
+ table.dataTable thead .sorting {
50
+ background-image: url("../images/sort_both.png");
51
+ }
52
+ table.dataTable thead .sorting_asc {
53
+ background-image: url("../images/sort_asc.png");
54
+ }
55
+ table.dataTable thead .sorting_desc {
56
+ background-image: url("../images/sort_desc.png");
57
+ }
58
+ table.dataTable thead .sorting_asc_disabled {
59
+ background-image: url("../images/sort_asc_disabled.png");
60
+ }
61
+ table.dataTable thead .sorting_desc_disabled {
62
+ background-image: url("../images/sort_desc_disabled.png");
63
+ }
64
+ table.dataTable tbody tr {
65
+ background-color: #ffffff;
66
+ }
67
+ table.dataTable tbody tr.selected {
68
+ background-color: #B0BED9;
69
+ }
70
+ table.dataTable tbody th,
71
+ table.dataTable tbody td {
72
+ padding: 8px 10px;
73
+ }
74
+ table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, table.dataTable.display tbody th, table.dataTable.display tbody td {
75
+ border-top: 1px solid #ddd;
76
+ }
77
+ table.dataTable.row-border tbody tr:first-child th,
78
+ table.dataTable.row-border tbody tr:first-child td, table.dataTable.display tbody tr:first-child th,
79
+ table.dataTable.display tbody tr:first-child td {
80
+ border-top: none;
81
+ }
82
+ table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody td {
83
+ border-top: 1px solid #ddd;
84
+ border-right: 1px solid #ddd;
85
+ }
86
+ table.dataTable.cell-border tbody tr th:first-child,
87
+ table.dataTable.cell-border tbody tr td:first-child {
88
+ border-left: 1px solid #ddd;
89
+ }
90
+ table.dataTable.cell-border tbody tr:first-child th,
91
+ table.dataTable.cell-border tbody tr:first-child td {
92
+ border-top: none;
93
+ }
94
+ table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd {
95
+ background-color: #f9f9f9;
96
+ }
97
+ table.dataTable.stripe tbody tr.odd.selected, table.dataTable.display tbody tr.odd.selected {
98
+ background-color: #acbad4;
99
+ }
100
+ table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover {
101
+ background-color: #f6f6f6;
102
+ }
103
+ table.dataTable.hover tbody tr:hover.selected, table.dataTable.display tbody tr:hover.selected {
104
+ background-color: #aab7d1;
105
+ }
106
+ table.dataTable.order-column tbody tr > .sorting_1,
107
+ table.dataTable.order-column tbody tr > .sorting_2,
108
+ table.dataTable.order-column tbody tr > .sorting_3, table.dataTable.display tbody tr > .sorting_1,
109
+ table.dataTable.display tbody tr > .sorting_2,
110
+ table.dataTable.display tbody tr > .sorting_3 {
111
+ background-color: #fafafa;
112
+ }
113
+ table.dataTable.order-column tbody tr.selected > .sorting_1,
114
+ table.dataTable.order-column tbody tr.selected > .sorting_2,
115
+ table.dataTable.order-column tbody tr.selected > .sorting_3, table.dataTable.display tbody tr.selected > .sorting_1,
116
+ table.dataTable.display tbody tr.selected > .sorting_2,
117
+ table.dataTable.display tbody tr.selected > .sorting_3 {
118
+ background-color: #acbad5;
119
+ }
120
+ table.dataTable.display tbody tr.odd > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 {
121
+ background-color: #f1f1f1;
122
+ }
123
+ table.dataTable.display tbody tr.odd > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 {
124
+ background-color: #f3f3f3;
125
+ }
126
+ table.dataTable.display tbody tr.odd > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 {
127
+ background-color: whitesmoke;
128
+ }
129
+ table.dataTable.display tbody tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 {
130
+ background-color: #a6b4cd;
131
+ }
132
+ table.dataTable.display tbody tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 {
133
+ background-color: #a8b5cf;
134
+ }
135
+ table.dataTable.display tbody tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 {
136
+ background-color: #a9b7d1;
137
+ }
138
+ table.dataTable.display tbody tr.even > .sorting_1, table.dataTable.order-column.stripe tbody tr.even > .sorting_1 {
139
+ background-color: #fafafa;
140
+ }
141
+ table.dataTable.display tbody tr.even > .sorting_2, table.dataTable.order-column.stripe tbody tr.even > .sorting_2 {
142
+ background-color: #fcfcfc;
143
+ }
144
+ table.dataTable.display tbody tr.even > .sorting_3, table.dataTable.order-column.stripe tbody tr.even > .sorting_3 {
145
+ background-color: #fefefe;
146
+ }
147
+ table.dataTable.display tbody tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 {
148
+ background-color: #acbad5;
149
+ }
150
+ table.dataTable.display tbody tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 {
151
+ background-color: #aebcd6;
152
+ }
153
+ table.dataTable.display tbody tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 {
154
+ background-color: #afbdd8;
155
+ }
156
+ table.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 {
157
+ background-color: #eaeaea;
158
+ }
159
+ table.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 {
160
+ background-color: #ececec;
161
+ }
162
+ table.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 {
163
+ background-color: #efefef;
164
+ }
165
+ table.dataTable.display tbody tr:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 {
166
+ background-color: #a2aec7;
167
+ }
168
+ table.dataTable.display tbody tr:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 {
169
+ background-color: #a3b0c9;
170
+ }
171
+ table.dataTable.display tbody tr:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 {
172
+ background-color: #a5b2cb;
173
+ }
174
+ table.dataTable.no-footer {
175
+ border-bottom: 1px solid #111;
176
+ }
177
+ table.dataTable.nowrap th, table.dataTable.nowrap td {
178
+ white-space: nowrap;
179
+ }
180
+ table.dataTable.compact thead th,
181
+ table.dataTable.compact thead td {
182
+ padding: 4px 17px 4px 4px;
183
+ }
184
+ table.dataTable.compact tfoot th,
185
+ table.dataTable.compact tfoot td {
186
+ padding: 4px;
187
+ }
188
+ table.dataTable.compact tbody th,
189
+ table.dataTable.compact tbody td {
190
+ padding: 4px;
191
+ }
192
+ table.dataTable th.dt-left,
193
+ table.dataTable td.dt-left {
194
+ text-align: left;
195
+ }
196
+ table.dataTable th.dt-center,
197
+ table.dataTable td.dt-center,
198
+ table.dataTable td.dataTables_empty {
199
+ text-align: center;
200
+ }
201
+ table.dataTable th.dt-right,
202
+ table.dataTable td.dt-right {
203
+ text-align: right;
204
+ }
205
+ table.dataTable th.dt-justify,
206
+ table.dataTable td.dt-justify {
207
+ text-align: justify;
208
+ }
209
+ table.dataTable th.dt-nowrap,
210
+ table.dataTable td.dt-nowrap {
211
+ white-space: nowrap;
212
+ }
213
+ table.dataTable thead th.dt-head-left,
214
+ table.dataTable thead td.dt-head-left,
215
+ table.dataTable tfoot th.dt-head-left,
216
+ table.dataTable tfoot td.dt-head-left {
217
+ text-align: left;
218
+ }
219
+ table.dataTable thead th.dt-head-center,
220
+ table.dataTable thead td.dt-head-center,
221
+ table.dataTable tfoot th.dt-head-center,
222
+ table.dataTable tfoot td.dt-head-center {
223
+ text-align: center;
224
+ }
225
+ table.dataTable thead th.dt-head-right,
226
+ table.dataTable thead td.dt-head-right,
227
+ table.dataTable tfoot th.dt-head-right,
228
+ table.dataTable tfoot td.dt-head-right {
229
+ text-align: right;
230
+ }
231
+ table.dataTable thead th.dt-head-justify,
232
+ table.dataTable thead td.dt-head-justify,
233
+ table.dataTable tfoot th.dt-head-justify,
234
+ table.dataTable tfoot td.dt-head-justify {
235
+ text-align: justify;
236
+ }
237
+ table.dataTable thead th.dt-head-nowrap,
238
+ table.dataTable thead td.dt-head-nowrap,
239
+ table.dataTable tfoot th.dt-head-nowrap,
240
+ table.dataTable tfoot td.dt-head-nowrap {
241
+ white-space: nowrap;
242
+ }
243
+ table.dataTable tbody th.dt-body-left,
244
+ table.dataTable tbody td.dt-body-left {
245
+ text-align: left;
246
+ }
247
+ table.dataTable tbody th.dt-body-center,
248
+ table.dataTable tbody td.dt-body-center {
249
+ text-align: center;
250
+ }
251
+ table.dataTable tbody th.dt-body-right,
252
+ table.dataTable tbody td.dt-body-right {
253
+ text-align: right;
254
+ }
255
+ table.dataTable tbody th.dt-body-justify,
256
+ table.dataTable tbody td.dt-body-justify {
257
+ text-align: justify;
258
+ }
259
+ table.dataTable tbody th.dt-body-nowrap,
260
+ table.dataTable tbody td.dt-body-nowrap {
261
+ white-space: nowrap;
262
+ }
263
+
264
+ table.dataTable,
265
+ table.dataTable th,
266
+ table.dataTable td {
267
+ -webkit-box-sizing: content-box;
268
+ box-sizing: content-box;
269
+ }
270
+
271
+ /*
272
+ * Control feature layout
273
+ */
274
+ .dataTables_wrapper {
275
+ position: relative;
276
+ clear: both;
277
+ *zoom: 1;
278
+ zoom: 1;
279
+ }
280
+ .dataTables_wrapper .dataTables_length {
281
+ float: left;
282
+ }
283
+ .dataTables_wrapper .dataTables_filter {
284
+ float: right;
285
+ text-align: right;
286
+ }
287
+ .dataTables_wrapper .dataTables_filter input {
288
+ margin-left: 0.5em;
289
+ }
290
+ .dataTables_wrapper .dataTables_info {
291
+ clear: both;
292
+ float: left;
293
+ padding-top: 0.755em;
294
+ }
295
+ .dataTables_wrapper .dataTables_paginate {
296
+ float: right;
297
+ text-align: right;
298
+ padding-top: 0.25em;
299
+ }
300
+ .dataTables_wrapper .dataTables_paginate .paginate_button {
301
+ box-sizing: border-box;
302
+ display: inline-block;
303
+ min-width: 1.5em;
304
+ padding: 0.5em 1em;
305
+ margin-left: 2px;
306
+ text-align: center;
307
+ text-decoration: none !important;
308
+ cursor: pointer;
309
+ *cursor: hand;
310
+ color: #333 !important;
311
+ border: 1px solid transparent;
312
+ border-radius: 2px;
313
+ }
314
+ .dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
315
+ color: #333 !important;
316
+ border: 1px solid #979797;
317
+ background-color: white;
318
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white), color-stop(100%, #dcdcdc));
319
+ /* Chrome,Safari4+ */
320
+ background: -webkit-linear-gradient(top, white 0%, #dcdcdc 100%);
321
+ /* Chrome10+,Safari5.1+ */
322
+ background: -moz-linear-gradient(top, white 0%, #dcdcdc 100%);
323
+ /* FF3.6+ */
324
+ background: -ms-linear-gradient(top, white 0%, #dcdcdc 100%);
325
+ /* IE10+ */
326
+ background: -o-linear-gradient(top, white 0%, #dcdcdc 100%);
327
+ /* Opera 11.10+ */
328
+ background: linear-gradient(to bottom, white 0%, #dcdcdc 100%);
329
+ /* W3C */
330
+ }
331
+ .dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {
332
+ cursor: default;
333
+ color: #666 !important;
334
+ border: 1px solid transparent;
335
+ background: transparent;
336
+ box-shadow: none;
337
+ }
338
+ .dataTables_wrapper .dataTables_paginate .paginate_button:hover {
339
+ color: white !important;
340
+ border: 1px solid #111;
341
+ background-color: #585858;
342
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111));
343
+ /* Chrome,Safari4+ */
344
+ background: -webkit-linear-gradient(top, #585858 0%, #111 100%);
345
+ /* Chrome10+,Safari5.1+ */
346
+ background: -moz-linear-gradient(top, #585858 0%, #111 100%);
347
+ /* FF3.6+ */
348
+ background: -ms-linear-gradient(top, #585858 0%, #111 100%);
349
+ /* IE10+ */
350
+ background: -o-linear-gradient(top, #585858 0%, #111 100%);
351
+ /* Opera 11.10+ */
352
+ background: linear-gradient(to bottom, #585858 0%, #111 100%);
353
+ /* W3C */
354
+ }
355
+ .dataTables_wrapper .dataTables_paginate .paginate_button:active {
356
+ outline: none;
357
+ background-color: #2b2b2b;
358
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));
359
+ /* Chrome,Safari4+ */
360
+ background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
361
+ /* Chrome10+,Safari5.1+ */
362
+ background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
363
+ /* FF3.6+ */
364
+ background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
365
+ /* IE10+ */
366
+ background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
367
+ /* Opera 11.10+ */
368
+ background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);
369
+ /* W3C */
370
+ box-shadow: inset 0 0 3px #111;
371
+ }
372
+ .dataTables_wrapper .dataTables_paginate .ellipsis {
373
+ padding: 0 1em;
374
+ }
375
+ .dataTables_wrapper .dataTables_processing {
376
+ position: absolute;
377
+ top: 50%;
378
+ left: 50%;
379
+ width: 100%;
380
+ height: 40px;
381
+ margin-left: -50%;
382
+ margin-top: -25px;
383
+ padding-top: 20px;
384
+ text-align: center;
385
+ font-size: 1.2em;
386
+ background-color: white;
387
+ background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(25%, rgba(255, 255, 255, 0.9)), color-stop(75%, rgba(255, 255, 255, 0.9)), color-stop(100%, rgba(255, 255, 255, 0)));
388
+ background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
389
+ background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
390
+ background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
391
+ background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
392
+ background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
393
+ }
394
+ .dataTables_wrapper .dataTables_length,
395
+ .dataTables_wrapper .dataTables_filter,
396
+ .dataTables_wrapper .dataTables_info,
397
+ .dataTables_wrapper .dataTables_processing,
398
+ .dataTables_wrapper .dataTables_paginate {
399
+ color: #333;
400
+ }
401
+ .dataTables_wrapper .dataTables_scroll {
402
+ clear: both;
403
+ }
404
+ .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody {
405
+ *margin-top: -1px;
406
+ -webkit-overflow-scrolling: touch;
407
+ }
408
+ .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td {
409
+ vertical-align: middle;
410
+ }
411
+ .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th > div.dataTables_sizing,
412
+ .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td > div.dataTables_sizing {
413
+ height: 0;
414
+ overflow: hidden;
415
+ margin: 0 !important;
416
+ padding: 0 !important;
417
+ }
418
+ .dataTables_wrapper.no-footer .dataTables_scrollBody {
419
+ border-bottom: 1px solid #111;
420
+ }
421
+ .dataTables_wrapper.no-footer div.dataTables_scrollHead table,
422
+ .dataTables_wrapper.no-footer div.dataTables_scrollBody table {
423
+ border-bottom: none;
424
+ }
425
+ .dataTables_wrapper:after {
426
+ visibility: hidden;
427
+ display: block;
428
+ content: "";
429
+ clear: both;
430
+ height: 0;
431
+ }
432
+
433
+ @media screen and (max-width: 767px) {
434
+ .dataTables_wrapper .dataTables_info,
435
+ .dataTables_wrapper .dataTables_paginate {
436
+ float: none;
437
+ text-align: center;
438
+ }
439
+ .dataTables_wrapper .dataTables_paginate {
440
+ margin-top: 0.5em;
441
+ }
442
+ }
443
+ @media screen and (max-width: 640px) {
444
+ .dataTables_wrapper .dataTables_length,
445
+ .dataTables_wrapper .dataTables_filter {
446
+ float: none;
447
+ text-align: center;
448
+ }
449
+ .dataTables_wrapper .dataTables_filter {
450
+ margin-top: 0.5em;
451
+ }
452
+ }
common/vendor/datatables/media/css/jquery.dataTables.min.css ADDED
@@ -0,0 +1 @@
 
1
+ table.dataTable{width:100%;margin:0 auto;clear:both;border-collapse:separate;border-spacing:0}table.dataTable thead th,table.dataTable tfoot th{font-weight:bold}table.dataTable thead th,table.dataTable thead td{padding:10px 18px;border-bottom:1px solid #111}table.dataTable thead th:active,table.dataTable thead td:active{outline:none}table.dataTable tfoot th,table.dataTable tfoot td{padding:10px 18px 6px 18px;border-top:1px solid #111}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc{cursor:pointer;*cursor:hand}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc_disabled{background-repeat:no-repeat;background-position:center right}table.dataTable thead .sorting{background-image:url("../images/sort_both.png")}table.dataTable thead .sorting_asc{background-image:url("../images/sort_asc.png")}table.dataTable thead .sorting_desc{background-image:url("../images/sort_desc.png")}table.dataTable thead .sorting_asc_disabled{background-image:url("../images/sort_asc_disabled.png")}table.dataTable thead .sorting_desc_disabled{background-image:url("../images/sort_desc_disabled.png")}table.dataTable tbody tr{background-color:#ffffff}table.dataTable tbody tr.selected{background-color:#B0BED9}table.dataTable tbody th,table.dataTable tbody td{padding:8px 10px}table.dataTable.row-border tbody th,table.dataTable.row-border tbody td,table.dataTable.display tbody th,table.dataTable.display tbody td{border-top:1px solid #ddd}table.dataTable.row-border tbody tr:first-child th,table.dataTable.row-border tbody tr:first-child td,table.dataTable.display tbody tr:first-child th,table.dataTable.display tbody tr:first-child td{border-top:none}table.dataTable.cell-border tbody th,table.dataTable.cell-border tbody td{border-top:1px solid #ddd;border-right:1px solid #ddd}table.dataTable.cell-border tbody tr th:first-child,table.dataTable.cell-border tbody tr td:first-child{border-left:1px solid #ddd}table.dataTable.cell-border tbody tr:first-child th,table.dataTable.cell-border tbody tr:first-child td{border-top:none}table.dataTable.stripe tbody tr.odd,table.dataTable.display tbody tr.odd{background-color:#f9f9f9}table.dataTable.stripe tbody tr.odd.selected,table.dataTable.display tbody tr.odd.selected{background-color:#acbad4}table.dataTable.hover tbody tr:hover,table.dataTable.display tbody tr:hover{background-color:#f6f6f6}table.dataTable.hover tbody tr:hover.selected,table.dataTable.display tbody tr:hover.selected{background-color:#aab7d1}table.dataTable.order-column tbody tr>.sorting_1,table.dataTable.order-column tbody tr>.sorting_2,table.dataTable.order-column tbody tr>.sorting_3,table.dataTable.display tbody tr>.sorting_1,table.dataTable.display tbody tr>.sorting_2,table.dataTable.display tbody tr>.sorting_3{background-color:#fafafa}table.dataTable.order-column tbody tr.selected>.sorting_1,table.dataTable.order-column tbody tr.selected>.sorting_2,table.dataTable.order-column tbody tr.selected>.sorting_3,table.dataTable.display tbody tr.selected>.sorting_1,table.dataTable.display tbody tr.selected>.sorting_2,table.dataTable.display tbody tr.selected>.sorting_3{background-color:#acbad5}table.dataTable.display tbody tr.odd>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd>.sorting_1{background-color:#f1f1f1}table.dataTable.display tbody tr.odd>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd>.sorting_2{background-color:#f3f3f3}table.dataTable.display tbody tr.odd>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd>.sorting_3{background-color:whitesmoke}table.dataTable.display tbody tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_1{background-color:#a6b4cd}table.dataTable.display tbody tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody tr.even>.sorting_1,table.dataTable.order-column.stripe tbody tr.even>.sorting_1{background-color:#fafafa}table.dataTable.display tbody tr.even>.sorting_2,table.dataTable.order-column.stripe tbody tr.even>.sorting_2{background-color:#fcfcfc}table.dataTable.display tbody tr.even>.sorting_3,table.dataTable.order-column.stripe tbody tr.even>.sorting_3{background-color:#fefefe}table.dataTable.display tbody tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_1{background-color:#acbad5}table.dataTable.display tbody tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody tr:hover>.sorting_1,table.dataTable.order-column.hover tbody tr:hover>.sorting_1{background-color:#eaeaea}table.dataTable.display tbody tr:hover>.sorting_2,table.dataTable.order-column.hover tbody tr:hover>.sorting_2{background-color:#ececec}table.dataTable.display tbody tr:hover>.sorting_3,table.dataTable.order-column.hover tbody tr:hover>.sorting_3{background-color:#efefef}table.dataTable.display tbody tr:hover.selected>.sorting_1,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1{background-color:#a2aec7}table.dataTable.display tbody tr:hover.selected>.sorting_2,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody tr:hover.selected>.sorting_3,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3{background-color:#a5b2cb}table.dataTable.no-footer{border-bottom:1px solid #111}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}table.dataTable.compact thead th,table.dataTable.compact thead td{padding:4px 17px 4px 4px}table.dataTable.compact tfoot th,table.dataTable.compact tfoot td{padding:4px}table.dataTable.compact tbody th,table.dataTable.compact tbody td{padding:4px}table.dataTable th.dt-left,table.dataTable td.dt-left{text-align:left}table.dataTable th.dt-center,table.dataTable td.dt-center,table.dataTable td.dataTables_empty{text-align:center}table.dataTable th.dt-right,table.dataTable td.dt-right{text-align:right}table.dataTable th.dt-justify,table.dataTable td.dt-justify{text-align:justify}table.dataTable th.dt-nowrap,table.dataTable td.dt-nowrap{white-space:nowrap}table.dataTable thead th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable tfoot td.dt-head-left{text-align:left}table.dataTable thead th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable tfoot td.dt-head-center{text-align:center}table.dataTable thead th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable tfoot td.dt-head-right{text-align:right}table.dataTable thead th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable tfoot td.dt-head-justify{text-align:justify}table.dataTable thead th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable tfoot td.dt-head-nowrap{white-space:nowrap}table.dataTable tbody th.dt-body-left,table.dataTable tbody td.dt-body-left{text-align:left}table.dataTable tbody th.dt-body-center,table.dataTable tbody td.dt-body-center{text-align:center}table.dataTable tbody th.dt-body-right,table.dataTable tbody td.dt-body-right{text-align:right}table.dataTable tbody th.dt-body-justify,table.dataTable tbody td.dt-body-justify{text-align:justify}table.dataTable tbody th.dt-body-nowrap,table.dataTable tbody td.dt-body-nowrap{white-space:nowrap}table.dataTable,table.dataTable th,table.dataTable td{-webkit-box-sizing:content-box;box-sizing:content-box}.dataTables_wrapper{position:relative;clear:both;*zoom:1;zoom:1}.dataTables_wrapper .dataTables_length{float:left}.dataTables_wrapper .dataTables_filter{float:right;text-align:right}.dataTables_wrapper .dataTables_filter input{margin-left:0.5em}.dataTables_wrapper .dataTables_info{clear:both;float:left;padding-top:0.755em}.dataTables_wrapper .dataTables_paginate{float:right;text-align:right;padding-top:0.25em}.dataTables_wrapper .dataTables_paginate .paginate_button{box-sizing:border-box;display:inline-block;min-width:1.5em;padding:0.5em 1em;margin-left:2px;text-align:center;text-decoration:none !important;cursor:pointer;*cursor:hand;color:#333 !important;border:1px solid transparent;border-radius:2px}.dataTables_wrapper .dataTables_paginate .paginate_button.current,.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover{color:#333 !important;border:1px solid #979797;background-color:white;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #fff), color-stop(100%, #dcdcdc));background:-webkit-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-moz-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-ms-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-o-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:linear-gradient(to bottom, #fff 0%, #dcdcdc 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button.disabled,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active{cursor:default;color:#666 !important;border:1px solid transparent;background:transparent;box-shadow:none}.dataTables_wrapper .dataTables_paginate .paginate_button:hover{color:white !important;border:1px solid #111;background-color:#585858;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111));background:-webkit-linear-gradient(top, #585858 0%, #111 100%);background:-moz-linear-gradient(top, #585858 0%, #111 100%);background:-ms-linear-gradient(top, #585858 0%, #111 100%);background:-o-linear-gradient(top, #585858 0%, #111 100%);background:linear-gradient(to bottom, #585858 0%, #111 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button:active{outline:none;background-color:#2b2b2b;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));background:-webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);box-shadow:inset 0 0 3px #111}.dataTables_wrapper .dataTables_paginate .ellipsis{padding:0 1em}.dataTables_wrapper .dataTables_processing{position:absolute;top:50%;left:50%;width:100%;height:40px;margin-left:-50%;margin-top:-25px;padding-top:20px;text-align:center;font-size:1.2em;background-color:white;background:-webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255,255,255,0)), color-stop(25%, rgba(255,255,255,0.9)), color-stop(75%, rgba(255,255,255,0.9)), color-stop(100%, rgba(255,255,255,0)));background:-webkit-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-o-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%)}.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter,.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_processing,.dataTables_wrapper .dataTables_paginate{color:#333}.dataTables_wrapper .dataTables_scroll{clear:both}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody{*margin-top:-1px;-webkit-overflow-scrolling:touch}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td{vertical-align:middle}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th>div.dataTables_sizing,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td>div.dataTables_sizing{height:0;overflow:hidden;margin:0 !important;padding:0 !important}.dataTables_wrapper.no-footer .dataTables_scrollBody{border-bottom:1px solid #111}.dataTables_wrapper.no-footer div.dataTables_scrollHead table,.dataTables_wrapper.no-footer div.dataTables_scrollBody table{border-bottom:none}.dataTables_wrapper:after{visibility:hidden;display:block;content:"";clear:both;height:0}@media screen and (max-width: 767px){.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_paginate{float:none;text-align:center}.dataTables_wrapper .dataTables_paginate{margin-top:0.5em}}@media screen and (max-width: 640px){.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter{float:none;text-align:center}.dataTables_wrapper .dataTables_filter{margin-top:0.5em}}
common/vendor/datatables/media/css/jquery.dataTables_themeroller.css ADDED
@@ -0,0 +1,416 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Table styles
3
+ */
4
+ table.dataTable {
5
+ width: 100%;
6
+ margin: 0 auto;
7
+ clear: both;
8
+ border-collapse: separate;
9
+ border-spacing: 0;
10
+ /*
11
+ * Header and footer styles
12
+ */
13
+ /*
14
+ * Body styles
15
+ */
16
+ }
17
+ table.dataTable thead th,
18
+ table.dataTable thead td,
19
+ table.dataTable tfoot th,
20
+ table.dataTable tfoot td {
21
+ padding: 4px 10px;
22
+ }
23
+ table.dataTable thead th,
24
+ table.dataTable tfoot th {
25
+ font-weight: bold;
26
+ }
27
+ table.dataTable thead th:active,
28
+ table.dataTable thead td:active {
29
+ outline: none;
30
+ }
31
+ table.dataTable thead .sorting_asc,
32
+ table.dataTable thead .sorting_desc,
33
+ table.dataTable thead .sorting {
34
+ cursor: pointer;
35
+ *cursor: hand;
36
+ }
37
+ table.dataTable thead th div.DataTables_sort_wrapper {
38
+ position: relative;
39
+ padding-right: 10px;
40
+ }
41
+ table.dataTable thead th div.DataTables_sort_wrapper span {
42
+ position: absolute;
43
+ top: 50%;
44
+ margin-top: -8px;
45
+ right: -5px;
46
+ }
47
+ table.dataTable thead th.ui-state-default {
48
+ border-right-width: 0;
49
+ }
50
+ table.dataTable thead th.ui-state-default:last-child {
51
+ border-right-width: 1px;
52
+ }
53
+ table.dataTable tbody tr {
54
+ background-color: #ffffff;
55
+ }
56
+ table.dataTable tbody tr.selected {
57
+ background-color: #B0BED9;
58
+ }
59
+ table.dataTable tbody th,
60
+ table.dataTable tbody td {
61
+ padding: 8px 10px;
62
+ }
63
+ table.dataTable th.center,
64
+ table.dataTable td.center,
65
+ table.dataTable td.dataTables_empty {
66
+ text-align: center;
67
+ }
68
+ table.dataTable th.right,
69
+ table.dataTable td.right {
70
+ text-align: right;
71
+ }
72
+ table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, table.dataTable.display tbody th, table.dataTable.display tbody td {
73
+ border-top: 1px solid #ddd;
74
+ }
75
+ table.dataTable.row-border tbody tr:first-child th,
76
+ table.dataTable.row-border tbody tr:first-child td, table.dataTable.display tbody tr:first-child th,
77
+ table.dataTable.display tbody tr:first-child td {
78
+ border-top: none;
79
+ }
80
+ table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody td {
81
+ border-top: 1px solid #ddd;
82
+ border-right: 1px solid #ddd;
83
+ }
84
+ table.dataTable.cell-border tbody tr th:first-child,
85
+ table.dataTable.cell-border tbody tr td:first-child {
86
+ border-left: 1px solid #ddd;
87
+ }
88
+ table.dataTable.cell-border tbody tr:first-child th,
89
+ table.dataTable.cell-border tbody tr:first-child td {
90
+ border-top: none;
91
+ }
92
+ table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd {
93
+ background-color: #f9f9f9;
94
+ }
95
+ table.dataTable.stripe tbody tr.odd.selected, table.dataTable.display tbody tr.odd.selected {
96
+ background-color: #abb9d3;
97
+ }
98
+ table.dataTable.hover tbody tr:hover,
99
+ table.dataTable.hover tbody tr.odd:hover,
100
+ table.dataTable.hover tbody tr.even:hover, table.dataTable.display tbody tr:hover,
101
+ table.dataTable.display tbody tr.odd:hover,
102
+ table.dataTable.display tbody tr.even:hover {
103
+ background-color: whitesmoke;
104
+ }
105
+ table.dataTable.hover tbody tr:hover.selected,
106
+ table.dataTable.hover tbody tr.odd:hover.selected,
107
+ table.dataTable.hover tbody tr.even:hover.selected, table.dataTable.display tbody tr:hover.selected,
108
+ table.dataTable.display tbody tr.odd:hover.selected,
109
+ table.dataTable.display tbody tr.even:hover.selected {
110
+ background-color: #a9b7d1;
111
+ }
112
+ table.dataTable.order-column tbody tr > .sorting_1,
113
+ table.dataTable.order-column tbody tr > .sorting_2,
114
+ table.dataTable.order-column tbody tr > .sorting_3, table.dataTable.display tbody tr > .sorting_1,
115
+ table.dataTable.display tbody tr > .sorting_2,
116
+ table.dataTable.display tbody tr > .sorting_3 {
117
+ background-color: #f9f9f9;
118
+ }
119
+ table.dataTable.order-column tbody tr.selected > .sorting_1,
120
+ table.dataTable.order-column tbody tr.selected > .sorting_2,
121
+ table.dataTable.order-column tbody tr.selected > .sorting_3, table.dataTable.display tbody tr.selected > .sorting_1,
122
+ table.dataTable.display tbody tr.selected > .sorting_2,
123
+ table.dataTable.display tbody tr.selected > .sorting_3 {
124
+ background-color: #acbad4;
125
+ }
126
+ table.dataTable.display tbody tr.odd > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 {
127
+ background-color: #f1f1f1;
128
+ }
129
+ table.dataTable.display tbody tr.odd > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 {
130
+ background-color: #f3f3f3;
131
+ }
132
+ table.dataTable.display tbody tr.odd > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 {
133
+ background-color: whitesmoke;
134
+ }
135
+ table.dataTable.display tbody tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 {
136
+ background-color: #a6b3cd;
137
+ }
138
+ table.dataTable.display tbody tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 {
139
+ background-color: #a7b5ce;
140
+ }
141
+ table.dataTable.display tbody tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 {
142
+ background-color: #a9b6d0;
143
+ }
144
+ table.dataTable.display tbody tr.even > .sorting_1, table.dataTable.order-column.stripe tbody tr.even > .sorting_1 {
145
+ background-color: #f9f9f9;
146
+ }
147
+ table.dataTable.display tbody tr.even > .sorting_2, table.dataTable.order-column.stripe tbody tr.even > .sorting_2 {
148
+ background-color: #fbfbfb;
149
+ }
150
+ table.dataTable.display tbody tr.even > .sorting_3, table.dataTable.order-column.stripe tbody tr.even > .sorting_3 {
151
+ background-color: #fdfdfd;
152
+ }
153
+ table.dataTable.display tbody tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 {
154
+ background-color: #acbad4;
155
+ }
156
+ table.dataTable.display tbody tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 {
157
+ background-color: #adbbd6;
158
+ }
159
+ table.dataTable.display tbody tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 {
160
+ background-color: #afbdd8;
161
+ }
162
+ table.dataTable.display tbody tr:hover > .sorting_1,
163
+ table.dataTable.display tbody tr.odd:hover > .sorting_1,
164
+ table.dataTable.display tbody tr.even:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1,
165
+ table.dataTable.order-column.hover tbody tr.odd:hover > .sorting_1,
166
+ table.dataTable.order-column.hover tbody tr.even:hover > .sorting_1 {
167
+ background-color: #eaeaea;
168
+ }
169
+ table.dataTable.display tbody tr:hover > .sorting_2,
170
+ table.dataTable.display tbody tr.odd:hover > .sorting_2,
171
+ table.dataTable.display tbody tr.even:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2,
172
+ table.dataTable.order-column.hover tbody tr.odd:hover > .sorting_2,
173
+ table.dataTable.order-column.hover tbody tr.even:hover > .sorting_2 {
174
+ background-color: #ebebeb;
175
+ }
176
+ table.dataTable.display tbody tr:hover > .sorting_3,
177
+ table.dataTable.display tbody tr.odd:hover > .sorting_3,
178
+ table.dataTable.display tbody tr.even:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3,
179
+ table.dataTable.order-column.hover tbody tr.odd:hover > .sorting_3,
180
+ table.dataTable.order-column.hover tbody tr.even:hover > .sorting_3 {
181
+ background-color: #eeeeee;
182
+ }
183
+ table.dataTable.display tbody tr:hover.selected > .sorting_1,
184
+ table.dataTable.display tbody tr.odd:hover.selected > .sorting_1,
185
+ table.dataTable.display tbody tr.even:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1,
186
+ table.dataTable.order-column.hover tbody tr.odd:hover.selected > .sorting_1,
187
+ table.dataTable.order-column.hover tbody tr.even:hover.selected > .sorting_1 {
188
+ background-color: #a1aec7;
189
+ }
190
+ table.dataTable.display tbody tr:hover.selected > .sorting_2,
191
+ table.dataTable.display tbody tr.odd:hover.selected > .sorting_2,
192
+ table.dataTable.display tbody tr.even:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2,
193
+ table.dataTable.order-column.hover tbody tr.odd:hover.selected > .sorting_2,
194
+ table.dataTable.order-column.hover tbody tr.even:hover.selected > .sorting_2 {
195
+ background-color: #a2afc8;
196
+ }
197
+ table.dataTable.display tbody tr:hover.selected > .sorting_3,
198
+ table.dataTable.display tbody tr.odd:hover.selected > .sorting_3,
199
+ table.dataTable.display tbody tr.even:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3,
200
+ table.dataTable.order-column.hover tbody tr.odd:hover.selected > .sorting_3,
201
+ table.dataTable.order-column.hover tbody tr.even:hover.selected > .sorting_3 {
202
+ background-color: #a4b2cb;
203
+ }
204
+ table.dataTable.nowrap th, table.dataTable.nowrap td {
205
+ white-space: nowrap;
206
+ }
207
+ table.dataTable.compact thead th,
208
+ table.dataTable.compact thead td {
209
+ padding: 5px 9px;
210
+ }
211
+ table.dataTable.compact tfoot th,
212
+ table.dataTable.compact tfoot td {
213
+ padding: 5px 9px 3px 9px;
214
+ }
215
+ table.dataTable.compact tbody th,
216
+ table.dataTable.compact tbody td {
217
+ padding: 4px 5px;
218
+ }
219
+ table.dataTable th.dt-left,
220
+ table.dataTable td.dt-left {
221
+ text-align: left;
222
+ }
223
+ table.dataTable th.dt-center,
224
+ table.dataTable td.dt-center,
225
+ table.dataTable td.dataTables_empty {
226
+ text-align: center;
227
+ }
228
+ table.dataTable th.dt-right,
229
+ table.dataTable td.dt-right {
230
+ text-align: right;
231
+ }
232
+ table.dataTable th.dt-justify,
233
+ table.dataTable td.dt-justify {
234
+ text-align: justify;
235
+ }
236
+ table.dataTable th.dt-nowrap,
237
+ table.dataTable td.dt-nowrap {
238
+ white-space: nowrap;
239
+ }
240
+ table.dataTable thead th.dt-head-left,
241
+ table.dataTable thead td.dt-head-left,
242
+ table.dataTable tfoot th.dt-head-left,
243
+ table.dataTable tfoot td.dt-head-left {
244
+ text-align: left;
245
+ }
246
+ table.dataTable thead th.dt-head-center,
247
+ table.dataTable thead td.dt-head-center,
248
+ table.dataTable tfoot th.dt-head-center,
249
+ table.dataTable tfoot td.dt-head-center {
250
+ text-align: center;
251
+ }
252
+ table.dataTable thead th.dt-head-right,
253
+ table.dataTable thead td.dt-head-right,
254
+ table.dataTable tfoot th.dt-head-right,
255
+ table.dataTable tfoot td.dt-head-right {
256
+ text-align: right;
257
+ }
258
+ table.dataTable thead th.dt-head-justify,
259
+ table.dataTable thead td.dt-head-justify,
260
+ table.dataTable tfoot th.dt-head-justify,
261
+ table.dataTable tfoot td.dt-head-justify {
262
+ text-align: justify;
263
+ }
264
+ table.dataTable thead th.dt-head-nowrap,
265
+ table.dataTable thead td.dt-head-nowrap,
266
+ table.dataTable tfoot th.dt-head-nowrap,
267
+ table.dataTable tfoot td.dt-head-nowrap {
268
+ white-space: nowrap;
269
+ }
270
+ table.dataTable tbody th.dt-body-left,
271
+ table.dataTable tbody td.dt-body-left {
272
+ text-align: left;
273
+ }
274
+ table.dataTable tbody th.dt-body-center,
275
+ table.dataTable tbody td.dt-body-center {
276
+ text-align: center;
277
+ }
278
+ table.dataTable tbody th.dt-body-right,
279
+ table.dataTable tbody td.dt-body-right {
280
+ text-align: right;
281
+ }
282
+ table.dataTable tbody th.dt-body-justify,
283
+ table.dataTable tbody td.dt-body-justify {
284
+ text-align: justify;
285
+ }
286
+ table.dataTable tbody th.dt-body-nowrap,
287
+ table.dataTable tbody td.dt-body-nowrap {
288
+ white-space: nowrap;
289
+ }
290
+
291
+ table.dataTable,
292
+ table.dataTable th,
293
+ table.dataTable td {
294
+ -webkit-box-sizing: content-box;
295
+ -moz-box-sizing: content-box;
296
+ box-sizing: content-box;
297
+ }
298
+
299
+ /*
300
+ * Control feature layout
301
+ */
302
+ .dataTables_wrapper {
303
+ position: relative;
304
+ clear: both;
305
+ *zoom: 1;
306
+ zoom: 1;
307
+ }
308
+ .dataTables_wrapper .dataTables_length {
309
+ float: left;
310
+ }
311
+ .dataTables_wrapper .dataTables_filter {
312
+ float: right;
313
+ text-align: right;
314
+ }
315
+ .dataTables_wrapper .dataTables_filter input {
316
+ margin-left: 0.5em;
317
+ }
318
+ .dataTables_wrapper .dataTables_info {
319
+ clear: both;
320
+ float: left;
321
+ padding-top: 0.55em;
322
+ }
323
+ .dataTables_wrapper .dataTables_paginate {
324
+ float: right;
325
+ text-align: right;
326
+ }
327
+ .dataTables_wrapper .dataTables_paginate .fg-button {
328
+ box-sizing: border-box;
329
+ display: inline-block;
330
+ min-width: 1.5em;
331
+ padding: 0.5em;
332
+ margin-left: 2px;
333
+ text-align: center;
334
+ text-decoration: none !important;
335
+ cursor: pointer;
336
+ *cursor: hand;
337
+ color: #333 !important;
338
+ border: 1px solid transparent;
339
+ }
340
+ .dataTables_wrapper .dataTables_paginate .fg-button:active {
341
+ outline: none;
342
+ }
343
+ .dataTables_wrapper .dataTables_paginate .fg-button:first-child {
344
+ border-top-left-radius: 3px;
345
+ border-bottom-left-radius: 3px;
346
+ }
347
+ .dataTables_wrapper .dataTables_paginate .fg-button:last-child {
348
+ border-top-right-radius: 3px;
349
+ border-bottom-right-radius: 3px;
350
+ }
351
+ .dataTables_wrapper .dataTables_processing {
352
+ position: absolute;
353
+ top: 50%;
354
+ left: 50%;
355
+ width: 100%;
356
+ height: 40px;
357
+ margin-left: -50%;
358
+ margin-top: -25px;
359
+ padding-top: 20px;
360
+ text-align: center;
361
+ font-size: 1.2em;
362
+ background-color: white;
363
+ background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(25%, rgba(255, 255, 255, 0.9)), color-stop(75%, rgba(255, 255, 255, 0.9)), color-stop(100%, rgba(255, 255, 255, 0)));
364
+ /* Chrome,Safari4+ */
365
+ background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
366
+ /* Chrome10+,Safari5.1+ */
367
+ background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
368
+ /* FF3.6+ */
369
+ background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
370
+ /* IE10+ */
371
+ background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
372
+ /* Opera 11.10+ */
373
+ background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
374
+ /* W3C */
375
+ }
376
+ .dataTables_wrapper .dataTables_length,
377
+ .dataTables_wrapper .dataTables_filter,
378
+ .dataTables_wrapper .dataTables_info,
379
+ .dataTables_wrapper .dataTables_processing,
380
+ .dataTables_wrapper .dataTables_paginate {
381
+ color: #333;
382
+ }
383
+ .dataTables_wrapper .dataTables_scroll {
384
+ clear: both;
385
+ }
386
+ .dataTables_wrapper .dataTables_scrollBody {
387
+ *margin-top: -1px;
388
+ -webkit-overflow-scrolling: touch;
389
+ }
390
+ .dataTables_wrapper .ui-widget-header {
391
+ font-weight: normal;
392
+ }
393
+ .dataTables_wrapper .ui-toolbar {
394
+ padding: 8px;
395
+ }
396
+ .dataTables_wrapper:after {
397
+ visibility: hidden;
398
+ display: block;
399
+ content: "";
400
+ clear: both;
401
+ height: 0;
402
+ }
403
+
404
+ @media screen and (max-width: 767px) {
405
+ .dataTables_wrapper .dataTables_length,
406
+ .dataTables_wrapper .dataTables_filter,
407
+ .dataTables_wrapper .dataTables_info,
408
+ .dataTables_wrapper .dataTables_paginate {
409
+ float: none;
410
+ text-align: center;
411
+ }
412
+ .dataTables_wrapper .dataTables_filter,
413
+ .dataTables_wrapper .dataTables_paginate {
414
+ margin-top: 0.5em;
415
+ }
416
+ }
common/vendor/datatables/media/images/sort_asc.png ADDED
Binary file
common/vendor/datatables/media/images/sort_asc_disabled.png ADDED
Binary file
common/vendor/datatables/media/images/sort_both.png ADDED
Binary file
common/vendor/datatables/media/images/sort_desc.png ADDED
Binary file
common/vendor/datatables/media/images/sort_desc_disabled.png ADDED
Binary file
common/vendor/datatables/media/js/jquery.dataTables.js ADDED
@@ -0,0 +1,15278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*! DataTables 1.10.12
2
+ * ©2008-2015 SpryMedia Ltd - datatables.net/license
3
+ */
4
+
5
+ /**
6
+ * @summary DataTables
7
+ * @description Paginate, search and order HTML tables
8
+ * @version 1.10.12
9
+ * @file jquery.dataTables.js
10
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
11
+ * @contact www.sprymedia.co.uk/contact
12
+ * @copyright Copyright 2008-2015 SpryMedia Ltd.
13
+ *
14
+ * This source file is free software, available under the following license:
15
+ * MIT license - http://datatables.net/license
16
+ *
17
+ * This source file is distributed in the hope that it will be useful, but
18
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
20
+ *
21
+ * For details please refer to: http://www.datatables.net
22
+ */
23
+
24
+ /*jslint evil: true, undef: true, browser: true */
25
+ /*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
26
+
27
+ (function( factory ) {
28
+ "use strict";
29
+
30
+ if ( typeof define === 'function' && define.amd ) {
31
+ // AMD
32
+ define( ['jquery'], function ( $ ) {
33
+ return factory( $, window, document );
34
+ } );
35
+ }
36
+ else if ( typeof exports === 'object' ) {
37
+ // CommonJS
38
+ module.exports = function (root, $) {
39
+ if ( ! root ) {
40
+ // CommonJS environments without a window global must pass a
41
+ // root. This will give an error otherwise
42
+ root = window;
43
+ }
44
+
45
+ if ( ! $ ) {
46
+ $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
47
+ require('jquery') :
48
+ require('jquery')( root );
49
+ }
50
+
51
+ return factory( $, root, root.document );
52
+ };
53
+ }
54
+ else {
55
+ // Browser
56
+ factory( jQuery, window, document );
57
+ }
58
+ }
59
+ (function( $, window, document, undefined ) {
60
+ "use strict";
61
+
62
+ /**
63
+ * DataTables is a plug-in for the jQuery Javascript library. It is a highly
64
+ * flexible tool, based upon the foundations of progressive enhancement,
65
+ * which will add advanced interaction controls to any HTML table. For a
66
+ * full list of features please refer to
67
+ * [DataTables.net](href="http://datatables.net).
68
+ *
69
+ * Note that the `DataTable` object is not a global variable but is aliased
70
+ * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may
71
+ * be accessed.
72
+ *
73
+ * @class
74
+ * @param {object} [init={}] Configuration object for DataTables. Options
75
+ * are defined by {@link DataTable.defaults}
76
+ * @requires jQuery 1.7+
77
+ *
78
+ * @example
79
+ * // Basic initialisation
80
+ * $(document).ready( function {
81
+ * $('#example').dataTable();
82
+ * } );
83
+ *
84
+ * @example
85
+ * // Initialisation with configuration options - in this case, disable
86
+ * // pagination and sorting.
87
+ * $(document).ready( function {
88
+ * $('#example').dataTable( {
89
+ * "paginate": false,
90
+ * "sort": false
91
+ * } );
92
+ * } );
93
+ */
94
+ var DataTable = function ( options )
95
+ {
96
+ /**
97
+ * Perform a jQuery selector action on the table's TR elements (from the tbody) and
98
+ * return the resulting jQuery object.
99
+ * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
100
+ * @param {object} [oOpts] Optional parameters for modifying the rows to be included
101
+ * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
102
+ * criterion ("applied") or all TR elements (i.e. no filter).
103
+ * @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
104
+ * Can be either 'current', whereby the current sorting of the table is used, or
105
+ * 'original' whereby the original order the data was read into the table is used.
106
+ * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
107
+ * ("current") or not ("all"). If 'current' is given, then order is assumed to be
108
+ * 'current' and filter is 'applied', regardless of what they might be given as.
109
+ * @returns {object} jQuery object, filtered by the given selector.
110
+ * @dtopt API
111
+ * @deprecated Since v1.10
112
+ *
113
+ * @example
114
+ * $(document).ready(function() {
115
+ * var oTable = $('#example').dataTable();
116
+ *
117
+ * // Highlight every second row
118
+ * oTable.$('tr:odd').css('backgroundColor', 'blue');
119
+ * } );
120
+ *
121
+ * @example
122
+ * $(document).ready(function() {
123
+ * var oTable = $('#example').dataTable();
124
+ *
125
+ * // Filter to rows with 'Webkit' in them, add a background colour and then
126
+ * // remove the filter, thus highlighting the 'Webkit' rows only.
127
+ * oTable.fnFilter('Webkit');
128
+ * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
129
+ * oTable.fnFilter('');
130
+ * } );
131
+ */
132
+ this.$ = function ( sSelector, oOpts )
133
+ {
134
+ return this.api(true).$( sSelector, oOpts );
135
+ };
136
+
137
+
138
+ /**
139
+ * Almost identical to $ in operation, but in this case returns the data for the matched
140
+ * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
141
+ * rather than any descendants, so the data can be obtained for the row/cell. If matching
142
+ * rows are found, the data returned is the original data array/object that was used to
143
+ * create the row (or a generated array if from a DOM source).
144
+ *
145
+ * This method is often useful in-combination with $ where both functions are given the
146
+ * same parameters and the array indexes will match identically.
147
+ * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
148
+ * @param {object} [oOpts] Optional parameters for modifying the rows to be included
149
+ * @param {string} [oOpts.filter=none] Select elements that meet the current filter
150
+ * criterion ("applied") or all elements (i.e. no filter).
151
+ * @param {string} [oOpts.order=current] Order of the data in the processed array.
152
+ * Can be either 'current', whereby the current sorting of the table is used, or
153
+ * 'original' whereby the original order the data was read into the table is used.
154
+ * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
155
+ * ("current") or not ("all"). If 'current' is given, then order is assumed to be
156
+ * 'current' and filter is 'applied', regardless of what they might be given as.
157
+ * @returns {array} Data for the matched elements. If any elements, as a result of the
158
+ * selector, were not TR, TD or TH elements in the DataTable, they will have a null
159
+ * entry in the array.
160
+ * @dtopt API
161
+ * @deprecated Since v1.10
162
+ *
163
+ * @example
164
+ * $(document).ready(function() {
165
+ * var oTable = $('#example').dataTable();
166
+ *
167
+ * // Get the data from the first row in the table
168
+ * var data = oTable._('tr:first');
169
+ *
170
+ * // Do something useful with the data
171
+ * alert( "First cell is: "+data[0] );
172
+ * } );
173
+ *
174
+ * @example
175
+ * $(document).ready(function() {
176
+ * var oTable = $('#example').dataTable();
177
+ *
178
+ * // Filter to 'Webkit' and get all data for
179
+ * oTable.fnFilter('Webkit');
180
+ * var data = oTable._('tr', {"search": "applied"});
181
+ *
182
+ * // Do something with the data
183
+ * alert( data.length+" rows matched the search" );
184
+ * } );
185
+ */
186
+ this._ = function ( sSelector, oOpts )
187
+ {
188
+ return this.api(true).rows( sSelector, oOpts ).data();
189
+ };
190
+
191
+
192
+ /**
193
+ * Create a DataTables Api instance, with the currently selected tables for
194
+ * the Api's context.
195
+ * @param {boolean} [traditional=false] Set the API instance's context to be
196
+ * only the table referred to by the `DataTable.ext.iApiIndex` option, as was
197
+ * used in the API presented by DataTables 1.9- (i.e. the traditional mode),
198
+ * or if all tables captured in the jQuery object should be used.
199
+ * @return {DataTables.Api}
200
+ */
201
+ this.api = function ( traditional )
202
+ {
203
+ return traditional ?
204
+ new _Api(
205
+ _fnSettingsFromNode( this[ _ext.iApiIndex ] )
206
+ ) :
207
+ new _Api( this );
208
+ };
209
+
210
+
211
+ /**
212
+ * Add a single new row or multiple rows of data to the table. Please note
213
+ * that this is suitable for client-side processing only - if you are using
214
+ * server-side processing (i.e. "bServerSide": true), then to add data, you
215
+ * must add it to the data source, i.e. the server-side, through an Ajax call.
216
+ * @param {array|object} data The data to be added to the table. This can be:
217
+ * <ul>
218
+ * <li>1D array of data - add a single row with the data provided</li>
219
+ * <li>2D array of arrays - add multiple rows in a single call</li>
220
+ * <li>object - data object when using <i>mData</i></li>
221
+ * <li>array of objects - multiple data objects when using <i>mData</i></li>
222
+ * </ul>
223
+ * @param {bool} [redraw=true] redraw the table or not
224
+ * @returns {array} An array of integers, representing the list of indexes in
225
+ * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
226
+ * the table.
227
+ * @dtopt API
228
+ * @deprecated Since v1.10
229
+ *
230
+ * @example
231
+ * // Global var for counter
232
+ * var giCount = 2;
233
+ *
234
+ * $(document).ready(function() {
235
+ * $('#example').dataTable();
236
+ * } );
237
+ *
238
+ * function fnClickAddRow() {
239
+ * $('#example').dataTable().fnAddData( [
240
+ * giCount+".1",
241
+ * giCount+".2",
242
+ * giCount+".3",
243
+ * giCount+".4" ]
244
+ * );
245
+ *
246
+ * giCount++;
247
+ * }
248
+ */
249
+ this.fnAddData = function( data, redraw )
250
+ {
251
+ var api = this.api( true );
252
+
253
+ /* Check if we want to add multiple rows or not */
254
+ var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
255
+ api.rows.add( data ) :
256
+ api.row.add( data );
257
+
258
+ if ( redraw === undefined || redraw ) {
259
+ api.draw();
260
+ }
261
+
262
+ return rows.flatten().toArray();
263
+ };
264
+
265
+
266
+ /**
267
+ * This function will make DataTables recalculate the column sizes, based on the data
268
+ * contained in the table and the sizes applied to the columns (in the DOM, CSS or
269
+ * through the sWidth parameter). This can be useful when the width of the table's
270
+ * parent element changes (for example a window resize).
271
+ * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
272
+ * @dtopt API
273
+ * @deprecated Since v1.10
274
+ *
275
+ * @example
276
+ * $(document).ready(function() {
277
+ * var oTable = $('#example').dataTable( {
278
+ * "sScrollY": "200px",
279
+ * "bPaginate": false
280
+ * } );
281
+ *
282
+ * $(window).bind('resize', function () {
283
+ * oTable.fnAdjustColumnSizing();
284
+ * } );
285
+ * } );
286
+ */
287
+ this.fnAdjustColumnSizing = function ( bRedraw )
288
+ {
289
+ var api = this.api( true ).columns.adjust();
290
+ var settings = api.settings()[0];
291
+ var scroll = settings.oScroll;
292
+
293
+ if ( bRedraw === undefined || bRedraw ) {
294
+ api.draw( false );
295
+ }
296
+ else if ( scroll.sX !== "" || scroll.sY !== "" ) {
297
+ /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
298
+ _fnScrollDraw( settings );
299
+ }
300
+ };
301
+
302
+
303
+ /**
304
+ * Quickly and simply clear a table
305
+ * @param {bool} [bRedraw=true] redraw the table or not
306
+ * @dtopt API
307
+ * @deprecated Since v1.10
308
+ *
309
+ * @example
310
+ * $(document).ready(function() {
311
+ * var oTable = $('#example').dataTable();
312
+ *
313
+ * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
314
+ * oTable.fnClearTable();
315
+ * } );
316
+ */
317
+ this.fnClearTable = function( bRedraw )
318
+ {
319
+ var api = this.api( true ).clear();
320
+
321
+ if ( bRedraw === undefined || bRedraw ) {
322
+ api.draw();
323
+ }
324
+ };
325
+
326
+
327
+ /**
328
+ * The exact opposite of 'opening' a row, this function will close any rows which
329
+ * are currently 'open'.
330
+ * @param {node} nTr the table row to 'close'
331
+ * @returns {int} 0 on success, or 1 if failed (can't find the row)
332
+ * @dtopt API
333
+ * @deprecated Since v1.10
334
+ *
335
+ * @example
336
+ * $(document).ready(function() {
337
+ * var oTable;
338
+ *
339
+ * // 'open' an information row when a row is clicked on
340
+ * $('#example tbody tr').click( function () {
341
+ * if ( oTable.fnIsOpen(this) ) {
342
+ * oTable.fnClose( this );
343
+ * } else {
344
+ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
345
+ * }
346
+ * } );
347
+ *
348
+ * oTable = $('#example').dataTable();
349
+ * } );
350
+ */
351
+ this.fnClose = function( nTr )
352
+ {
353
+ this.api( true ).row( nTr ).child.hide();
354
+ };
355
+
356
+
357
+ /**
358
+ * Remove a row for the table
359
+ * @param {mixed} target The index of the row from aoData to be deleted, or
360
+ * the TR element you want to delete
361
+ * @param {function|null} [callBack] Callback function
362
+ * @param {bool} [redraw=true] Redraw the table or not
363
+ * @returns {array} The row that was deleted
364
+ * @dtopt API
365
+ * @deprecated Since v1.10
366
+ *
367
+ * @example
368
+ * $(document).ready(function() {
369
+ * var oTable = $('#example').dataTable();
370
+ *
371
+ * // Immediately remove the first row
372
+ * oTable.fnDeleteRow( 0 );
373
+ * } );
374
+ */
375
+ this.fnDeleteRow = function( target, callback, redraw )
376
+ {
377
+ var api = this.api( true );
378
+ var rows = api.rows( target );
379
+ var settings = rows.settings()[0];
380
+ var data = settings.aoData[ rows[0][0] ];
381
+
382
+ rows.remove();
383
+
384
+ if ( callback ) {
385
+ callback.call( this, settings, data );
386
+ }
387
+
388
+ if ( redraw === undefined || redraw ) {
389
+ api.draw();
390
+ }
391
+
392
+ return data;
393
+ };
394
+
395
+
396
+ /**
397
+ * Restore the table to it's original state in the DOM by removing all of DataTables
398
+ * enhancements, alterations to the DOM structure of the table and event listeners.
399
+ * @param {boolean} [remove=false] Completely remove the table from the DOM
400
+ * @dtopt API
401
+ * @deprecated Since v1.10
402
+ *
403
+ * @example
404
+ * $(document).ready(function() {
405
+ * // This example is fairly pointless in reality, but shows how fnDestroy can be used
406
+ * var oTable = $('#example').dataTable();
407
+ * oTable.fnDestroy();
408
+ * } );
409
+ */
410
+ this.fnDestroy = function ( remove )
411
+ {
412
+ this.api( true ).destroy( remove );
413
+ };
414
+
415
+
416
+ /**
417
+ * Redraw the table
418
+ * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
419
+ * @dtopt API
420
+ * @deprecated Since v1.10
421
+ *
422
+ * @example
423
+ * $(document).ready(function() {
424
+ * var oTable = $('#example').dataTable();
425
+ *
426
+ * // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
427
+ * oTable.fnDraw();
428
+ * } );
429
+ */
430
+ this.fnDraw = function( complete )
431
+ {
432
+ // Note that this isn't an exact match to the old call to _fnDraw - it takes
433
+ // into account the new data, but can hold position.
434
+ this.api( true ).draw( complete );
435
+ };
436
+
437
+
438
+ /**
439
+ * Filter the input based on data
440
+ * @param {string} sInput String to filter the table on
441
+ * @param {int|null} [iColumn] Column to limit filtering to
442
+ * @param {bool} [bRegex=false] Treat as regular expression or not
443
+ * @param {bool} [bSmart=true] Perform smart filtering or not
444
+ * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
445
+ * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
446
+ * @dtopt API
447
+ * @deprecated Since v1.10
448
+ *
449
+ * @example
450
+ * $(document).ready(function() {
451
+ * var oTable = $('#example').dataTable();
452
+ *
453
+ * // Sometime later - filter...
454
+ * oTable.fnFilter( 'test string' );
455
+ * } );
456
+ */
457
+ this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
458
+ {
459
+ var api = this.api( true );
460
+
461
+ if ( iColumn === null || iColumn === undefined ) {
462
+ api.search( sInput, bRegex, bSmart, bCaseInsensitive );
463
+ }
464
+ else {
465
+ api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
466
+ }
467
+
468
+ api.draw();
469
+ };
470
+
471
+
472
+ /**
473
+ * Get the data for the whole table, an individual row or an individual cell based on the
474
+ * provided parameters.
475
+ * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
476
+ * a TR node then the data source for the whole row will be returned. If given as a
477
+ * TD/TH cell node then iCol will be automatically calculated and the data for the
478
+ * cell returned. If given as an integer, then this is treated as the aoData internal
479
+ * data index for the row (see fnGetPosition) and the data for that row used.
480
+ * @param {int} [col] Optional column index that you want the data of.
481
+ * @returns {array|object|string} If mRow is undefined, then the data for all rows is
482
+ * returned. If mRow is defined, just data for that row, and is iCol is
483
+ * defined, only data for the designated cell is returned.
484
+ * @dtopt API
485
+ * @deprecated Since v1.10
486
+ *
487
+ * @example
488
+ * // Row data
489
+ * $(document).ready(function() {
490
+ * oTable = $('#example').dataTable();
491
+ *
492
+ * oTable.$('tr').click( function () {
493
+ * var data = oTable.fnGetData( this );
494
+ * // ... do something with the array / object of data for the row
495
+ * } );
496
+ * } );
497
+ *
498
+ * @example
499
+ * // Individual cell data
500
+ * $(document).ready(function() {
501
+ * oTable = $('#example').dataTable();
502
+ *
503
+ * oTable.$('td').click( function () {
504
+ * var sData = oTable.fnGetData( this );
505
+ * alert( 'The cell clicked on had the value of '+sData );
506
+ * } );
507
+ * } );
508
+ */
509
+ this.fnGetData = function( src, col )
510
+ {
511
+ var api = this.api( true );
512
+
513
+ if ( src !== undefined ) {
514
+ var type = src.nodeName ? src.nodeName.toLowerCase() : '';
515
+
516
+ return col !== undefined || type == 'td' || type == 'th' ?
517
+ api.cell( src, col ).data() :
518
+ api.row( src ).data() || null;
519
+ }
520
+
521
+ return api.data().toArray();
522
+ };
523
+
524
+
525
+ /**
526
+ * Get an array of the TR nodes that are used in the table's body. Note that you will
527
+ * typically want to use the '$' API method in preference to this as it is more
528
+ * flexible.
529
+ * @param {int} [iRow] Optional row index for the TR element you want
530
+ * @returns {array|node} If iRow is undefined, returns an array of all TR elements
531
+ * in the table's body, or iRow is defined, just the TR element requested.
532
+ * @dtopt API
533
+ * @deprecated Since v1.10
534
+ *
535
+ * @example
536
+ * $(document).ready(function() {
537
+ * var oTable = $('#example').dataTable();
538
+ *
539
+ * // Get the nodes from the table
540
+ * var nNodes = oTable.fnGetNodes( );
541
+ * } );
542
+ */
543
+ this.fnGetNodes = function( iRow )
544
+ {
545
+ var api = this.api( true );
546
+
547
+ return iRow !== undefined ?
548
+ api.row( iRow ).node() :
549
+ api.rows().nodes().flatten().toArray();
550
+ };
551
+
552
+
553
+ /**
554
+ * Get the array indexes of a particular cell from it's DOM element
555
+ * and column index including hidden columns
556
+ * @param {node} node this can either be a TR, TD or TH in the table's body
557
+ * @returns {int} If nNode is given as a TR, then a single index is returned, or
558
+ * if given as a cell, an array of [row index, column index (visible),
559
+ * column index (all)] is given.
560
+ * @dtopt API
561
+ * @deprecated Since v1.10
562
+ *
563
+ * @example
564
+ * $(document).ready(function() {
565
+ * $('#example tbody td').click( function () {
566
+ * // Get the position of the current data from the node
567
+ * var aPos = oTable.fnGetPosition( this );
568
+ *
569
+ * // Get the data array for this row
570
+ * var aData = oTable.fnGetData( aPos[0] );
571
+ *
572
+ * // Update the data array and return the value
573
+ * aData[ aPos[1] ] = 'clicked';
574
+ * this.innerHTML = 'clicked';
575
+ * } );
576
+ *
577
+ * // Init DataTables
578
+ * oTable = $('#example').dataTable();
579
+ * } );
580
+ */
581
+ this.fnGetPosition = function( node )
582
+ {
583
+ var api = this.api( true );
584
+ var nodeName = node.nodeName.toUpperCase();
585
+
586
+ if ( nodeName == 'TR' ) {
587
+ return api.row( node ).index();
588
+ }
589
+ else if ( nodeName == 'TD' || nodeName == 'TH' ) {
590
+ var cell = api.cell( node ).index();
591
+
592
+ return [
593
+ cell.row,
594
+ cell.columnVisible,
595
+ cell.column
596
+ ];
597
+ }
598
+ return null;
599
+ };
600
+
601
+
602
+ /**
603
+ * Check to see if a row is 'open' or not.
604
+ * @param {node} nTr the table row to check
605
+ * @returns {boolean} true if the row is currently open, false otherwise
606
+ * @dtopt API
607
+ * @deprecated Since v1.10
608
+ *
609
+ * @example
610
+ * $(document).ready(function() {
611
+ * var oTable;
612
+ *
613
+ * // 'open' an information row when a row is clicked on
614
+ * $('#example tbody tr').click( function () {
615
+ * if ( oTable.fnIsOpen(this) ) {
616
+ * oTable.fnClose( this );
617
+ * } else {
618
+ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
619
+ * }
620
+ * } );
621
+ *
622
+ * oTable = $('#example').dataTable();
623
+ * } );
624
+ */
625
+ this.fnIsOpen = function( nTr )
626
+ {
627
+ return this.api( true ).row( nTr ).child.isShown();
628
+ };
629
+
630
+
631
+ /**
632
+ * This function will place a new row directly after a row which is currently
633
+ * on display on the page, with the HTML contents that is passed into the
634
+ * function. This can be used, for example, to ask for confirmation that a
635
+ * particular record should be deleted.
636
+ * @param {node} nTr The table row to 'open'
637
+ * @param {string|node|jQuery} mHtml The HTML to put into the row
638
+ * @param {string} sClass Class to give the new TD cell
639
+ * @returns {node} The row opened. Note that if the table row passed in as the
640
+ * first parameter, is not found in the table, this method will silently
641
+ * return.
642
+ * @dtopt API
643
+ * @deprecated Since v1.10
644
+ *
645
+ * @example
646
+ * $(document).ready(function() {
647
+ * var oTable;
648
+ *
649
+ * // 'open' an information row when a row is clicked on
650
+ * $('#example tbody tr').click( function () {
651
+ * if ( oTable.fnIsOpen(this) ) {
652
+ * oTable.fnClose( this );
653
+ * } else {
654
+ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
655
+ * }
656
+ * } );
657
+ *
658
+ * oTable = $('#example').dataTable();
659
+ * } );
660
+ */
661
+ this.fnOpen = function( nTr, mHtml, sClass )
662
+ {
663
+ return this.api( true )
664
+ .row( nTr )
665
+ .child( mHtml, sClass )
666
+ .show()
667
+ .child()[0];
668
+ };
669
+
670
+
671
+ /**
672
+ * Change the pagination - provides the internal logic for pagination in a simple API
673
+ * function. With this function you can have a DataTables table go to the next,
674
+ * previous, first or last pages.
675
+ * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
676
+ * or page number to jump to (integer), note that page 0 is the first page.
677
+ * @param {bool} [bRedraw=true] Redraw the table or not
678
+ * @dtopt API
679
+ * @deprecated Since v1.10
680
+ *
681
+ * @example
682
+ * $(document).ready(function() {
683
+ * var oTable = $('#example').dataTable();
684
+ * oTable.fnPageChange( 'next' );
685
+ * } );
686
+ */
687
+ this.fnPageChange = function ( mAction, bRedraw )
688
+ {
689
+ var api = this.api( true ).page( mAction );
690
+
691
+ if ( bRedraw === undefined || bRedraw ) {
692
+ api.draw(false);
693
+ }
694
+ };
695
+
696
+
697
+ /**
698
+ * Show a particular column
699
+ * @param {int} iCol The column whose display should be changed
700
+ * @param {bool} bShow Show (true) or hide (false) the column
701
+ * @param {bool} [bRedraw=true] Redraw the table or not
702
+ * @dtopt API
703
+ * @deprecated Since v1.10
704
+ *
705
+ * @example
706
+ * $(document).ready(function() {
707
+ * var oTable = $('#example').dataTable();
708
+ *
709
+ * // Hide the second column after initialisation
710
+ * oTable.fnSetColumnVis( 1, false );
711
+ * } );
712
+ */
713
+ this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
714
+ {
715
+ var api = this.api( true ).column( iCol ).visible( bShow );
716
+
717
+ if ( bRedraw === undefined || bRedraw ) {
718
+ api.columns.adjust().draw();
719
+ }
720
+ };
721
+
722
+
723
+ /**
724
+ * Get the settings for a particular table for external manipulation
725
+ * @returns {object} DataTables settings object. See
726
+ * {@link DataTable.models.oSettings}
727
+ * @dtopt API
728
+ * @deprecated Since v1.10
729
+ *
730
+ * @example
731
+ * $(document).ready(function() {
732
+ * var oTable = $('#example').dataTable();
733
+ * var oSettings = oTable.fnSettings();
734
+ *
735
+ * // Show an example parameter from the settings
736
+ * alert( oSettings._iDisplayStart );
737
+ * } );
738
+ */
739
+ this.fnSettings = function()
740
+ {
741
+ return _fnSettingsFromNode( this[_ext.iApiIndex] );
742
+ };
743
+
744
+
745
+ /**
746
+ * Sort the table by a particular column
747
+ * @param {int} iCol the data index to sort on. Note that this will not match the
748
+ * 'display index' if you have hidden data entries
749
+ * @dtopt API
750
+ * @deprecated Since v1.10
751
+ *
752
+ * @example
753
+ * $(document).ready(function() {
754
+ * var oTable = $('#example').dataTable();
755
+ *
756
+ * // Sort immediately with columns 0 and 1
757
+ * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
758
+ * } );
759
+ */
760
+ this.fnSort = function( aaSort )
761
+ {
762
+ this.api( true ).order( aaSort ).draw();
763
+ };
764
+
765
+
766
+ /**
767
+ * Attach a sort listener to an element for a given column
768
+ * @param {node} nNode the element to attach the sort listener to
769
+ * @param {int} iColumn the column that a click on this node will sort on
770
+ * @param {function} [fnCallback] callback function when sort is run
771
+ * @dtopt API
772
+ * @deprecated Since v1.10
773
+ *
774
+ * @example
775
+ * $(document).ready(function() {
776
+ * var oTable = $('#example').dataTable();
777
+ *
778
+ * // Sort on column 1, when 'sorter' is clicked on
779
+ * oTable.fnSortListener( document.getElementById('sorter'), 1 );
780
+ * } );
781
+ */
782
+ this.fnSortListener = function( nNode, iColumn, fnCallback )
783
+ {
784
+ this.api( true ).order.listener( nNode, iColumn, fnCallback );
785
+ };
786
+
787
+
788
+ /**
789
+ * Update a table cell or row - this method will accept either a single value to
790
+ * update the cell with, an array of values with one element for each column or
791
+ * an object in the same format as the original data source. The function is
792
+ * self-referencing in order to make the multi column updates easier.
793
+ * @param {object|array|string} mData Data to update the cell/row with
794
+ * @param {node|int} mRow TR element you want to update or the aoData index
795
+ * @param {int} [iColumn] The column to update, give as null or undefined to
796
+ * update a whole row.
797
+ * @param {bool} [bRedraw=true] Redraw the table or not
798
+ * @param {bool} [bAction=true] Perform pre-draw actions or not
799
+ * @returns {int} 0 on success, 1 on error
800
+ * @dtopt API
801
+ * @deprecated Since v1.10
802
+ *
803
+ * @example
804
+ * $(document).ready(function() {
805
+ * var oTable = $('#example').dataTable();
806
+ * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
807
+ * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
808
+ * } );
809
+ */
810
+ this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
811
+ {
812
+ var api = this.api( true );
813
+
814
+ if ( iColumn === undefined || iColumn === null ) {
815
+ api.row( mRow ).data( mData );
816
+ }
817
+ else {
818
+ api.cell( mRow, iColumn ).data( mData );
819
+ }
820
+
821
+ if ( bAction === undefined || bAction ) {
822
+ api.columns.adjust();
823
+ }
824
+
825
+ if ( bRedraw === undefined || bRedraw ) {
826
+ api.draw();
827
+ }
828
+ return 0;
829
+ };
830
+
831
+
832
+ /**
833
+ * Provide a common method for plug-ins to check the version of DataTables being used, in order
834
+ * to ensure compatibility.
835
+ * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
836
+ * formats "X" and "X.Y" are also acceptable.
837
+ * @returns {boolean} true if this version of DataTables is greater or equal to the required
838
+ * version, or false if this version of DataTales is not suitable
839
+ * @method
840
+ * @dtopt API
841
+ * @deprecated Since v1.10
842
+ *
843
+ * @example
844
+ * $(document).ready(function() {
845
+ * var oTable = $('#example').dataTable();
846
+ * alert( oTable.fnVersionCheck( '1.9.0' ) );
847
+ * } );
848
+ */
849
+ this.fnVersionCheck = _ext.fnVersionCheck;
850
+
851
+
852
+ var _that = this;
853
+ var emptyInit = options === undefined;
854
+ var len = this.length;
855
+
856
+ if ( emptyInit ) {
857
+ options = {};
858
+ }
859
+
860
+ this.oApi = this.internal = _ext.internal;
861
+
862
+ // Extend with old style plug-in API methods
863
+ for ( var fn in DataTable.ext.internal ) {
864
+ if ( fn ) {
865
+ this[fn] = _fnExternApiFunc(fn);
866
+ }
867
+ }
868
+
869
+ this.each(function() {
870
+ // For each initialisation we want to give it a clean initialisation
871
+ // object that can be bashed around
872
+ var o = {};
873
+ var oInit = len > 1 ? // optimisation for single table case
874
+ _fnExtend( o, options, true ) :
875
+ options;
876
+
877
+ /*global oInit,_that,emptyInit*/
878
+ var i=0, iLen, j, jLen, k, kLen;
879
+ var sId = this.getAttribute( 'id' );
880
+ var bInitHandedOff = false;
881
+ var defaults = DataTable.defaults;
882
+ var $this = $(this);
883
+
884
+
885
+ /* Sanity check */
886
+ if ( this.nodeName.toLowerCase() != 'table' )
887
+ {
888
+ _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
889
+ return;
890
+ }
891
+
892
+ /* Backwards compatibility for the defaults */
893
+ _fnCompatOpts( defaults );
894
+ _fnCompatCols( defaults.column );
895
+
896
+ /* Convert the camel-case defaults to Hungarian */
897
+ _fnCamelToHungarian( defaults, defaults, true );
898
+ _fnCamelToHungarian( defaults.column, defaults.column, true );
899
+
900
+ /* Setting up the initialisation object */
901
+ _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) );
902
+
903
+
904
+
905
+ /* Check to see if we are re-initialising a table */
906
+ var allSettings = DataTable.settings;
907
+ for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
908
+ {
909
+ var s = allSettings[i];
910
+
911
+ /* Base check on table node */
912
+ if ( s.nTable == this || s.nTHead.parentNode == this || (s.nTFoot && s.nTFoot.parentNode == this) )
913
+ {
914
+ var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
915
+ var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
916
+
917
+ if ( emptyInit || bRetrieve )
918
+ {
919
+ return s.oInstance;
920
+ }
921
+ else if ( bDestroy )
922
+ {
923
+ s.oInstance.fnDestroy();
924
+ break;
925
+ }
926
+ else
927
+ {
928
+ _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
929
+ return;
930
+ }
931
+ }
932
+
933
+ /* If the element we are initialising has the same ID as a table which was previously
934
+ * initialised, but the table nodes don't match (from before) then we destroy the old
935
+ * instance by simply deleting it. This is under the assumption that the table has been
936
+ * destroyed by other methods. Anyone using non-id selectors will need to do this manually
937
+ */
938
+ if ( s.sTableId == this.id )
939
+ {
940
+ allSettings.splice( i, 1 );
941
+ break;
942
+ }
943
+ }
944
+
945
+ /* Ensure the table has an ID - required for accessibility */
946
+ if ( sId === null || sId === "" )
947
+ {
948
+ sId = "DataTables_Table_"+(DataTable.ext._unique++);
949
+ this.id = sId;
950
+ }
951
+
952
+ /* Create the settings object for this table and set some of the default parameters */
953
+ var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
954
+ "sDestroyWidth": $this[0].style.width,
955
+ "sInstance": sId,
956
+ "sTableId": sId
957
+ } );
958
+ oSettings.nTable = this;
959
+ oSettings.oApi = _that.internal;
960
+ oSettings.oInit = oInit;
961
+
962
+ allSettings.push( oSettings );
963
+
964
+ // Need to add the instance after the instance after the settings object has been added
965
+ // to the settings array, so we can self reference the table instance if more than one
966
+ oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
967
+
968
+ // Backwards compatibility, before we apply all the defaults
969
+ _fnCompatOpts( oInit );
970
+
971
+ if ( oInit.oLanguage )
972
+ {
973
+ _fnLanguageCompat( oInit.oLanguage );
974
+ }
975
+
976
+ // If the length menu is given, but the init display length is not, use the length menu
977
+ if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
978
+ {
979
+ oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
980
+ oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
981
+ }
982
+
983
+ // Apply the defaults and init options to make a single init object will all
984
+ // options defined from defaults and instance options.
985
+ oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
986
+
987
+
988
+ // Map the initialisation options onto the settings object
989
+ _fnMap( oSettings.oFeatures, oInit, [
990
+ "bPaginate",
991
+ "bLengthChange",
992
+ "bFilter",
993
+ "bSort",
994
+ "bSortMulti",
995
+ "bInfo",
996
+ "bProcessing",
997
+ "bAutoWidth",
998
+ "bSortClasses",
999
+ "bServerSide",
1000
+ "bDeferRender"
1001
+ ] );
1002
+ _fnMap( oSettings, oInit, [
1003
+ "asStripeClasses",
1004
+ "ajax",
1005
+ "fnServerData",
1006
+ "fnFormatNumber",
1007
+ "sServerMethod",
1008
+ "aaSorting",
1009
+ "aaSortingFixed",
1010
+ "aLengthMenu",
1011
+ "sPaginationType",
1012
+ "sAjaxSource",
1013
+ "sAjaxDataProp",
1014
+ "iStateDuration",
1015
+ "sDom",
1016
+ "bSortCellsTop",
1017
+ "iTabIndex",
1018
+ "fnStateLoadCallback",
1019
+ "fnStateSaveCallback",
1020
+ "renderer",
1021
+ "searchDelay",
1022
+ "rowId",
1023
+ [ "iCookieDuration", "iStateDuration" ], // backwards compat
1024
+ [ "oSearch", "oPreviousSearch" ],
1025
+ [ "aoSearchCols", "aoPreSearchCols" ],
1026
+ [ "iDisplayLength", "_iDisplayLength" ],
1027
+ [ "bJQueryUI", "bJUI" ]
1028
+ ] );
1029
+ _fnMap( oSettings.oScroll, oInit, [
1030
+ [ "sScrollX", "sX" ],
1031
+ [ "sScrollXInner", "sXInner" ],
1032
+ [ "sScrollY", "sY" ],
1033
+ [ "bScrollCollapse", "bCollapse" ]
1034
+ ] );
1035
+ _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
1036
+
1037
+ /* Callback functions which are array driven */
1038
+ _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
1039
+ _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
1040
+ _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
1041
+ _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
1042
+ _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
1043
+ _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
1044
+ _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
1045
+ _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
1046
+ _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
1047
+ _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
1048
+ _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
1049
+
1050
+ oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
1051
+
1052
+ /* Browser support detection */
1053
+ _fnBrowserDetect( oSettings );
1054
+
1055
+ var oClasses = oSettings.oClasses;
1056
+
1057
+ // @todo Remove in 1.11
1058
+ if ( oInit.bJQueryUI )
1059
+ {
1060
+ /* Use the JUI classes object for display. You could clone the oStdClasses object if
1061
+ * you want to have multiple tables with multiple independent classes
1062
+ */
1063
+ $.extend( oClasses, DataTable.ext.oJUIClasses, oInit.oClasses );
1064
+
1065
+ if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip" )
1066
+ {
1067
+ /* Set the DOM to use a layout suitable for jQuery UI's theming */
1068
+ oSettings.sDom = '<"H"lfr>t<"F"ip>';
1069
+ }
1070
+
1071
+ if ( ! oSettings.renderer ) {
1072
+ oSettings.renderer = 'jqueryui';
1073
+ }
1074
+ else if ( $.isPlainObject( oSettings.renderer ) && ! oSettings.renderer.header ) {
1075
+ oSettings.renderer.header = 'jqueryui';
1076
+ }
1077
+ }
1078
+ else
1079
+ {
1080
+ $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
1081
+ }
1082
+ $this.addClass( oClasses.sTable );
1083
+
1084
+
1085
+ if ( oSettings.iInitDisplayStart === undefined )
1086
+ {
1087
+ /* Display start point, taking into account the save saving */
1088
+ oSettings.iInitDisplayStart = oInit.iDisplayStart;
1089
+ oSettings._iDisplayStart = oInit.iDisplayStart;
1090
+ }
1091
+
1092
+ if ( oInit.iDeferLoading !== null )
1093
+ {
1094
+ oSettings.bDeferLoading = true;
1095
+ var tmp = $.isArray( oInit.iDeferLoading );
1096
+ oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
1097
+ oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
1098
+ }
1099
+
1100
+ /* Language definitions */
1101
+ var oLanguage = oSettings.oLanguage;
1102
+ $.extend( true, oLanguage, oInit.oLanguage );
1103
+
1104
+ if ( oLanguage.sUrl !== "" )
1105
+ {
1106
+ /* Get the language definitions from a file - because this Ajax call makes the language
1107
+ * get async to the remainder of this function we use bInitHandedOff to indicate that
1108
+ * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
1109
+ */
1110
+ $.ajax( {
1111
+ dataType: 'json',
1112
+ url: oLanguage.sUrl,
1113
+ success: function ( json ) {
1114
+ _fnLanguageCompat( json );
1115
+ _fnCamelToHungarian( defaults.oLanguage, json );
1116
+ $.extend( true, oLanguage, json );
1117
+ _fnInitialise( oSettings );
1118
+ },
1119
+ error: function () {
1120
+ // Error occurred loading language file, continue on as best we can
1121
+ _fnInitialise( oSettings );
1122
+ }
1123
+ } );
1124
+ bInitHandedOff = true;
1125
+ }
1126
+
1127
+ /*
1128
+ * Stripes
1129
+ */
1130
+ if ( oInit.asStripeClasses === null )
1131
+ {
1132
+ oSettings.asStripeClasses =[
1133
+ oClasses.sStripeOdd,
1134
+ oClasses.sStripeEven
1135
+ ];
1136
+ }
1137
+
1138
+ /* Remove row stripe classes if they are already on the table row */
1139
+ var stripeClasses = oSettings.asStripeClasses;
1140
+ var rowOne = $this.children('tbody').find('tr').eq(0);
1141
+ if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
1142
+ return rowOne.hasClass(el);
1143
+ } ) ) !== -1 ) {
1144
+ $('tbody tr', this).removeClass( stripeClasses.join(' ') );
1145
+ oSettings.asDestroyStripes = stripeClasses.slice();
1146
+ }
1147
+
1148
+ /*
1149
+ * Columns
1150
+ * See if we should load columns automatically or use defined ones
1151
+ */
1152
+ var anThs = [];
1153
+ var aoColumnsInit;
1154
+ var nThead = this.getElementsByTagName('thead');
1155
+ if ( nThead.length !== 0 )
1156
+ {
1157
+ _fnDetectHeader( oSettings.aoHeader, nThead[0] );
1158
+ anThs = _fnGetUniqueThs( oSettings );
1159
+ }
1160
+
1161
+ /* If not given a column array, generate one with nulls */
1162
+ if ( oInit.aoColumns === null )
1163
+ {
1164
+ aoColumnsInit = [];
1165
+ for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
1166
+ {
1167
+ aoColumnsInit.push( null );
1168
+ }
1169
+ }
1170
+ else
1171
+ {
1172
+ aoColumnsInit = oInit.aoColumns;
1173
+ }
1174
+
1175
+ /* Add the columns */
1176
+ for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
1177
+ {
1178
+ _fnAddColumn( oSettings, anThs ? anThs[i] : null );
1179
+ }
1180
+
1181
+ /* Apply the column definitions */
1182
+ _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
1183
+ _fnColumnOptions( oSettings, iCol, oDef );
1184
+ } );
1185
+
1186
+ /* HTML5 attribute detection - build an mData object automatically if the
1187
+ * attributes are found
1188
+ */
1189
+ if ( rowOne.length ) {
1190
+ var a = function ( cell, name ) {
1191
+ return cell.getAttribute( 'data-'+name ) !== null ? name : null;
1192
+ };
1193
+
1194
+ $( rowOne[0] ).children('th, td').each( function (i, cell) {
1195
+ var col = oSettings.aoColumns[i];
1196
+
1197
+ if ( col.mData === i ) {
1198
+ var sort = a( cell, 'sort' ) || a( cell, 'order' );
1199
+ var filter = a( cell, 'filter' ) || a( cell, 'search' );
1200
+
1201
+ if ( sort !== null || filter !== null ) {
1202
+ col.mData = {
1203
+ _: i+'.display',
1204
+ sort: sort !== null ? i+'.@data-'+sort : undefined,
1205
+ type: sort !== null ? i+'.@data-'+sort : undefined,
1206
+ filter: filter !== null ? i+'.@data-'+filter : undefined
1207
+ };
1208
+
1209
+ _fnColumnOptions( oSettings, i );
1210
+ }
1211
+ }
1212
+ } );
1213
+ }
1214
+
1215
+ var features = oSettings.oFeatures;
1216
+
1217
+ /* Must be done after everything which can be overridden by the state saving! */
1218
+ if ( oInit.bStateSave )
1219
+ {
1220
+ features.bStateSave = true;
1221
+ _fnLoadState( oSettings, oInit );
1222
+ _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
1223
+ }
1224
+
1225
+
1226
+ /*
1227
+ * Sorting
1228
+ * @todo For modularisation (1.11) this needs to do into a sort start up handler
1229
+ */
1230
+
1231
+ // If aaSorting is not defined, then we use the first indicator in asSorting
1232
+ // in case that has been altered, so the default sort reflects that option
1233
+ if ( oInit.aaSorting === undefined )
1234
+ {
1235
+ var sorting = oSettings.aaSorting;
1236
+ for ( i=0, iLen=sorting.length ; i<iLen ; i++ )
1237
+ {
1238
+ sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
1239
+ }
1240
+ }
1241
+
1242
+ /* Do a first pass on the sorting classes (allows any size changes to be taken into
1243
+ * account, and also will apply sorting disabled classes if disabled
1244
+ */
1245
+ _fnSortingClasses( oSettings );
1246
+
1247
+ if ( features.bSort )
1248
+ {
1249
+ _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1250
+ if ( oSettings.bSorted ) {
1251
+ var aSort = _fnSortFlatten( oSettings );
1252
+ var sortedColumns = {};
1253
+
1254
+ $.each( aSort, function (i, val) {
1255
+ sortedColumns[ val.src ] = val.dir;
1256
+ } );
1257
+
1258
+ _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
1259
+ _fnSortAria( oSettings );
1260
+ }
1261
+ } );
1262
+ }
1263
+
1264
+ _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1265
+ if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
1266
+ _fnSortingClasses( oSettings );
1267
+ }
1268
+ }, 'sc' );
1269
+
1270
+
1271
+ /*
1272
+ * Final init
1273
+ * Cache the header, body and footer as required, creating them if needed
1274
+ */
1275
+
1276
+ // Work around for Webkit bug 83867 - store the caption-side before removing from doc
1277
+ var captions = $this.children('caption').each( function () {
1278
+ this._captionSide = $this.css('caption-side');
1279
+ } );
1280
+
1281
+ var thead = $this.children('thead');
1282
+ if ( thead.length === 0 )
1283
+ {
1284
+ thead = $('<thead/>').appendTo(this);
1285
+ }
1286
+ oSettings.nTHead = thead[0];
1287
+
1288
+ var tbody = $this.children('tbody');
1289
+ if ( tbody.length === 0 )
1290
+ {
1291
+ tbody = $('<tbody/>').appendTo(this);
1292
+ }
1293
+ oSettings.nTBody = tbody[0];
1294
+
1295
+ var tfoot = $this.children('tfoot');
1296
+ if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") )
1297
+ {
1298
+ // If we are a scrolling table, and no footer has been given, then we need to create
1299
+ // a tfoot element for the caption element to be appended to
1300
+ tfoot = $('<tfoot/>').appendTo(this);
1301
+ }
1302
+
1303
+ if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
1304
+ $this.addClass( oClasses.sNoFooter );
1305
+ }
1306
+ else if ( tfoot.length > 0 ) {
1307
+ oSettings.nTFoot = tfoot[0];
1308
+ _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
1309
+ }
1310
+
1311
+ /* Check if there is data passing into the constructor */
1312
+ if ( oInit.aaData )
1313
+ {
1314
+ for ( i=0 ; i<oInit.aaData.length ; i++ )
1315
+ {
1316
+ _fnAddData( oSettings, oInit.aaData[ i ] );
1317
+ }
1318
+ }
1319
+ else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' )
1320
+ {
1321
+ /* Grab the data from the page - only do this when deferred loading or no Ajax
1322
+ * source since there is no point in reading the DOM data if we are then going
1323
+ * to replace it with Ajax data
1324
+ */
1325
+ _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
1326
+ }
1327
+
1328
+ /* Copy the data index array */
1329
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
1330
+
1331
+ /* Initialisation complete - table can be drawn */
1332
+ oSettings.bInitialised = true;
1333
+
1334
+ /* Check if we need to initialise the table (it might not have been handed off to the
1335
+ * language processor)
1336
+ */
1337
+ if ( bInitHandedOff === false )
1338
+ {
1339
+ _fnInitialise( oSettings );
1340
+ }
1341
+ } );
1342
+ _that = null;
1343
+ return this;
1344
+ };
1345
+
1346
+
1347
+ /*
1348
+ * It is useful to have variables which are scoped locally so only the
1349
+ * DataTables functions can access them and they don't leak into global space.
1350
+ * At the same time these functions are often useful over multiple files in the
1351
+ * core and API, so we list, or at least document, all variables which are used
1352
+ * by DataTables as private variables here. This also ensures that there is no
1353
+ * clashing of variable names and that they can easily referenced for reuse.
1354
+ */
1355
+
1356
+
1357
+ // Defined else where
1358
+ // _selector_run
1359
+ // _selector_opts
1360
+ // _selector_first
1361
+ // _selector_row_indexes
1362
+
1363
+ var _ext; // DataTable.ext
1364
+ var _Api; // DataTable.Api
1365
+ var _api_register; // DataTable.Api.register
1366
+ var _api_registerPlural; // DataTable.Api.registerPlural
1367
+
1368
+ var _re_dic = {};
1369
+ var _re_new_lines = /[\r\n]/g;
1370
+ var _re_html = /<.*?>/g;
1371
+ var _re_date_start = /^[\w\+\-]/;
1372
+ var _re_date_end = /[\w\+\-]$/;
1373
+
1374
+ // Escape regular expression special characters
1375
+ var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
1376
+
1377
+ // http://en.wikipedia.org/wiki/Foreign_exchange_market
1378
+ // - \u20BD - Russian ruble.
1379
+ // - \u20a9 - South Korean Won
1380
+ // - \u20BA - Turkish Lira
1381
+ // - \u20B9 - Indian Rupee
1382
+ // - R - Brazil (R$) and South Africa
1383
+ // - fr - Swiss Franc
1384
+ // - kr - Swedish krona, Norwegian krone and Danish krone
1385
+ // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
1386
+ // standards as thousands separators.
1387
+ var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi;
1388
+
1389
+
1390
+ var _empty = function ( d ) {
1391
+ return !d || d === true || d === '-' ? true : false;
1392
+ };
1393
+
1394
+
1395
+ var _intVal = function ( s ) {
1396
+ var integer = parseInt( s, 10 );
1397
+ return !isNaN(integer) && isFinite(s) ? integer : null;
1398
+ };
1399
+
1400
+ // Convert from a formatted number with characters other than `.` as the
1401
+ // decimal place, to a Javascript number
1402
+ var _numToDecimal = function ( num, decimalPoint ) {
1403
+ // Cache created regular expressions for speed as this function is called often
1404
+ if ( ! _re_dic[ decimalPoint ] ) {
1405
+ _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
1406
+ }
1407
+ return typeof num === 'string' && decimalPoint !== '.' ?
1408
+ num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
1409
+ num;
1410
+ };
1411
+
1412
+
1413
+ var _isNumber = function ( d, decimalPoint, formatted ) {
1414
+ var strType = typeof d === 'string';
1415
+
1416
+ // If empty return immediately so there must be a number if it is a
1417
+ // formatted string (this stops the string "k", or "kr", etc being detected
1418
+ // as a formatted number for currency
1419
+ if ( _empty( d ) ) {
1420
+ return true;
1421
+ }
1422
+
1423
+ if ( decimalPoint && strType ) {
1424
+ d = _numToDecimal( d, decimalPoint );
1425
+ }
1426
+
1427
+ if ( formatted && strType ) {
1428
+ d = d.replace( _re_formatted_numeric, '' );
1429
+ }
1430
+
1431
+ return !isNaN( parseFloat(d) ) && isFinite( d );
1432
+ };
1433
+
1434
+
1435
+ // A string without HTML in it can be considered to be HTML still
1436
+ var _isHtml = function ( d ) {
1437
+ return _empty( d ) || typeof d === 'string';
1438
+ };
1439
+
1440
+
1441
+ var _htmlNumeric = function ( d, decimalPoint, formatted ) {
1442
+ if ( _empty( d ) ) {
1443
+ return true;
1444
+ }
1445
+
1446
+ var html = _isHtml( d );
1447
+ return ! html ?
1448
+ null :
1449
+ _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
1450
+ true :
1451
+ null;
1452
+ };
1453
+
1454
+
1455
+ var _pluck = function ( a, prop, prop2 ) {
1456
+ var out = [];
1457
+ var i=0, ien=a.length;
1458
+
1459
+ // Could have the test in the loop for slightly smaller code, but speed
1460
+ // is essential here
1461
+ if ( prop2 !== undefined ) {
1462
+ for ( ; i<ien ; i++ ) {
1463
+ if ( a[i] && a[i][ prop ] ) {
1464
+ out.push( a[i][ prop ][ prop2 ] );
1465
+ }
1466
+ }
1467
+ }
1468
+ else {
1469
+ for ( ; i<ien ; i++ ) {
1470
+ if ( a[i] ) {
1471
+ out.push( a[i][ prop ] );
1472
+ }
1473
+ }
1474
+ }
1475
+
1476
+ return out;
1477
+ };
1478
+
1479
+
1480
+ // Basically the same as _pluck, but rather than looping over `a` we use `order`
1481
+ // as the indexes to pick from `a`
1482
+ var _pluck_order = function ( a, order, prop, prop2 )
1483
+ {
1484
+ var out = [];
1485
+ var i=0, ien=order.length;
1486
+
1487
+ // Could have the test in the loop for slightly smaller code, but speed
1488
+ // is essential here
1489
+ if ( prop2 !== undefined ) {
1490
+ for ( ; i<ien ; i++ ) {
1491
+ if ( a[ order[i] ][ prop ] ) {
1492
+ out.push( a[ order[i] ][ prop ][ prop2 ] );
1493
+ }
1494
+ }
1495
+ }
1496
+ else {
1497
+ for ( ; i<ien ; i++ ) {
1498
+ out.push( a[ order[i] ][ prop ] );
1499
+ }
1500
+ }
1501
+
1502
+ return out;
1503
+ };
1504
+
1505
+
1506
+ var _range = function ( len, start )
1507
+ {
1508
+ var out = [];
1509
+ var end;
1510
+
1511
+ if ( start === undefined ) {
1512
+ start = 0;
1513
+ end = len;
1514
+ }
1515
+ else {
1516
+ end = start;
1517
+ start = len;
1518
+ }
1519
+
1520
+ for ( var i=start ; i<end ; i++ ) {
1521
+ out.push( i );
1522
+ }
1523
+
1524
+ return out;
1525
+ };
1526
+
1527
+
1528
+ var _removeEmpty = function ( a )
1529
+ {
1530
+ var out = [];
1531
+
1532
+ for ( var i=0, ien=a.length ; i<ien ; i++ ) {
1533
+ if ( a[i] ) { // careful - will remove all falsy values!
1534
+ out.push( a[i] );
1535
+ }
1536
+ }
1537
+
1538
+ return out;
1539
+ };
1540
+
1541
+
1542
+ var _stripHtml = function ( d ) {
1543
+ return d.replace( _re_html, '' );
1544
+ };
1545
+
1546
+
1547
+ /**
1548
+ * Find the unique elements in a source array.
1549
+ *
1550
+ * @param {array} src Source array
1551
+ * @return {array} Array of unique items
1552
+ * @ignore
1553
+ */
1554
+ var _unique = function ( src )
1555
+ {
1556
+ // A faster unique method is to use object keys to identify used values,
1557
+ // but this doesn't work with arrays or objects, which we must also
1558
+ // consider. See jsperf.com/compare-array-unique-versions/4 for more
1559
+ // information.
1560
+ var
1561
+ out = [],
1562
+ val,
1563
+ i, ien=src.length,
1564
+ j, k=0;
1565
+
1566
+ again: for ( i=0 ; i<ien ; i++ ) {
1567
+ val = src[i];
1568
+
1569
+ for ( j=0 ; j<k ; j++ ) {
1570
+ if ( out[j] === val ) {
1571
+ continue again;
1572
+ }
1573
+ }
1574
+
1575
+ out.push( val );
1576
+ k++;
1577
+ }
1578
+
1579
+ return out;
1580
+ };
1581
+
1582
+
1583
+ /**
1584
+ * DataTables utility methods
1585
+ *
1586
+ * This namespace provides helper methods that DataTables uses internally to
1587
+ * create a DataTable, but which are not exclusively used only for DataTables.
1588
+ * These methods can be used by extension authors to save the duplication of
1589
+ * code.
1590
+ *
1591
+ * @namespace
1592
+ */
1593
+ DataTable.util = {
1594
+ /**
1595
+ * Throttle the calls to a function. Arguments and context are maintained
1596
+ * for the throttled function.
1597
+ *
1598
+ * @param {function} fn Function to be called
1599
+ * @param {integer} freq Call frequency in mS
1600
+ * @return {function} Wrapped function
1601
+ */
1602
+ throttle: function ( fn, freq ) {
1603
+ var
1604
+ frequency = freq !== undefined ? freq : 200,
1605
+ last,
1606
+ timer;
1607
+
1608
+ return function () {
1609
+ var
1610
+ that = this,
1611
+ now = +new Date(),
1612
+ args = arguments;
1613
+
1614
+ if ( last && now < last + frequency ) {
1615
+ clearTimeout( timer );
1616
+
1617
+ timer = setTimeout( function () {
1618
+ last = undefined;
1619
+ fn.apply( that, args );
1620
+ }, frequency );
1621
+ }
1622
+ else {
1623
+ last = now;
1624
+ fn.apply( that, args );
1625
+ }
1626
+ };
1627
+ },
1628
+
1629
+
1630
+ /**
1631
+ * Escape a string such that it can be used in a regular expression
1632
+ *
1633
+ * @param {string} val string to escape
1634
+ * @returns {string} escaped string
1635
+ */
1636
+ escapeRegex: function ( val ) {
1637
+ return val.replace( _re_escape_regex, '\\$1' );
1638
+ }
1639
+ };
1640
+
1641
+
1642
+
1643
+ /**
1644
+ * Create a mapping object that allows camel case parameters to be looked up
1645
+ * for their Hungarian counterparts. The mapping is stored in a private
1646
+ * parameter called `_hungarianMap` which can be accessed on the source object.
1647
+ * @param {object} o
1648
+ * @memberof DataTable#oApi
1649
+ */
1650
+ function _fnHungarianMap ( o )
1651
+ {
1652
+ var
1653
+ hungarian = 'a aa ai ao as b fn i m o s ',
1654
+ match,
1655
+ newKey,
1656
+ map = {};
1657
+
1658
+ $.each( o, function (key, val) {
1659
+ match = key.match(/^([^A-Z]+?)([A-Z])/);
1660
+
1661
+ if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
1662
+ {
1663
+ newKey = key.replace( match[0], match[2].toLowerCase() );
1664
+ map[ newKey ] = key;
1665
+
1666
+ if ( match[1] === 'o' )
1667
+ {
1668
+ _fnHungarianMap( o[key] );
1669
+ }
1670
+ }
1671
+ } );
1672
+
1673
+ o._hungarianMap = map;
1674
+ }
1675
+
1676
+
1677
+ /**
1678
+ * Convert from camel case parameters to Hungarian, based on a Hungarian map
1679
+ * created by _fnHungarianMap.
1680
+ * @param {object} src The model object which holds all parameters that can be
1681
+ * mapped.
1682
+ * @param {object} user The object to convert from camel case to Hungarian.
1683
+ * @param {boolean} force When set to `true`, properties which already have a
1684
+ * Hungarian value in the `user` object will be overwritten. Otherwise they
1685
+ * won't be.
1686
+ * @memberof DataTable#oApi
1687
+ */
1688
+ function _fnCamelToHungarian ( src, user, force )
1689
+ {
1690
+ if ( ! src._hungarianMap ) {
1691
+ _fnHungarianMap( src );
1692
+ }
1693
+
1694
+ var hungarianKey;
1695
+
1696
+ $.each( user, function (key, val) {
1697
+ hungarianKey = src._hungarianMap[ key ];
1698
+
1699
+ if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
1700
+ {
1701
+ // For objects, we need to buzz down into the object to copy parameters
1702
+ if ( hungarianKey.charAt(0) === 'o' )
1703
+ {
1704
+ // Copy the camelCase options over to the hungarian
1705
+ if ( ! user[ hungarianKey ] ) {
1706
+ user[ hungarianKey ] = {};
1707
+ }
1708
+ $.extend( true, user[hungarianKey], user[key] );
1709
+
1710
+ _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
1711
+ }
1712
+ else {
1713
+ user[hungarianKey] = user[ key ];
1714
+ }
1715
+ }
1716
+ } );
1717
+ }
1718
+
1719
+
1720
+ /**
1721
+ * Language compatibility - when certain options are given, and others aren't, we
1722
+ * need to duplicate the values over, in order to provide backwards compatibility
1723
+ * with older language files.
1724
+ * @param {object} oSettings dataTables settings object
1725
+ * @memberof DataTable#oApi
1726
+ */
1727
+ function _fnLanguageCompat( lang )
1728
+ {
1729
+ var defaults = DataTable.defaults.oLanguage;
1730
+ var zeroRecords = lang.sZeroRecords;
1731
+
1732
+ /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
1733
+ * sZeroRecords - assuming that is given.
1734
+ */
1735
+ if ( ! lang.sEmptyTable && zeroRecords &&
1736
+ defaults.sEmptyTable === "No data available in table" )
1737
+ {
1738
+ _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
1739
+ }
1740
+
1741
+ /* Likewise with loading records */
1742
+ if ( ! lang.sLoadingRecords && zeroRecords &&
1743
+ defaults.sLoadingRecords === "Loading..." )
1744
+ {
1745
+ _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
1746
+ }
1747
+
1748
+ // Old parameter name of the thousands separator mapped onto the new
1749
+ if ( lang.sInfoThousands ) {
1750
+ lang.sThousands = lang.sInfoThousands;
1751
+ }
1752
+
1753
+ var decimal = lang.sDecimal;
1754
+ if ( decimal ) {
1755
+ _addNumericSort( decimal );
1756
+ }
1757
+ }
1758
+
1759
+
1760
+ /**
1761
+ * Map one parameter onto another
1762
+ * @param {object} o Object to map
1763
+ * @param {*} knew The new parameter name
1764
+ * @param {*} old The old parameter name
1765
+ */
1766
+ var _fnCompatMap = function ( o, knew, old ) {
1767
+ if ( o[ knew ] !== undefined ) {
1768
+ o[ old ] = o[ knew ];
1769
+ }
1770
+ };
1771
+
1772
+
1773
+ /**
1774
+ * Provide backwards compatibility for the main DT options. Note that the new
1775
+ * options are mapped onto the old parameters, so this is an external interface
1776
+ * change only.
1777
+ * @param {object} init Object to map
1778
+ */
1779
+ function _fnCompatOpts ( init )
1780
+ {
1781
+ _fnCompatMap( init, 'ordering', 'bSort' );
1782
+ _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
1783
+ _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
1784
+ _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
1785
+ _fnCompatMap( init, 'order', 'aaSorting' );
1786
+ _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
1787
+ _fnCompatMap( init, 'paging', 'bPaginate' );
1788
+ _fnCompatMap( init, 'pagingType', 'sPaginationType' );
1789
+ _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
1790
+ _fnCompatMap( init, 'searching', 'bFilter' );
1791
+
1792
+ // Boolean initialisation of x-scrolling
1793
+ if ( typeof init.sScrollX === 'boolean' ) {
1794
+ init.sScrollX = init.sScrollX ? '100%' : '';
1795
+ }
1796
+ if ( typeof init.scrollX === 'boolean' ) {
1797
+ init.scrollX = init.scrollX ? '100%' : '';
1798
+ }
1799
+
1800
+ // Column search objects are in an array, so it needs to be converted
1801
+ // element by element
1802
+ var searchCols = init.aoSearchCols;
1803
+
1804
+ if ( searchCols ) {
1805
+ for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
1806
+ if ( searchCols[i] ) {
1807
+ _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
1808
+ }
1809
+ }
1810
+ }
1811
+ }
1812
+
1813
+
1814
+ /**
1815
+ * Provide backwards compatibility for column options. Note that the new options
1816
+ * are mapped onto the old parameters, so this is an external interface change
1817
+ * only.
1818
+ * @param {object} init Object to map
1819
+ */
1820
+ function _fnCompatCols ( init )
1821
+ {
1822
+ _fnCompatMap( init, 'orderable', 'bSortable' );
1823
+ _fnCompatMap( init, 'orderData', 'aDataSort' );
1824
+ _fnCompatMap( init, 'orderSequence', 'asSorting' );
1825
+ _fnCompatMap( init, 'orderDataType', 'sortDataType' );
1826
+
1827
+ // orderData can be given as an integer
1828
+ var dataSort = init.aDataSort;
1829
+ if ( dataSort && ! $.isArray( dataSort ) ) {
1830
+ init.aDataSort = [ dataSort ];
1831
+ }
1832
+ }
1833
+
1834
+
1835
+ /**
1836
+ * Browser feature detection for capabilities, quirks
1837
+ * @param {object} settings dataTables settings object
1838
+ * @memberof DataTable#oApi
1839
+ */
1840
+ function _fnBrowserDetect( settings )
1841
+ {
1842
+ // We don't need to do this every time DataTables is constructed, the values
1843
+ // calculated are specific to the browser and OS configuration which we
1844
+ // don't expect to change between initialisations
1845
+ if ( ! DataTable.__browser ) {
1846
+ var browser = {};
1847
+ DataTable.__browser = browser;
1848
+
1849
+ // Scrolling feature / quirks detection
1850
+ var n = $('<div/>')
1851
+ .css( {
1852
+ position: 'fixed',
1853
+ top: 0,
1854
+ left: 0,
1855
+ height: 1,
1856
+ width: 1,
1857
+ overflow: 'hidden'
1858
+ } )
1859
+ .append(
1860
+ $('<div/>')
1861
+ .css( {
1862
+ position: 'absolute',
1863
+ top: 1,
1864
+ left: 1,
1865
+ width: 100,
1866
+ overflow: 'scroll'
1867
+ } )
1868
+ .append(
1869
+ $('<div/>')
1870
+ .css( {
1871
+ width: '100%',
1872
+ height: 10
1873
+ } )
1874
+ )
1875
+ )
1876
+ .appendTo( 'body' );
1877
+
1878
+ var outer = n.children();
1879
+ var inner = outer.children();
1880
+
1881
+ // Numbers below, in order, are:
1882
+ // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
1883
+ //
1884
+ // IE6 XP: 100 100 100 83
1885
+ // IE7 Vista: 100 100 100 83
1886
+ // IE 8+ Windows: 83 83 100 83
1887
+ // Evergreen Windows: 83 83 100 83
1888
+ // Evergreen Mac with scrollbars: 85 85 100 85
1889
+ // Evergreen Mac without scrollbars: 100 100 100 100
1890
+
1891
+ // Get scrollbar width
1892
+ browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
1893
+
1894
+ // IE6/7 will oversize a width 100% element inside a scrolling element, to
1895
+ // include the width of the scrollbar, while other browsers ensure the inner
1896
+ // element is contained without forcing scrolling
1897
+ browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
1898
+
1899
+ // In rtl text layout, some browsers (most, but not all) will place the
1900
+ // scrollbar on the left, rather than the right.
1901
+ browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
1902
+
1903
+ // IE8- don't provide height and width for getBoundingClientRect
1904
+ browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
1905
+
1906
+ n.remove();
1907
+ }
1908
+
1909
+ $.extend( settings.oBrowser, DataTable.__browser );
1910
+ settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
1911
+ }
1912
+
1913
+
1914
+ /**
1915
+ * Array.prototype reduce[Right] method, used for browsers which don't support
1916
+ * JS 1.6. Done this way to reduce code size, since we iterate either way
1917
+ * @param {object} settings dataTables settings object
1918
+ * @memberof DataTable#oApi
1919
+ */
1920
+ function _fnReduce ( that, fn, init, start, end, inc )
1921
+ {
1922
+ var
1923
+ i = start,
1924
+ value,
1925
+ isSet = false;
1926
+
1927
+ if ( init !== undefined ) {
1928
+ value = init;
1929
+ isSet = true;
1930
+ }
1931
+
1932
+ while ( i !== end ) {
1933
+ if ( ! that.hasOwnProperty(i) ) {
1934
+ continue;
1935
+ }
1936
+
1937
+ value = isSet ?
1938
+ fn( value, that[i], i, that ) :
1939
+ that[i];
1940
+
1941
+ isSet = true;
1942
+ i += inc;
1943
+ }
1944
+
1945
+ return value;
1946
+ }
1947
+
1948
+ /**
1949
+ * Add a column to the list used for the table with default values
1950
+ * @param {object} oSettings dataTables settings object
1951
+ * @param {node} nTh The th element for this column
1952
+ * @memberof DataTable#oApi
1953
+ */
1954
+ function _fnAddColumn( oSettings, nTh )
1955
+ {
1956
+ // Add column to aoColumns array
1957
+ var oDefaults = DataTable.defaults.column;
1958
+ var iCol = oSettings.aoColumns.length;
1959
+ var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
1960
+ "nTh": nTh ? nTh : document.createElement('th'),
1961
+ "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
1962
+ "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
1963
+ "mData": oDefaults.mData ? oDefaults.mData : iCol,
1964
+ idx: iCol
1965
+ } );
1966
+ oSettings.aoColumns.push( oCol );
1967
+
1968
+ // Add search object for column specific search. Note that the `searchCols[ iCol ]`
1969
+ // passed into extend can be undefined. This allows the user to give a default
1970
+ // with only some of the parameters defined, and also not give a default
1971
+ var searchCols = oSettings.aoPreSearchCols;
1972
+ searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
1973
+
1974
+ // Use the default column options function to initialise classes etc
1975
+ _fnColumnOptions( oSettings, iCol, $(nTh).data() );
1976
+ }
1977
+
1978
+
1979
+ /**
1980
+ * Apply options for a column
1981
+ * @param {object} oSettings dataTables settings object
1982
+ * @param {int} iCol column index to consider
1983
+ * @param {object} oOptions object with sType, bVisible and bSearchable etc
1984
+ * @memberof DataTable#oApi
1985
+ */
1986
+ function _fnColumnOptions( oSettings, iCol, oOptions )
1987
+ {
1988
+ var oCol = oSettings.aoColumns[ iCol ];
1989
+ var oClasses = oSettings.oClasses;
1990
+ var th = $(oCol.nTh);
1991
+
1992
+ // Try to get width information from the DOM. We can't get it from CSS
1993
+ // as we'd need to parse the CSS stylesheet. `width` option can override
1994
+ if ( ! oCol.sWidthOrig ) {
1995
+ // Width attribute
1996
+ oCol.sWidthOrig = th.attr('width') || null;
1997
+
1998
+ // Style attribute
1999
+ var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
2000
+ if ( t ) {
2001
+ oCol.sWidthOrig = t[1];
2002
+ }
2003
+ }
2004
+
2005
+ /* User specified column options */
2006
+ if ( oOptions !== undefined && oOptions !== null )
2007
+ {
2008
+ // Backwards compatibility
2009
+ _fnCompatCols( oOptions );
2010
+
2011
+ // Map camel case parameters to their Hungarian counterparts
2012
+ _fnCamelToHungarian( DataTable.defaults.column, oOptions );
2013
+
2014
+ /* Backwards compatibility for mDataProp */
2015
+ if ( oOptions.mDataProp !== undefined && !oOptions.mData )
2016
+ {
2017
+ oOptions.mData = oOptions.mDataProp;
2018
+ }
2019
+
2020
+ if ( oOptions.sType )
2021
+ {
2022
+ oCol._sManualType = oOptions.sType;
2023
+ }
2024
+
2025
+ // `class` is a reserved word in Javascript, so we need to provide
2026
+ // the ability to use a valid name for the camel case input
2027
+ if ( oOptions.className && ! oOptions.sClass )
2028
+ {
2029
+ oOptions.sClass = oOptions.className;
2030
+ }
2031
+
2032
+ $.extend( oCol, oOptions );
2033
+ _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
2034
+
2035
+ /* iDataSort to be applied (backwards compatibility), but aDataSort will take
2036
+ * priority if defined
2037
+ */
2038
+ if ( oOptions.iDataSort !== undefined )
2039
+ {
2040
+ oCol.aDataSort = [ oOptions.iDataSort ];
2041
+ }
2042
+ _fnMap( oCol, oOptions, "aDataSort" );
2043
+ }
2044
+
2045
+ /* Cache the data get and set functions for speed */
2046
+ var mDataSrc = oCol.mData;
2047
+ var mData = _fnGetObjectDataFn( mDataSrc );
2048
+ var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
2049
+
2050
+ var attrTest = function( src ) {
2051
+ return typeof src === 'string' && src.indexOf('@') !== -1;
2052
+ };
2053
+ oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
2054
+ attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
2055
+ );
2056
+ oCol._setter = null;
2057
+
2058
+ oCol.fnGetData = function (rowData, type, meta) {
2059
+ var innerData = mData( rowData, type, undefined, meta );
2060
+
2061
+ return mRender && type ?
2062
+ mRender( innerData, type, rowData, meta ) :
2063
+ innerData;
2064
+ };
2065
+ oCol.fnSetData = function ( rowData, val, meta ) {
2066
+ return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
2067
+ };
2068
+
2069
+ // Indicate if DataTables should read DOM data as an object or array
2070
+ // Used in _fnGetRowElements
2071
+ if ( typeof mDataSrc !== 'number' ) {
2072
+ oSettings._rowReadObject = true;
2073
+ }
2074
+
2075
+ /* Feature sorting overrides column specific when off */
2076
+ if ( !oSettings.oFeatures.bSort )
2077
+ {
2078
+ oCol.bSortable = false;
2079
+ th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
2080
+ }
2081
+
2082
+ /* Check that the class assignment is correct for sorting */
2083
+ var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
2084
+ var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
2085
+ if ( !oCol.bSortable || (!bAsc && !bDesc) )
2086
+ {
2087
+ oCol.sSortingClass = oClasses.sSortableNone;
2088
+ oCol.sSortingClassJUI = "";
2089
+ }
2090
+ else if ( bAsc && !bDesc )
2091
+ {
2092
+ oCol.sSortingClass = oClasses.sSortableAsc;
2093
+ oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
2094
+ }
2095
+ else if ( !bAsc && bDesc )
2096
+ {
2097
+ oCol.sSortingClass = oClasses.sSortableDesc;
2098
+ oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
2099
+ }
2100
+ else
2101
+ {
2102
+ oCol.sSortingClass = oClasses.sSortable;
2103
+ oCol.sSortingClassJUI = oClasses.sSortJUI;
2104
+ }
2105
+ }
2106
+
2107
+
2108
+ /**
2109
+ * Adjust the table column widths for new data. Note: you would probably want to
2110
+ * do a redraw after calling this function!
2111
+ * @param {object} settings dataTables settings object
2112
+ * @memberof DataTable#oApi
2113
+ */
2114
+ function _fnAdjustColumnSizing ( settings )
2115
+ {
2116
+ /* Not interested in doing column width calculation if auto-width is disabled */
2117
+ if ( settings.oFeatures.bAutoWidth !== false )
2118
+ {
2119
+ var columns = settings.aoColumns;
2120
+
2121
+ _fnCalculateColumnWidths( settings );
2122
+ for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
2123
+ {
2124
+ columns[i].nTh.style.width = columns[i].sWidth;
2125
+ }
2126
+ }
2127
+
2128
+ var scroll = settings.oScroll;
2129
+ if ( scroll.sY !== '' || scroll.sX !== '')
2130
+ {
2131
+ _fnScrollDraw( settings );
2132
+ }
2133
+
2134
+ _fnCallbackFire( settings, null, 'column-sizing', [settings] );
2135
+ }
2136
+
2137
+
2138
+ /**
2139
+ * Covert the index of a visible column to the index in the data array (take account
2140
+ * of hidden columns)
2141
+ * @param {object} oSettings dataTables settings object
2142
+ * @param {int} iMatch Visible column index to lookup
2143
+ * @returns {int} i the data index
2144
+ * @memberof DataTable#oApi
2145
+ */
2146
+ function _fnVisibleToColumnIndex( oSettings, iMatch )
2147
+ {
2148
+ var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2149
+
2150
+ return typeof aiVis[iMatch] === 'number' ?
2151
+ aiVis[iMatch] :
2152
+ null;
2153
+ }
2154
+
2155
+
2156
+ /**
2157
+ * Covert the index of an index in the data array and convert it to the visible
2158
+ * column index (take account of hidden columns)
2159
+ * @param {int} iMatch Column index to lookup
2160
+ * @param {object} oSettings dataTables settings object
2161
+ * @returns {int} i the data index
2162
+ * @memberof DataTable#oApi
2163
+ */
2164
+ function _fnColumnIndexToVisible( oSettings, iMatch )
2165
+ {
2166
+ var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2167
+ var iPos = $.inArray( iMatch, aiVis );
2168
+
2169
+ return iPos !== -1 ? iPos : null;
2170
+ }
2171
+
2172
+
2173
+ /**
2174
+ * Get the number of visible columns
2175
+ * @param {object} oSettings dataTables settings object
2176
+ * @returns {int} i the number of visible columns
2177
+ * @memberof DataTable#oApi
2178
+ */
2179
+ function _fnVisbleColumns( oSettings )
2180
+ {
2181
+ var vis = 0;
2182
+
2183
+ // No reduce in IE8, use a loop for now
2184
+ $.each( oSettings.aoColumns, function ( i, col ) {
2185
+ if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {
2186
+ vis++;
2187
+ }
2188
+ } );
2189
+
2190
+ return vis;
2191
+ }
2192
+
2193
+
2194
+ /**
2195
+ * Get an array of column indexes that match a given property
2196
+ * @param {object} oSettings dataTables settings object
2197
+ * @param {string} sParam Parameter in aoColumns to look for - typically
2198
+ * bVisible or bSearchable
2199
+ * @returns {array} Array of indexes with matched properties
2200
+ * @memberof DataTable#oApi
2201
+ */
2202
+ function _fnGetColumns( oSettings, sParam )
2203
+ {
2204
+ var a = [];
2205
+
2206
+ $.map( oSettings.aoColumns, function(val, i) {
2207
+ if ( val[sParam] ) {
2208
+ a.push( i );
2209
+ }
2210
+ } );
2211
+
2212
+ return a;
2213
+ }
2214
+
2215
+
2216
+ /**
2217
+ * Calculate the 'type' of a column
2218
+ * @param {object} settings dataTables settings object
2219
+ * @memberof DataTable#oApi
2220
+ */
2221
+ function _fnColumnTypes ( settings )
2222
+ {
2223
+ var columns = settings.aoColumns;
2224
+ var data = settings.aoData;
2225
+ var types = DataTable.ext.type.detect;
2226
+ var i, ien, j, jen, k, ken;
2227
+ var col, cell, detectedType, cache;
2228
+
2229
+ // For each column, spin over the
2230
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
2231
+ col = columns[i];
2232
+ cache = [];
2233
+
2234
+ if ( ! col.sType && col._sManualType ) {
2235
+ col.sType = col._sManualType;
2236
+ }
2237
+ else if ( ! col.sType ) {
2238
+ for ( j=0, jen=types.length ; j<jen ; j++ ) {
2239
+ for ( k=0, ken=data.length ; k<ken ; k++ ) {
2240
+ // Use a cache array so we only need to get the type data
2241
+ // from the formatter once (when using multiple detectors)
2242
+ if ( cache[k] === undefined ) {
2243
+ cache[k] = _fnGetCellData( settings, k, i, 'type' );
2244
+ }
2245
+
2246
+ detectedType = types[j]( cache[k], settings );
2247
+
2248
+ // If null, then this type can't apply to this column, so
2249
+ // rather than testing all cells, break out. There is an
2250
+ // exception for the last type which is `html`. We need to
2251
+ // scan all rows since it is possible to mix string and HTML
2252
+ // types
2253
+ if ( ! detectedType && j !== types.length-1 ) {
2254
+ break;
2255
+ }
2256
+
2257
+ // Only a single match is needed for html type since it is
2258
+ // bottom of the pile and very similar to string
2259
+ if ( detectedType === 'html' ) {
2260
+ break;
2261
+ }
2262
+ }
2263
+
2264
+ // Type is valid for all data points in the column - use this
2265
+ // type
2266
+ if ( detectedType ) {
2267
+ col.sType = detectedType;
2268
+ break;
2269
+ }
2270
+ }
2271
+
2272
+ // Fall back - if no type was detected, always use string
2273
+ if ( ! col.sType ) {
2274
+ col.sType = 'string';
2275
+ }
2276
+ }
2277
+ }
2278
+ }
2279
+
2280
+
2281
+ /**
2282
+ * Take the column definitions and static columns arrays and calculate how
2283
+ * they relate to column indexes. The callback function will then apply the
2284
+ * definition found for a column to a suitable configuration object.
2285
+ * @param {object} oSettings dataTables settings object
2286
+ * @param {array} aoColDefs The aoColumnDefs array that is to be applied
2287
+ * @param {array} aoCols The aoColumns array that defines columns individually
2288
+ * @param {function} fn Callback function - takes two parameters, the calculated
2289
+ * column index and the definition for that column.
2290
+ * @memberof DataTable#oApi
2291
+ */
2292
+ function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
2293
+ {
2294
+ var i, iLen, j, jLen, k, kLen, def;
2295
+ var columns = oSettings.aoColumns;
2296
+
2297
+ // Column definitions with aTargets
2298
+ if ( aoColDefs )
2299
+ {
2300
+ /* Loop over the definitions array - loop in reverse so first instance has priority */
2301
+ for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
2302
+ {
2303
+ def = aoColDefs[i];
2304
+
2305
+ /* Each definition can target multiple columns, as it is an array */
2306
+ var aTargets = def.targets !== undefined ?
2307
+ def.targets :
2308
+ def.aTargets;
2309
+
2310
+ if ( ! $.isArray( aTargets ) )
2311
+ {
2312
+ aTargets = [ aTargets ];
2313
+ }
2314
+
2315
+ for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
2316
+ {
2317
+ if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
2318
+ {
2319
+ /* Add columns that we don't yet know about */
2320
+ while( columns.length <= aTargets[j] )
2321
+ {
2322
+ _fnAddColumn( oSettings );
2323
+ }
2324
+
2325
+ /* Integer, basic index */
2326
+ fn( aTargets[j], def );
2327
+ }
2328
+ else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
2329
+ {
2330
+ /* Negative integer, right to left column counting */
2331
+ fn( columns.length+aTargets[j], def );
2332
+ }
2333
+ else if ( typeof aTargets[j] === 'string' )
2334
+ {
2335
+ /* Class name matching on TH element */
2336
+ for ( k=0, kLen=columns.length ; k<kLen ; k++ )
2337
+ {
2338
+ if ( aTargets[j] == "_all" ||
2339
+ $(columns[k].nTh).hasClass( aTargets[j] ) )
2340
+ {
2341
+ fn( k, def );
2342
+ }
2343
+ }
2344
+ }
2345
+ }
2346
+ }
2347
+ }
2348
+
2349
+ // Statically defined columns array
2350
+ if ( aoCols )
2351
+ {
2352
+ for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
2353
+ {
2354
+ fn( i, aoCols[i] );
2355
+ }
2356
+ }
2357
+ }
2358
+
2359
+ /**
2360
+ * Add a data array to the table, creating DOM node etc. This is the parallel to
2361
+ * _fnGatherData, but for adding rows from a Javascript source, rather than a
2362
+ * DOM source.
2363
+ * @param {object} oSettings dataTables settings object
2364
+ * @param {array} aData data array to be added
2365
+ * @param {node} [nTr] TR element to add to the table - optional. If not given,
2366
+ * DataTables will create a row automatically
2367
+ * @param {array} [anTds] Array of TD|TH elements for the row - must be given
2368
+ * if nTr is.
2369
+ * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
2370
+ * @memberof DataTable#oApi
2371
+ */
2372
+ function _fnAddData ( oSettings, aDataIn, nTr, anTds )
2373
+ {
2374
+ /* Create the object for storing information about this new row */
2375
+ var iRow = oSettings.aoData.length;
2376
+ var oData = $.extend( true, {}, DataTable.models.oRow, {
2377
+ src: nTr ? 'dom' : 'data',
2378
+ idx: iRow
2379
+ } );
2380
+
2381
+ oData._aData = aDataIn;
2382
+ oSettings.aoData.push( oData );
2383
+
2384
+ /* Create the cells */
2385
+ var nTd, sThisType;
2386
+ var columns = oSettings.aoColumns;
2387
+
2388
+ // Invalidate the column types as the new data needs to be revalidated
2389
+ for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
2390
+ {
2391
+ columns[i].sType = null;
2392
+ }
2393
+
2394
+ /* Add to the display array */
2395
+ oSettings.aiDisplayMaster.push( iRow );
2396
+
2397
+ var id = oSettings.rowIdFn( aDataIn );
2398
+ if ( id !== undefined ) {
2399
+ oSettings.aIds[ id ] = oData;
2400
+ }
2401
+
2402
+ /* Create the DOM information, or register it if already present */
2403
+ if ( nTr || ! oSettings.oFeatures.bDeferRender )
2404
+ {
2405
+ _fnCreateTr( oSettings, iRow, nTr, anTds );
2406
+ }
2407
+
2408
+ return iRow;
2409
+ }
2410
+
2411
+
2412
+ /**
2413
+ * Add one or more TR elements to the table. Generally we'd expect to
2414
+ * use this for reading data from a DOM sourced table, but it could be
2415
+ * used for an TR element. Note that if a TR is given, it is used (i.e.
2416
+ * it is not cloned).
2417
+ * @param {object} settings dataTables settings object
2418
+ * @param {array|node|jQuery} trs The TR element(s) to add to the table
2419
+ * @returns {array} Array of indexes for the added rows
2420
+ * @memberof DataTable#oApi
2421
+ */
2422
+ function _fnAddTr( settings, trs )
2423
+ {
2424
+ var row;
2425
+
2426
+ // Allow an individual node to be passed in
2427
+ if ( ! (trs instanceof $) ) {
2428
+ trs = $(trs);
2429
+ }
2430
+
2431
+ return trs.map( function (i, el) {
2432
+ row = _fnGetRowElements( settings, el );
2433
+ return _fnAddData( settings, row.data, el, row.cells );
2434
+ } );
2435
+ }
2436
+
2437
+
2438
+ /**
2439
+ * Take a TR element and convert it to an index in aoData
2440
+ * @param {object} oSettings dataTables settings object
2441
+ * @param {node} n the TR element to find
2442
+ * @returns {int} index if the node is found, null if not
2443
+ * @memberof DataTable#oApi
2444
+ */
2445
+ function _fnNodeToDataIndex( oSettings, n )
2446
+ {
2447
+ return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
2448
+ }
2449
+
2450
+
2451
+ /**
2452
+ * Take a TD element and convert it into a column data index (not the visible index)
2453
+ * @param {object} oSettings dataTables settings object
2454
+ * @param {int} iRow The row number the TD/TH can be found in
2455
+ * @param {node} n The TD/TH element to find
2456
+ * @returns {int} index if the node is found, -1 if not
2457
+ * @memberof DataTable#oApi
2458
+ */
2459
+ function _fnNodeToColumnIndex( oSettings, iRow, n )
2460
+ {
2461
+ return $.inArray( n, oSettings.aoData[ iRow ].anCells );
2462
+ }
2463
+
2464
+
2465
+ /**
2466
+ * Get the data for a given cell from the internal cache, taking into account data mapping
2467
+ * @param {object} settings dataTables settings object
2468
+ * @param {int} rowIdx aoData row id
2469
+ * @param {int} colIdx Column index
2470
+ * @param {string} type data get type ('display', 'type' 'filter' 'sort')
2471
+ * @returns {*} Cell data
2472
+ * @memberof DataTable#oApi
2473
+ */
2474
+ function _fnGetCellData( settings, rowIdx, colIdx, type )
2475
+ {
2476
+ var draw = settings.iDraw;
2477
+ var col = settings.aoColumns[colIdx];
2478
+ var rowData = settings.aoData[rowIdx]._aData;
2479
+ var defaultContent = col.sDefaultContent;
2480
+ var cellData = col.fnGetData( rowData, type, {
2481
+ settings: settings,
2482
+ row: rowIdx,
2483
+ col: colIdx
2484
+ } );
2485
+
2486
+ if ( cellData === undefined ) {
2487
+ if ( settings.iDrawError != draw && defaultContent === null ) {
2488
+ _fnLog( settings, 0, "Requested unknown parameter "+
2489
+ (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
2490
+ " for row "+rowIdx+", column "+colIdx, 4 );
2491
+ settings.iDrawError = draw;
2492
+ }
2493
+ return defaultContent;
2494
+ }
2495
+
2496
+ // When the data source is null and a specific data type is requested (i.e.
2497
+ // not the original data), we can use default column data
2498
+ if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
2499
+ cellData = defaultContent;
2500
+ }
2501
+ else if ( typeof cellData === 'function' ) {
2502
+ // If the data source is a function, then we run it and use the return,
2503
+ // executing in the scope of the data object (for instances)
2504
+ return cellData.call( rowData );
2505
+ }
2506
+
2507
+ if ( cellData === null && type == 'display' ) {
2508
+ return '';
2509
+ }
2510
+ return cellData;
2511
+ }
2512
+
2513
+
2514
+ /**
2515
+ * Set the value for a specific cell, into the internal data cache
2516
+ * @param {object} settings dataTables settings object
2517
+ * @param {int} rowIdx aoData row id
2518
+ * @param {int} colIdx Column index
2519
+ * @param {*} val Value to set
2520
+ * @memberof DataTable#oApi
2521
+ */
2522
+ function _fnSetCellData( settings, rowIdx, colIdx, val )
2523
+ {
2524
+ var col = settings.aoColumns[colIdx];
2525
+ var rowData = settings.aoData[rowIdx]._aData;
2526
+
2527
+ col.fnSetData( rowData, val, {
2528
+ settings: settings,
2529
+ row: rowIdx,
2530
+ col: colIdx
2531
+ } );
2532
+ }
2533
+
2534
+
2535
+ // Private variable that is used to match action syntax in the data property object
2536
+ var __reArray = /\[.*?\]$/;
2537
+ var __reFn = /\(\)$/;
2538
+
2539
+ /**
2540
+ * Split string on periods, taking into account escaped periods
2541
+ * @param {string} str String to split
2542
+ * @return {array} Split string
2543
+ */
2544
+ function _fnSplitObjNotation( str )
2545
+ {
2546
+ return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
2547
+ return s.replace(/\\./g, '.');
2548
+ } );
2549
+ }
2550
+
2551
+
2552
+ /**
2553
+ * Return a function that can be used to get data from a source object, taking
2554
+ * into account the ability to use nested objects as a source
2555
+ * @param {string|int|function} mSource The data source for the object
2556
+ * @returns {function} Data get function
2557
+ * @memberof DataTable#oApi
2558
+ */
2559
+ function _fnGetObjectDataFn( mSource )
2560
+ {
2561
+ if ( $.isPlainObject( mSource ) )
2562
+ {
2563
+ /* Build an object of get functions, and wrap them in a single call */
2564
+ var o = {};
2565
+ $.each( mSource, function (key, val) {
2566
+ if ( val ) {
2567
+ o[key] = _fnGetObjectDataFn( val );
2568
+ }
2569
+ } );
2570
+
2571
+ return function (data, type, row, meta) {
2572
+ var t = o[type] || o._;
2573
+ return t !== undefined ?
2574
+ t(data, type, row, meta) :
2575
+ data;
2576
+ };
2577
+ }
2578
+ else if ( mSource === null )
2579
+ {
2580
+ /* Give an empty string for rendering / sorting etc */
2581
+ return function (data) { // type, row and meta also passed, but not used
2582
+ return data;
2583
+ };
2584
+ }
2585
+ else if ( typeof mSource === 'function' )
2586
+ {
2587
+ return function (data, type, row, meta) {
2588
+ return mSource( data, type, row, meta );
2589
+ };
2590
+ }
2591
+ else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
2592
+ mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
2593
+ {
2594
+ /* If there is a . in the source string then the data source is in a
2595
+ * nested object so we loop over the data for each level to get the next
2596
+ * level down. On each loop we test for undefined, and if found immediately
2597
+ * return. This allows entire objects to be missing and sDefaultContent to
2598
+ * be used if defined, rather than throwing an error
2599
+ */
2600
+ var fetchData = function (data, type, src) {
2601
+ var arrayNotation, funcNotation, out, innerSrc;
2602
+
2603
+ if ( src !== "" )
2604
+ {
2605
+ var a = _fnSplitObjNotation( src );
2606
+
2607
+ for ( var i=0, iLen=a.length ; i<iLen ; i++ )
2608
+ {
2609
+ // Check if we are dealing with special notation
2610
+ arrayNotation = a[i].match(__reArray);
2611
+ funcNotation = a[i].match(__reFn);
2612
+
2613
+ if ( arrayNotation )
2614
+ {
2615
+ // Array notation
2616
+ a[i] = a[i].replace(__reArray, '');
2617
+
2618
+ // Condition allows simply [] to be passed in
2619
+ if ( a[i] !== "" ) {
2620
+ data = data[ a[i] ];
2621
+ }
2622
+ out = [];
2623
+
2624
+ // Get the remainder of the nested object to get
2625
+ a.splice( 0, i+1 );
2626
+ innerSrc = a.join('.');
2627
+
2628
+ // Traverse each entry in the array getting the properties requested
2629
+ if ( $.isArray( data ) ) {
2630
+ for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
2631
+ out.push( fetchData( data[j], type, innerSrc ) );
2632
+ }
2633
+ }
2634
+
2635
+ // If a string is given in between the array notation indicators, that
2636
+ // is used to join the strings together, otherwise an array is returned
2637
+ var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
2638
+ data = (join==="") ? out : out.join(join);
2639
+
2640
+ // The inner call to fetchData has already traversed through the remainder
2641
+ // of the source requested, so we exit from the loop
2642
+ break;
2643
+ }
2644
+ else if ( funcNotation )
2645
+ {
2646
+ // Function call
2647
+ a[i] = a[i].replace(__reFn, '');
2648
+ data = data[ a[i] ]();
2649
+ continue;
2650
+ }
2651
+
2652
+ if ( data === null || data[ a[i] ] === undefined )
2653
+ {
2654
+ return undefined;
2655
+ }
2656
+ data = data[ a[i] ];
2657
+ }
2658
+ }
2659
+
2660
+ return data;
2661
+ };
2662
+
2663
+ return function (data, type) { // row and meta also passed, but not used
2664
+ return fetchData( data, type, mSource );
2665
+ };
2666
+ }
2667
+ else
2668
+ {
2669
+ /* Array or flat object mapping */
2670
+ return function (data, type) { // row and meta also passed, but not used
2671
+ return data[mSource];
2672
+ };
2673
+ }
2674
+ }
2675
+
2676
+
2677
+ /**
2678
+ * Return a function that can be used to set data from a source object, taking
2679
+ * into account the ability to use nested objects as a source
2680
+ * @param {string|int|function} mSource The data source for the object
2681
+ * @returns {function} Data set function
2682
+ * @memberof DataTable#oApi
2683
+ */
2684
+ function _fnSetObjectDataFn( mSource )
2685
+ {
2686
+ if ( $.isPlainObject( mSource ) )
2687
+ {
2688
+ /* Unlike get, only the underscore (global) option is used for for
2689
+ * setting data since we don't know the type here. This is why an object
2690
+ * option is not documented for `mData` (which is read/write), but it is
2691
+ * for `mRender` which is read only.
2692
+ */
2693
+ return _fnSetObjectDataFn( mSource._ );
2694
+ }
2695
+ else if ( mSource === null )
2696
+ {
2697
+ /* Nothing to do when the data source is null */
2698
+ return function () {};
2699
+ }
2700
+ else if ( typeof mSource === 'function' )
2701
+ {
2702
+ return function (data, val, meta) {
2703
+ mSource( data, 'set', val, meta );
2704
+ };
2705
+ }
2706
+ else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
2707
+ mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
2708
+ {
2709
+ /* Like the get, we need to get data from a nested object */
2710
+ var setData = function (data, val, src) {
2711
+ var a = _fnSplitObjNotation( src ), b;
2712
+ var aLast = a[a.length-1];
2713
+ var arrayNotation, funcNotation, o, innerSrc;
2714
+
2715
+ for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
2716
+ {
2717
+ // Check if we are dealing with an array notation request
2718
+ arrayNotation = a[i].match(__reArray);
2719
+ funcNotation = a[i].match(__reFn);
2720
+
2721
+ if ( arrayNotation )
2722
+ {
2723
+ a[i] = a[i].replace(__reArray, '');
2724
+ data[ a[i] ] = [];
2725
+
2726
+ // Get the remainder of the nested object to set so we can recurse
2727
+ b = a.slice();
2728
+ b.splice( 0, i+1 );
2729
+ innerSrc = b.join('.');
2730
+
2731
+ // Traverse each entry in the array setting the properties requested
2732
+ if ( $.isArray( val ) )
2733
+ {
2734
+ for ( var j=0, jLen=val.length ; j<jLen ; j++ )
2735
+ {
2736
+ o = {};
2737
+ setData( o, val[j], innerSrc );
2738
+ data[ a[i] ].push( o );
2739
+ }
2740
+ }
2741
+ else
2742
+ {
2743
+ // We've been asked to save data to an array, but it
2744
+ // isn't array data to be saved. Best that can be done
2745
+ // is to just save the value.
2746
+ data[ a[i] ] = val;
2747
+ }
2748
+
2749
+ // The inner call to setData has already traversed through the remainder
2750
+ // of the source and has set the data, thus we can exit here
2751
+ return;
2752
+ }
2753
+ else if ( funcNotation )
2754
+ {
2755
+ // Function call
2756
+ a[i] = a[i].replace(__reFn, '');
2757
+ data = data[ a[i] ]( val );
2758
+ }
2759
+
2760
+ // If the nested object doesn't currently exist - since we are
2761
+ // trying to set the value - create it
2762
+ if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
2763
+ {
2764
+ data[ a[i] ] = {};
2765
+ }
2766
+ data = data[ a[i] ];
2767
+ }
2768
+
2769
+ // Last item in the input - i.e, the actual set
2770
+ if ( aLast.match(__reFn ) )
2771
+ {
2772
+ // Function call
2773
+ data = data[ aLast.replace(__reFn, '') ]( val );
2774
+ }
2775
+ else
2776
+ {
2777
+ // If array notation is used, we just want to strip it and use the property name
2778
+ // and assign the value. If it isn't used, then we get the result we want anyway
2779
+ data[ aLast.replace(__reArray, '') ] = val;
2780
+ }
2781
+ };
2782
+
2783
+ return function (data, val) { // meta is also passed in, but not used
2784
+ return setData( data, val, mSource );
2785
+ };
2786
+ }
2787
+ else
2788
+ {
2789
+ /* Array or flat object mapping */
2790
+ return function (data, val) { // meta is also passed in, but not used
2791
+ data[mSource] = val;
2792
+ };
2793
+ }
2794
+ }
2795
+
2796
+
2797
+ /**
2798
+ * Return an array with the full table data
2799
+ * @param {object} oSettings dataTables settings object
2800
+ * @returns array {array} aData Master data array
2801
+ * @memberof DataTable#oApi
2802
+ */
2803
+ function _fnGetDataMaster ( settings )
2804
+ {
2805
+ return _pluck( settings.aoData, '_aData' );
2806
+ }
2807
+
2808
+
2809
+ /**
2810
+ * Nuke the table
2811
+ * @param {object} oSettings dataTables settings object
2812
+ * @memberof DataTable#oApi
2813
+ */
2814
+ function _fnClearTable( settings )
2815
+ {
2816
+ settings.aoData.length = 0;
2817
+ settings.aiDisplayMaster.length = 0;
2818
+ settings.aiDisplay.length = 0;
2819
+ settings.aIds = {};
2820
+ }
2821
+
2822
+
2823
+ /**
2824
+ * Take an array of integers (index array) and remove a target integer (value - not
2825
+ * the key!)
2826
+ * @param {array} a Index array to target
2827
+ * @param {int} iTarget value to find
2828
+ * @memberof DataTable#oApi
2829
+ */
2830
+ function _fnDeleteIndex( a, iTarget, splice )
2831
+ {
2832
+ var iTargetIndex = -1;
2833
+
2834
+ for ( var i=0, iLen=a.length ; i<iLen ; i++ )
2835
+ {
2836
+ if ( a[i] == iTarget )
2837
+ {
2838
+ iTargetIndex = i;
2839
+ }
2840
+ else if ( a[i] > iTarget )
2841
+ {
2842
+ a[i]--;
2843
+ }
2844
+ }
2845
+
2846
+ if ( iTargetIndex != -1 && splice === undefined )
2847
+ {
2848
+ a.splice( iTargetIndex, 1 );
2849
+ }
2850
+ }
2851
+
2852
+
2853
+ /**
2854
+ * Mark cached data as invalid such that a re-read of the data will occur when
2855
+ * the cached data is next requested. Also update from the data source object.
2856
+ *
2857
+ * @param {object} settings DataTables settings object
2858
+ * @param {int} rowIdx Row index to invalidate
2859
+ * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
2860
+ * or 'data'
2861
+ * @param {int} [colIdx] Column index to invalidate. If undefined the whole
2862
+ * row will be invalidated
2863
+ * @memberof DataTable#oApi
2864
+ *
2865
+ * @todo For the modularisation of v1.11 this will need to become a callback, so
2866
+ * the sort and filter methods can subscribe to it. That will required
2867
+ * initialisation options for sorting, which is why it is not already baked in
2868
+ */
2869
+ function _fnInvalidate( settings, rowIdx, src, colIdx )
2870
+ {
2871
+ var row = settings.aoData[ rowIdx ];
2872
+ var i, ien;
2873
+ var cellWrite = function ( cell, col ) {
2874
+ // This is very frustrating, but in IE if you just write directly
2875
+ // to innerHTML, and elements that are overwritten are GC'ed,
2876
+ // even if there is a reference to them elsewhere
2877
+ while ( cell.childNodes.length ) {
2878
+ cell.removeChild( cell.firstChild );
2879
+ }
2880
+
2881
+ cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
2882
+ };
2883
+
2884
+ // Are we reading last data from DOM or the data object?
2885
+ if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
2886
+ // Read the data from the DOM
2887
+ row._aData = _fnGetRowElements(
2888
+ settings, row, colIdx, colIdx === undefined ? undefined : row._aData
2889
+ )
2890
+ .data;
2891
+ }
2892
+ else {
2893
+ // Reading from data object, update the DOM
2894
+ var cells = row.anCells;
2895
+
2896
+ if ( cells ) {
2897
+ if ( colIdx !== undefined ) {
2898
+ cellWrite( cells[colIdx], colIdx );
2899
+ }
2900
+ else {
2901
+ for ( i=0, ien=cells.length ; i<ien ; i++ ) {
2902
+ cellWrite( cells[i], i );
2903
+ }
2904
+ }
2905
+ }
2906
+ }
2907
+
2908
+ // For both row and cell invalidation, the cached data for sorting and
2909
+ // filtering is nulled out
2910
+ row._aSortData = null;
2911
+ row._aFilterData = null;
2912
+
2913
+ // Invalidate the type for a specific column (if given) or all columns since
2914
+ // the data might have changed
2915
+ var cols = settings.aoColumns;
2916
+ if ( colIdx !== undefined ) {
2917
+ cols[ colIdx ].sType = null;
2918
+ }
2919
+ else {
2920
+ for ( i=0, ien=cols.length ; i<ien ; i++ ) {
2921
+ cols[i].sType = null;
2922
+ }
2923
+
2924
+ // Update DataTables special `DT_*` attributes for the row
2925
+ _fnRowAttributes( settings, row );
2926
+ }
2927
+ }
2928
+
2929
+
2930
+ /**
2931
+ * Build a data source object from an HTML row, reading the contents of the
2932
+ * cells that are in the row.
2933
+ *
2934
+ * @param {object} settings DataTables settings object
2935
+ * @param {node|object} TR element from which to read data or existing row
2936
+ * object from which to re-read the data from the cells
2937
+ * @param {int} [colIdx] Optional column index
2938
+ * @param {array|object} [d] Data source object. If `colIdx` is given then this
2939
+ * parameter should also be given and will be used to write the data into.
2940
+ * Only the column in question will be written
2941
+ * @returns {object} Object with two parameters: `data` the data read, in
2942
+ * document order, and `cells` and array of nodes (they can be useful to the
2943
+ * caller, so rather than needing a second traversal to get them, just return
2944
+ * them from here).
2945
+ * @memberof DataTable#oApi
2946
+ */
2947
+ function _fnGetRowElements( settings, row, colIdx, d )
2948
+ {
2949
+ var
2950
+ tds = [],
2951
+ td = row.firstChild,
2952
+ name, col, o, i=0, contents,
2953
+ columns = settings.aoColumns,
2954
+ objectRead = settings._rowReadObject;
2955
+
2956
+ // Allow the data object to be passed in, or construct
2957
+ d = d !== undefined ?
2958
+ d :
2959
+ objectRead ?
2960
+ {} :
2961
+ [];
2962
+
2963
+ var attr = function ( str, td ) {
2964
+ if ( typeof str === 'string' ) {
2965
+ var idx = str.indexOf('@');
2966
+
2967
+ if ( idx !== -1 ) {
2968
+ var attr = str.substring( idx+1 );
2969
+ var setter = _fnSetObjectDataFn( str );
2970
+ setter( d, td.getAttribute( attr ) );
2971
+ }
2972
+ }
2973
+ };
2974
+
2975
+ // Read data from a cell and store into the data object
2976
+ var cellProcess = function ( cell ) {
2977
+ if ( colIdx === undefined || colIdx === i ) {
2978
+ col = columns[i];
2979
+ contents = $.trim(cell.innerHTML);
2980
+
2981
+ if ( col && col._bAttrSrc ) {
2982
+ var setter = _fnSetObjectDataFn( col.mData._ );
2983
+ setter( d, contents );
2984
+
2985
+ attr( col.mData.sort, cell );
2986
+ attr( col.mData.type, cell );
2987
+ attr( col.mData.filter, cell );
2988
+ }
2989
+ else {
2990
+ // Depending on the `data` option for the columns the data can
2991
+ // be read to either an object or an array.
2992
+ if ( objectRead ) {
2993
+ if ( ! col._setter ) {
2994
+ // Cache the setter function
2995
+ col._setter = _fnSetObjectDataFn( col.mData );
2996
+ }
2997
+ col._setter( d, contents );
2998
+ }
2999
+ else {
3000
+ d[i] = contents;
3001
+ }
3002
+ }
3003
+ }
3004
+
3005
+ i++;
3006
+ };
3007
+
3008
+ if ( td ) {
3009
+ // `tr` element was passed in
3010
+ while ( td ) {
3011
+ name = td.nodeName.toUpperCase();
3012
+
3013
+ if ( name == "TD" || name == "TH" ) {
3014
+ cellProcess( td );
3015
+ tds.push( td );
3016
+ }
3017
+
3018
+ td = td.nextSibling;
3019
+ }
3020
+ }
3021
+ else {
3022
+ // Existing row object passed in
3023
+ tds = row.anCells;
3024
+
3025
+ for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
3026
+ cellProcess( tds[j] );
3027
+ }
3028
+ }
3029
+
3030
+ // Read the ID from the DOM if present
3031
+ var rowNode = row.firstChild ? row : row.nTr;
3032
+
3033
+ if ( rowNode ) {
3034
+ var id = rowNode.getAttribute( 'id' );
3035
+
3036
+ if ( id ) {
3037
+ _fnSetObjectDataFn( settings.rowId )( d, id );
3038
+ }
3039
+ }
3040
+
3041
+ return {
3042
+ data: d,
3043
+ cells: tds
3044
+ };
3045
+ }
3046
+ /**
3047
+ * Create a new TR element (and it's TD children) for a row
3048
+ * @param {object} oSettings dataTables settings object
3049
+ * @param {int} iRow Row to consider
3050
+ * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
3051
+ * DataTables will create a row automatically
3052
+ * @param {array} [anTds] Array of TD|TH elements for the row - must be given
3053
+ * if nTr is.
3054
+ * @memberof DataTable#oApi
3055
+ */
3056
+ function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
3057
+ {
3058
+ var
3059
+ row = oSettings.aoData[iRow],
3060
+ rowData = row._aData,
3061
+ cells = [],
3062
+ nTr, nTd, oCol,
3063
+ i, iLen;
3064
+
3065
+ if ( row.nTr === null )
3066
+ {
3067
+ nTr = nTrIn || document.createElement('tr');
3068
+
3069
+ row.nTr = nTr;
3070
+ row.anCells = cells;
3071
+
3072
+ /* Use a private property on the node to allow reserve mapping from the node
3073
+ * to the aoData array for fast look up
3074
+ */
3075
+ nTr._DT_RowIndex = iRow;
3076
+
3077
+ /* Special parameters can be given by the data source to be used on the row */
3078
+ _fnRowAttributes( oSettings, row );
3079
+
3080
+ /* Process each column */
3081
+ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
3082
+ {
3083
+ oCol = oSettings.aoColumns[i];
3084
+
3085
+ nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType );
3086
+ nTd._DT_CellIndex = {
3087
+ row: iRow,
3088
+ column: i
3089
+ };
3090
+
3091
+ cells.push( nTd );
3092
+
3093
+ // Need to create the HTML if new, or if a rendering function is defined
3094
+ if ( (!nTrIn || oCol.mRender || oCol.mData !== i) &&
3095
+ (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
3096
+ ) {
3097
+ nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
3098
+ }
3099
+
3100
+ /* Add user defined class */
3101
+ if ( oCol.sClass )
3102
+ {
3103
+ nTd.className += ' '+oCol.sClass;
3104
+ }
3105
+
3106
+ // Visibility - add or remove as required
3107
+ if ( oCol.bVisible && ! nTrIn )
3108
+ {
3109
+ nTr.appendChild( nTd );
3110
+ }
3111
+ else if ( ! oCol.bVisible && nTrIn )
3112
+ {
3113
+ nTd.parentNode.removeChild( nTd );
3114
+ }
3115
+
3116
+ if ( oCol.fnCreatedCell )
3117
+ {
3118
+ oCol.fnCreatedCell.call( oSettings.oInstance,
3119
+ nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
3120
+ );
3121
+ }
3122
+ }
3123
+
3124
+ _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow] );
3125
+ }
3126
+
3127
+ // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved
3128
+ // and deployed
3129
+ row.nTr.setAttribute( 'role', 'row' );
3130
+ }
3131
+
3132
+
3133
+ /**
3134
+ * Add attributes to a row based on the special `DT_*` parameters in a data
3135
+ * source object.
3136
+ * @param {object} settings DataTables settings object
3137
+ * @param {object} DataTables row object for the row to be modified
3138
+ * @memberof DataTable#oApi
3139
+ */
3140
+ function _fnRowAttributes( settings, row )
3141
+ {
3142
+ var tr = row.nTr;
3143
+ var data = row._aData;
3144
+
3145
+ if ( tr ) {
3146
+ var id = settings.rowIdFn( data );
3147
+
3148
+ if ( id ) {
3149
+ tr.id = id;
3150
+ }
3151
+
3152
+ if ( data.DT_RowClass ) {
3153
+ // Remove any classes added by DT_RowClass before
3154
+ var a = data.DT_RowClass.split(' ');
3155
+ row.__rowc = row.__rowc ?
3156
+ _unique( row.__rowc.concat( a ) ) :
3157
+ a;
3158
+
3159
+ $(tr)
3160
+ .removeClass( row.__rowc.join(' ') )
3161
+ .addClass( data.DT_RowClass );
3162
+ }
3163
+
3164
+ if ( data.DT_RowAttr ) {
3165
+ $(tr).attr( data.DT_RowAttr );
3166
+ }
3167
+
3168
+ if ( data.DT_RowData ) {
3169
+ $(tr).data( data.DT_RowData );
3170
+ }
3171
+ }
3172
+ }
3173
+
3174
+
3175
+ /**
3176
+ * Create the HTML header for the table
3177
+ * @param {object} oSettings dataTables settings object
3178
+ * @memberof DataTable#oApi
3179
+ */
3180
+ function _fnBuildHead( oSettings )
3181
+ {
3182
+ var i, ien, cell, row, column;
3183
+ var thead = oSettings.nTHead;
3184
+ var tfoot = oSettings.nTFoot;
3185
+ var createHeader = $('th, td', thead).length === 0;
3186
+ var classes = oSettings.oClasses;
3187
+ var columns = oSettings.aoColumns;
3188
+
3189
+ if ( createHeader ) {
3190
+ row = $('<tr/>').appendTo( thead );
3191
+ }
3192
+
3193
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
3194
+ column = columns[i];
3195
+ cell = $( column.nTh ).addClass( column.sClass );
3196
+
3197
+ if ( createHeader ) {
3198
+ cell.appendTo( row );
3199
+ }
3200
+
3201
+ // 1.11 move into sorting
3202
+ if ( oSettings.oFeatures.bSort ) {
3203
+ cell.addClass( column.sSortingClass );
3204
+
3205
+ if ( column.bSortable !== false ) {
3206
+ cell
3207
+ .attr( 'tabindex', oSettings.iTabIndex )
3208
+ .attr( 'aria-controls', oSettings.sTableId );
3209
+
3210
+ _fnSortAttachListener( oSettings, column.nTh, i );
3211
+ }
3212
+ }
3213
+
3214
+ if ( column.sTitle != cell[0].innerHTML ) {
3215
+ cell.html( column.sTitle );
3216
+ }
3217
+
3218
+ _fnRenderer( oSettings, 'header' )(
3219
+ oSettings, cell, column, classes
3220
+ );
3221
+ }
3222
+
3223
+ if ( createHeader ) {
3224
+ _fnDetectHeader( oSettings.aoHeader, thead );
3225
+ }
3226
+
3227
+ /* ARIA role for the rows */
3228
+ $(thead).find('>tr').attr('role', 'row');
3229
+
3230
+ /* Deal with the footer - add classes if required */
3231
+ $(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH );
3232
+ $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH );
3233
+
3234
+ // Cache the footer cells. Note that we only take the cells from the first
3235
+ // row in the footer. If there is more than one row the user wants to
3236
+ // interact with, they need to use the table().foot() method. Note also this
3237
+ // allows cells to be used for multiple columns using colspan
3238
+ if ( tfoot !== null ) {
3239
+ var cells = oSettings.aoFooter[0];
3240
+
3241
+ for ( i=0, ien=cells.length ; i<ien ; i++ ) {
3242
+ column = columns[i];
3243
+ column.nTf = cells[i].cell;
3244
+
3245
+ if ( column.sClass ) {
3246
+ $(column.nTf).addClass( column.sClass );
3247
+ }
3248
+ }
3249
+ }
3250
+ }
3251
+
3252
+
3253
+ /**
3254
+ * Draw the header (or footer) element based on the column visibility states. The
3255
+ * methodology here is to use the layout array from _fnDetectHeader, modified for
3256
+ * the instantaneous column visibility, to construct the new layout. The grid is
3257
+ * traversed over cell at a time in a rows x columns grid fashion, although each
3258
+ * cell insert can cover multiple elements in the grid - which is tracks using the
3259
+ * aApplied array. Cell inserts in the grid will only occur where there isn't
3260
+ * already a cell in that position.
3261
+ * @param {object} oSettings dataTables settings object
3262
+ * @param array {objects} aoSource Layout array from _fnDetectHeader
3263
+ * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
3264
+ * @memberof DataTable#oApi
3265
+ */
3266
+ function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
3267
+ {
3268
+ var i, iLen, j, jLen, k, kLen, n, nLocalTr;
3269
+ var aoLocal = [];
3270
+ var aApplied = [];
3271
+ var iColumns = oSettings.aoColumns.length;
3272
+ var iRowspan, iColspan;
3273
+
3274
+ if ( ! aoSource )
3275
+ {
3276
+ return;
3277
+ }
3278
+
3279
+ if ( bIncludeHidden === undefined )
3280
+ {
3281
+ bIncludeHidden = false;
3282
+ }
3283
+
3284
+ /* Make a copy of the master layout array, but without the visible columns in it */
3285
+ for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
3286
+ {
3287
+ aoLocal[i] = aoSource[i].slice();
3288
+ aoLocal[i].nTr = aoSource[i].nTr;
3289
+
3290
+ /* Remove any columns which are currently hidden */
3291
+ for ( j=iColumns-1 ; j>=0 ; j-- )
3292
+ {
3293
+ if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
3294
+ {
3295
+ aoLocal[i].splice( j, 1 );
3296
+ }
3297
+ }
3298
+
3299
+ /* Prep the applied array - it needs an element for each row */
3300
+ aApplied.push( [] );
3301
+ }
3302
+
3303
+ for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
3304
+ {
3305
+ nLocalTr = aoLocal[i].nTr;
3306
+
3307
+ /* All cells are going to be replaced, so empty out the row */
3308
+ if ( nLocalTr )
3309
+ {
3310
+ while( (n = nLocalTr.firstChild) )
3311
+ {
3312
+ nLocalTr.removeChild( n );
3313
+ }
3314
+ }
3315
+
3316
+ for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
3317
+ {
3318
+ iRowspan = 1;
3319
+ iColspan = 1;
3320
+
3321
+ /* Check to see if there is already a cell (row/colspan) covering our target
3322
+ * insert point. If there is, then there is nothing to do.
3323
+ */
3324
+ if ( aApplied[i][j] === undefined )
3325
+ {
3326
+ nLocalTr.appendChild( aoLocal[i][j].cell );
3327
+ aApplied[i][j] = 1;
3328
+
3329
+ /* Expand the cell to cover as many rows as needed */
3330
+ while ( aoLocal[i+iRowspan] !== undefined &&
3331
+ aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
3332
+ {
3333
+ aApplied[i+iRowspan][j] = 1;
3334
+ iRowspan++;
3335
+ }
3336
+
3337
+ /* Expand the cell to cover as many columns as needed */
3338
+ while ( aoLocal[i][j+iColspan] !== undefined &&
3339
+ aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
3340
+ {
3341
+ /* Must update the applied array over the rows for the columns */
3342
+ for ( k=0 ; k<iRowspan ; k++ )
3343
+ {
3344
+ aApplied[i+k][j+iColspan] = 1;
3345
+ }
3346
+ iColspan++;
3347
+ }
3348
+
3349
+ /* Do the actual expansion in the DOM */
3350
+ $(aoLocal[i][j].cell)
3351
+ .attr('rowspan', iRowspan)
3352
+ .attr('colspan', iColspan);
3353
+ }
3354
+ }
3355
+ }
3356
+ }
3357
+
3358
+
3359
+ /**
3360
+ * Insert the required TR nodes into the table for display
3361
+ * @param {object} oSettings dataTables settings object
3362
+ * @memberof DataTable#oApi
3363
+ */
3364
+ function _fnDraw( oSettings )
3365
+ {
3366
+ /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
3367
+ var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
3368
+ if ( $.inArray( false, aPreDraw ) !== -1 )
3369
+ {
3370
+ _fnProcessingDisplay( oSettings, false );
3371
+ return;
3372
+ }
3373
+
3374
+ var i, iLen, n;
3375
+ var anRows = [];
3376
+ var iRowCount = 0;
3377
+ var asStripeClasses = oSettings.asStripeClasses;
3378
+ var iStripes = asStripeClasses.length;
3379
+ var iOpenRows = oSettings.aoOpenRows.length;
3380
+ var oLang = oSettings.oLanguage;
3381
+ var iInitDisplayStart = oSettings.iInitDisplayStart;
3382
+ var bServerSide = _fnDataSource( oSettings ) == 'ssp';
3383
+ var aiDisplay = oSettings.aiDisplay;
3384
+
3385
+ oSettings.bDrawing = true;
3386
+
3387
+ /* Check and see if we have an initial draw position from state saving */
3388
+ if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
3389
+ {
3390
+ oSettings._iDisplayStart = bServerSide ?
3391
+ iInitDisplayStart :
3392
+ iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
3393
+ 0 :
3394
+ iInitDisplayStart;
3395
+
3396
+ oSettings.iInitDisplayStart = -1;
3397
+ }
3398
+
3399
+ var iDisplayStart = oSettings._iDisplayStart;
3400
+ var iDisplayEnd = oSettings.fnDisplayEnd();
3401
+
3402
+ /* Server-side processing draw intercept */
3403
+ if ( oSettings.bDeferLoading )
3404
+ {
3405
+ oSettings.bDeferLoading = false;
3406
+ oSettings.iDraw++;
3407
+ _fnProcessingDisplay( oSettings, false );
3408
+ }
3409
+ else if ( !bServerSide )
3410
+ {
3411
+ oSettings.iDraw++;
3412
+ }
3413
+ else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
3414
+ {
3415
+ return;
3416
+ }
3417
+
3418
+ if ( aiDisplay.length !== 0 )
3419
+ {
3420
+ var iStart = bServerSide ? 0 : iDisplayStart;
3421
+ var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
3422
+
3423
+ for ( var j=iStart ; j<iEnd ; j++ )
3424
+ {
3425
+ var iDataIndex = aiDisplay[j];
3426
+ var aoData = oSettings.aoData[ iDataIndex ];
3427
+ if ( aoData.nTr === null )
3428
+ {
3429
+ _fnCreateTr( oSettings, iDataIndex );
3430
+ }
3431
+
3432
+ var nRow = aoData.nTr;
3433
+
3434
+ /* Remove the old striping classes and then add the new one */
3435
+ if ( iStripes !== 0 )
3436
+ {
3437
+ var sStripe = asStripeClasses[ iRowCount % iStripes ];
3438
+ if ( aoData._sRowStripe != sStripe )
3439
+ {
3440
+ $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
3441
+ aoData._sRowStripe = sStripe;
3442
+ }
3443
+ }
3444
+
3445
+ // Row callback functions - might want to manipulate the row
3446
+ // iRowCount and j are not currently documented. Are they at all
3447
+ // useful?
3448
+ _fnCallbackFire( oSettings, 'aoRowCallback', null,
3449
+ [nRow, aoData._aData, iRowCount, j] );
3450
+
3451
+ anRows.push( nRow );
3452
+ iRowCount++;
3453
+ }
3454
+ }
3455
+ else
3456
+ {
3457
+ /* Table is empty - create a row with an empty message in it */
3458
+ var sZero = oLang.sZeroRecords;
3459
+ if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
3460
+ {
3461
+ sZero = oLang.sLoadingRecords;
3462
+ }
3463
+ else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
3464
+ {
3465
+ sZero = oLang.sEmptyTable;
3466
+ }
3467
+
3468
+ anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
3469
+ .append( $('<td />', {
3470
+ 'valign': 'top',
3471
+ 'colSpan': _fnVisbleColumns( oSettings ),
3472
+ 'class': oSettings.oClasses.sRowEmpty
3473
+ } ).html( sZero ) )[0];
3474
+ }
3475
+
3476
+ /* Header and footer callbacks */
3477
+ _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
3478
+ _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3479
+
3480
+ _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
3481
+ _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3482
+
3483
+ var body = $(oSettings.nTBody);
3484
+
3485
+ body.children().detach();
3486
+ body.append( $(anRows) );
3487
+
3488
+ /* Call all required callback functions for the end of a draw */
3489
+ _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
3490
+
3491
+ /* Draw is complete, sorting and filtering must be as well */
3492
+ oSettings.bSorted = false;
3493
+ oSettings.bFiltered = false;
3494
+ oSettings.bDrawing = false;
3495
+ }
3496
+
3497
+
3498
+ /**
3499
+ * Redraw the table - taking account of the various features which are enabled
3500
+ * @param {object} oSettings dataTables settings object
3501
+ * @param {boolean} [holdPosition] Keep the current paging position. By default
3502
+ * the paging is reset to the first page
3503
+ * @memberof DataTable#oApi
3504
+ */
3505
+ function _fnReDraw( settings, holdPosition )
3506
+ {
3507
+ var
3508
+ features = settings.oFeatures,
3509
+ sort = features.bSort,
3510
+ filter = features.bFilter;
3511
+
3512
+ if ( sort ) {
3513
+ _fnSort( settings );
3514
+ }
3515
+
3516
+ if ( filter ) {
3517
+ _fnFilterComplete( settings, settings.oPreviousSearch );
3518
+ }
3519
+ else {
3520
+ // No filtering, so we want to just use the display master
3521
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
3522
+ }
3523
+
3524
+ if ( holdPosition !== true ) {
3525
+ settings._iDisplayStart = 0;
3526
+ }
3527
+
3528
+ // Let any modules know about the draw hold position state (used by
3529
+ // scrolling internally)
3530
+ settings._drawHold = holdPosition;
3531
+
3532
+ _fnDraw( settings );
3533
+
3534
+ settings._drawHold = false;
3535
+ }
3536
+
3537
+
3538
+ /**
3539
+ * Add the options to the page HTML for the table
3540
+ * @param {object} oSettings dataTables settings object
3541
+ * @memberof DataTable#oApi
3542
+ */
3543
+ function _fnAddOptionsHtml ( oSettings )
3544
+ {
3545
+ var classes = oSettings.oClasses;
3546
+ var table = $(oSettings.nTable);
3547
+ var holding = $('<div/>').insertBefore( table ); // Holding element for speed
3548
+ var features = oSettings.oFeatures;
3549
+
3550
+ // All DataTables are wrapped in a div
3551
+ var insert = $('<div/>', {
3552
+ id: oSettings.sTableId+'_wrapper',
3553
+ 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
3554
+ } );
3555
+
3556
+ oSettings.nHolding = holding[0];
3557
+ oSettings.nTableWrapper = insert[0];
3558
+ oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
3559
+
3560
+ /* Loop over the user set positioning and place the elements as needed */
3561
+ var aDom = oSettings.sDom.split('');
3562
+ var featureNode, cOption, nNewNode, cNext, sAttr, j;
3563
+ for ( var i=0 ; i<aDom.length ; i++ )
3564
+ {
3565
+ featureNode = null;
3566
+ cOption = aDom[i];
3567
+
3568
+ if ( cOption == '<' )
3569
+ {
3570
+ /* New container div */
3571
+ nNewNode = $('<div/>')[0];
3572
+
3573
+ /* Check to see if we should append an id and/or a class name to the container */
3574
+ cNext = aDom[i+1];
3575
+ if ( cNext == "'" || cNext == '"' )
3576
+ {
3577
+ sAttr = "";
3578
+ j = 2;
3579
+ while ( aDom[i+j] != cNext )
3580
+ {
3581
+ sAttr += aDom[i+j];
3582
+ j++;
3583
+ }
3584
+
3585
+ /* Replace jQuery UI constants @todo depreciated */
3586
+ if ( sAttr == "H" )
3587
+ {
3588
+ sAttr = classes.sJUIHeader;
3589
+ }
3590
+ else if ( sAttr == "F" )
3591
+ {
3592
+ sAttr = classes.sJUIFooter;
3593
+ }
3594
+
3595
+ /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
3596
+ * breaks the string into parts and applies them as needed
3597
+ */
3598
+ if ( sAttr.indexOf('.') != -1 )
3599
+ {
3600
+ var aSplit = sAttr.split('.');
3601
+ nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
3602
+ nNewNode.className = aSplit[1];
3603
+ }
3604
+ else if ( sAttr.charAt(0) == "#" )
3605
+ {
3606
+ nNewNode.id = sAttr.substr(1, sAttr.length-1);
3607
+ }
3608
+ else
3609
+ {
3610
+ nNewNode.className = sAttr;
3611
+ }
3612
+
3613
+ i += j; /* Move along the position array */
3614
+ }
3615
+
3616
+ insert.append( nNewNode );
3617
+ insert = $(nNewNode);
3618
+ }
3619
+ else if ( cOption == '>' )
3620
+ {
3621
+ /* End container div */
3622
+ insert = insert.parent();
3623
+ }
3624
+ // @todo Move options into their own plugins?
3625
+ else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
3626
+ {
3627
+ /* Length */
3628
+ featureNode = _fnFeatureHtmlLength( oSettings );
3629
+ }
3630
+ else if ( cOption == 'f' && features.bFilter )
3631
+ {
3632
+ /* Filter */
3633
+ featureNode = _fnFeatureHtmlFilter( oSettings );
3634
+ }
3635
+ else if ( cOption == 'r' && features.bProcessing )
3636
+ {
3637
+ /* pRocessing */
3638
+ featureNode = _fnFeatureHtmlProcessing( oSettings );
3639
+ }
3640
+ else if ( cOption == 't' )
3641
+ {
3642
+ /* Table */
3643
+ featureNode = _fnFeatureHtmlTable( oSettings );
3644
+ }
3645
+ else if ( cOption == 'i' && features.bInfo )
3646
+ {
3647
+ /* Info */
3648
+ featureNode = _fnFeatureHtmlInfo( oSettings );
3649
+ }
3650
+ else if ( cOption == 'p' && features.bPaginate )
3651
+ {
3652
+ /* Pagination */
3653
+ featureNode = _fnFeatureHtmlPaginate( oSettings );
3654
+ }
3655
+ else if ( DataTable.ext.feature.length !== 0 )
3656
+ {
3657
+ /* Plug-in features */
3658
+ var aoFeatures = DataTable.ext.feature;
3659
+ for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
3660
+ {
3661
+ if ( cOption == aoFeatures[k].cFeature )
3662
+ {
3663
+ featureNode = aoFeatures[k].fnInit( oSettings );
3664
+ break;
3665
+ }
3666
+ }
3667
+ }
3668
+
3669
+ /* Add to the 2D features array */
3670
+ if ( featureNode )
3671
+ {
3672
+ var aanFeatures = oSettings.aanFeatures;
3673
+
3674
+ if ( ! aanFeatures[cOption] )
3675
+ {
3676
+ aanFeatures[cOption] = [];
3677
+ }
3678
+
3679
+ aanFeatures[cOption].push( featureNode );
3680
+ insert.append( featureNode );
3681
+ }
3682
+ }
3683
+
3684
+ /* Built our DOM structure - replace the holding div with what we want */
3685
+ holding.replaceWith( insert );
3686
+ oSettings.nHolding = null;
3687
+ }
3688
+
3689
+
3690
+ /**
3691
+ * Use the DOM source to create up an array of header cells. The idea here is to
3692
+ * create a layout grid (array) of rows x columns, which contains a reference
3693
+ * to the cell that that point in the grid (regardless of col/rowspan), such that
3694
+ * any column / row could be removed and the new grid constructed
3695
+ * @param array {object} aLayout Array to store the calculated layout in
3696
+ * @param {node} nThead The header/footer element for the table
3697
+ * @memberof DataTable#oApi
3698
+ */
3699
+ function _fnDetectHeader ( aLayout, nThead )
3700
+ {
3701
+ var nTrs = $(nThead).children('tr');
3702
+ var nTr, nCell;
3703
+ var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
3704
+ var bUnique;
3705
+ var fnShiftCol = function ( a, i, j ) {
3706
+ var k = a[i];
3707
+ while ( k[j] ) {
3708
+ j++;
3709
+ }
3710
+ return j;
3711
+ };
3712
+
3713
+ aLayout.splice( 0, aLayout.length );
3714
+
3715
+ /* We know how many rows there are in the layout - so prep it */
3716
+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3717
+ {
3718
+ aLayout.push( [] );
3719
+ }
3720
+
3721
+ /* Calculate a layout array */
3722
+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3723
+ {
3724
+ nTr = nTrs[i];
3725
+ iColumn = 0;
3726
+
3727
+ /* For every cell in the row... */
3728
+ nCell = nTr.firstChild;
3729
+ while ( nCell ) {
3730
+ if ( nCell.nodeName.toUpperCase() == "TD" ||
3731
+ nCell.nodeName.toUpperCase() == "TH" )
3732
+ {
3733
+ /* Get the col and rowspan attributes from the DOM and sanitise them */
3734
+ iColspan = nCell.getAttribute('colspan') * 1;
3735
+ iRowspan = nCell.getAttribute('rowspan') * 1;
3736
+ iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
3737
+ iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
3738
+
3739
+ /* There might be colspan cells already in this row, so shift our target
3740
+ * accordingly
3741
+ */
3742
+ iColShifted = fnShiftCol( aLayout, i, iColumn );
3743
+
3744
+ /* Cache calculation for unique columns */
3745
+ bUnique = iColspan === 1 ? true : false;
3746
+
3747
+ /* If there is col / rowspan, copy the information into the layout grid */
3748
+ for ( l=0 ; l<iColspan ; l++ )
3749
+ {
3750
+ for ( k=0 ; k<iRowspan ; k++ )
3751
+ {
3752
+ aLayout[i+k][iColShifted+l] = {
3753
+ "cell": nCell,
3754
+ "unique": bUnique
3755
+ };
3756
+ aLayout[i+k].nTr = nTr;
3757
+ }
3758
+ }
3759
+ }
3760
+ nCell = nCell.nextSibling;
3761
+ }
3762
+ }
3763
+ }
3764
+
3765
+
3766
+ /**
3767
+ * Get an array of unique th elements, one for each column
3768
+ * @param {object} oSettings dataTables settings object
3769
+ * @param {node} nHeader automatically detect the layout from this node - optional
3770
+ * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
3771
+ * @returns array {node} aReturn list of unique th's
3772
+ * @memberof DataTable#oApi
3773
+ */
3774
+ function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
3775
+ {
3776
+ var aReturn = [];
3777
+ if ( !aLayout )
3778
+ {
3779
+ aLayout = oSettings.aoHeader;
3780
+ if ( nHeader )
3781
+ {
3782
+ aLayout = [];
3783
+ _fnDetectHeader( aLayout, nHeader );
3784
+ }
3785
+ }
3786
+
3787
+ for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
3788
+ {
3789
+ for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
3790
+ {
3791
+ if ( aLayout[i][j].unique &&
3792
+ (!aReturn[j] || !oSettings.bSortCellsTop) )
3793
+ {
3794
+ aReturn[j] = aLayout[i][j].cell;
3795
+ }
3796
+ }
3797
+ }
3798
+
3799
+ return aReturn;
3800
+ }
3801
+
3802
+ /**
3803
+ * Create an Ajax call based on the table's settings, taking into account that
3804
+ * parameters can have multiple forms, and backwards compatibility.
3805
+ *
3806
+ * @param {object} oSettings dataTables settings object
3807
+ * @param {array} data Data to send to the server, required by
3808
+ * DataTables - may be augmented by developer callbacks
3809
+ * @param {function} fn Callback function to run when data is obtained
3810
+ */
3811
+ function _fnBuildAjax( oSettings, data, fn )
3812
+ {
3813
+ // Compatibility with 1.9-, allow fnServerData and event to manipulate
3814
+ _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
3815
+
3816
+ // Convert to object based for 1.10+ if using the old array scheme which can
3817
+ // come from server-side processing or serverParams
3818
+ if ( data && $.isArray(data) ) {
3819
+ var tmp = {};
3820
+ var rbracket = /(.*?)\[\]$/;
3821
+
3822
+ $.each( data, function (key, val) {
3823
+ var match = val.name.match(rbracket);
3824
+
3825
+ if ( match ) {
3826
+ // Support for arrays
3827
+ var name = match[0];
3828
+
3829
+ if ( ! tmp[ name ] ) {
3830
+ tmp[ name ] = [];
3831
+ }
3832
+ tmp[ name ].push( val.value );
3833
+ }
3834
+ else {
3835
+ tmp[val.name] = val.value;
3836
+ }
3837
+ } );
3838
+ data = tmp;
3839
+ }
3840
+
3841
+ var ajaxData;
3842
+ var ajax = oSettings.ajax;
3843
+ var instance = oSettings.oInstance;
3844
+ var callback = function ( json ) {
3845
+ _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
3846
+ fn( json );
3847
+ };
3848
+
3849
+ if ( $.isPlainObject( ajax ) && ajax.data )
3850
+ {
3851
+ ajaxData = ajax.data;
3852
+
3853
+ var newData = $.isFunction( ajaxData ) ?
3854
+ ajaxData( data, oSettings ) : // fn can manipulate data or return
3855
+ ajaxData; // an object object or array to merge
3856
+
3857
+ // If the function returned something, use that alone
3858
+ data = $.isFunction( ajaxData ) && newData ?
3859
+ newData :
3860
+ $.extend( true, data, newData );
3861
+
3862
+ // Remove the data property as we've resolved it already and don't want
3863
+ // jQuery to do it again (it is restored at the end of the function)
3864
+ delete ajax.data;
3865
+ }
3866
+
3867
+ var baseAjax = {
3868
+ "data": data,
3869
+ "success": function (json) {
3870
+ var error = json.error || json.sError;
3871
+ if ( error ) {
3872
+ _fnLog( oSettings, 0, error );
3873
+ }
3874
+
3875
+ oSettings.json = json;
3876
+ callback( json );
3877
+ },
3878
+ "dataType": "json",
3879
+ "cache": false,
3880
+ "type": oSettings.sServerMethod,
3881
+ "error": function (xhr, error, thrown) {
3882
+ var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
3883
+
3884
+ if ( $.inArray( true, ret ) === -1 ) {
3885
+ if ( error == "parsererror" ) {
3886
+ _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
3887
+ }
3888
+ else if ( xhr.readyState === 4 ) {
3889
+ _fnLog( oSettings, 0, 'Ajax error', 7 );
3890
+ }
3891
+ }
3892
+
3893
+ _fnProcessingDisplay( oSettings, false );
3894
+ }
3895
+ };
3896
+
3897
+ // Store the data submitted for the API
3898
+ oSettings.oAjaxData = data;
3899
+
3900
+ // Allow plug-ins and external processes to modify the data
3901
+ _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
3902
+
3903
+ if ( oSettings.fnServerData )
3904
+ {
3905
+ // DataTables 1.9- compatibility
3906
+ oSettings.fnServerData.call( instance,
3907
+ oSettings.sAjaxSource,
3908
+ $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
3909
+ return { name: key, value: val };
3910
+ } ),
3911
+ callback,
3912
+ oSettings
3913
+ );
3914
+ }
3915
+ else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
3916
+ {
3917
+ // DataTables 1.9- compatibility
3918
+ oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
3919
+ url: ajax || oSettings.sAjaxSource
3920
+ } ) );
3921
+ }
3922
+ else if ( $.isFunction( ajax ) )
3923
+ {
3924
+ // Is a function - let the caller define what needs to be done
3925
+ oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
3926
+ }
3927
+ else
3928
+ {
3929
+ // Object to extend the base settings
3930
+ oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
3931
+
3932
+ // Restore for next time around
3933
+ ajax.data = ajaxData;
3934
+ }
3935
+ }
3936
+
3937
+
3938
+ /**
3939
+ * Update the table using an Ajax call
3940
+ * @param {object} settings dataTables settings object
3941
+ * @returns {boolean} Block the table drawing or not
3942
+ * @memberof DataTable#oApi
3943
+ */
3944
+ function _fnAjaxUpdate( settings )
3945
+ {
3946
+ if ( settings.bAjaxDataGet ) {
3947
+ settings.iDraw++;
3948
+ _fnProcessingDisplay( settings, true );
3949
+
3950
+ _fnBuildAjax(
3951
+ settings,
3952
+ _fnAjaxParameters( settings ),
3953
+ function(json) {
3954
+ _fnAjaxUpdateDraw( settings, json );
3955
+ }
3956
+ );
3957
+
3958
+ return false;
3959
+ }
3960
+ return true;
3961
+ }
3962
+
3963
+
3964
+ /**
3965
+ * Build up the parameters in an object needed for a server-side processing
3966
+ * request. Note that this is basically done twice, is different ways - a modern
3967
+ * method which is used by default in DataTables 1.10 which uses objects and
3968
+ * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
3969
+ * the sAjaxSource option is used in the initialisation, or the legacyAjax
3970
+ * option is set.
3971
+ * @param {object} oSettings dataTables settings object
3972
+ * @returns {bool} block the table drawing or not
3973
+ * @memberof DataTable#oApi
3974
+ */
3975
+ function _fnAjaxParameters( settings )
3976
+ {
3977
+ var
3978
+ columns = settings.aoColumns,
3979
+ columnCount = columns.length,
3980
+ features = settings.oFeatures,
3981
+ preSearch = settings.oPreviousSearch,
3982
+ preColSearch = settings.aoPreSearchCols,
3983
+ i, data = [], dataProp, column, columnSearch,
3984
+ sort = _fnSortFlatten( settings ),
3985
+ displayStart = settings._iDisplayStart,
3986
+ displayLength = features.bPaginate !== false ?
3987
+ settings._iDisplayLength :
3988
+ -1;
3989
+
3990
+ var param = function ( name, value ) {
3991
+ data.push( { 'name': name, 'value': value } );
3992
+ };
3993
+
3994
+ // DataTables 1.9- compatible method
3995
+ param( 'sEcho', settings.iDraw );
3996
+ param( 'iColumns', columnCount );
3997
+ param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
3998
+ param( 'iDisplayStart', displayStart );
3999
+ param( 'iDisplayLength', displayLength );
4000
+
4001
+ // DataTables 1.10+ method
4002
+ var d = {
4003
+ draw: settings.iDraw,
4004
+ columns: [],
4005
+ order: [],
4006
+ start: displayStart,
4007
+ length: displayLength,
4008
+ search: {
4009
+ value: preSearch.sSearch,
4010
+ regex: preSearch.bRegex
4011
+ }
4012
+ };
4013
+
4014
+ for ( i=0 ; i<columnCount ; i++ ) {
4015
+ column = columns[i];
4016
+ columnSearch = preColSearch[i];
4017
+ dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
4018
+
4019
+ d.columns.push( {
4020
+ data: dataProp,
4021
+ name: column.sName,
4022
+ searchable: column.bSearchable,
4023
+ orderable: column.bSortable,
4024
+ search: {
4025
+ value: columnSearch.sSearch,
4026
+ regex: columnSearch.bRegex
4027
+ }
4028
+ } );
4029
+
4030
+ param( "mDataProp_"+i, dataProp );
4031
+
4032
+ if ( features.bFilter ) {
4033
+ param( 'sSearch_'+i, columnSearch.sSearch );
4034
+ param( 'bRegex_'+i, columnSearch.bRegex );
4035
+ param( 'bSearchable_'+i, column.bSearchable );
4036
+ }
4037
+
4038
+ if ( features.bSort ) {
4039
+ param( 'bSortable_'+i, column.bSortable );
4040
+ }
4041
+ }
4042
+
4043
+ if ( features.bFilter ) {
4044
+ param( 'sSearch', preSearch.sSearch );
4045
+ param( 'bRegex', preSearch.bRegex );
4046
+ }
4047
+
4048
+ if ( features.bSort ) {
4049
+ $.each( sort, function ( i, val ) {
4050
+ d.order.push( { column: val.col, dir: val.dir } );
4051
+
4052
+ param( 'iSortCol_'+i, val.col );
4053
+ param( 'sSortDir_'+i, val.dir );
4054
+ } );
4055
+
4056
+ param( 'iSortingCols', sort.length );
4057
+ }
4058
+
4059
+ // If the legacy.ajax parameter is null, then we automatically decide which
4060
+ // form to use, based on sAjaxSource
4061
+ var legacy = DataTable.ext.legacy.ajax;
4062
+ if ( legacy === null ) {
4063
+ return settings.sAjaxSource ? data : d;
4064
+ }
4065
+
4066
+ // Otherwise, if legacy has been specified then we use that to decide on the
4067
+ // form
4068
+ return legacy ? data : d;
4069
+ }
4070
+
4071
+
4072
+ /**
4073
+ * Data the data from the server (nuking the old) and redraw the table
4074
+ * @param {object} oSettings dataTables settings object
4075
+ * @param {object} json json data return from the server.
4076
+ * @param {string} json.sEcho Tracking flag for DataTables to match requests
4077
+ * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
4078
+ * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
4079
+ * @param {array} json.aaData The data to display on this page
4080
+ * @param {string} [json.sColumns] Column ordering (sName, comma separated)
4081
+ * @memberof DataTable#oApi
4082
+ */
4083
+ function _fnAjaxUpdateDraw ( settings, json )
4084
+ {
4085
+ // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
4086
+ // Support both
4087
+ var compat = function ( old, modern ) {
4088
+ return json[old] !== undefined ? json[old] : json[modern];
4089
+ };
4090
+
4091
+ var data = _fnAjaxDataSrc( settings, json );
4092
+ var draw = compat( 'sEcho', 'draw' );
4093
+ var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
4094
+ var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
4095
+
4096
+ if ( draw ) {
4097
+ // Protect against out of sequence returns
4098
+ if ( draw*1 < settings.iDraw ) {
4099
+ return;
4100
+ }
4101
+ settings.iDraw = draw * 1;
4102
+ }
4103
+
4104
+ _fnClearTable( settings );
4105
+ settings._iRecordsTotal = parseInt(recordsTotal, 10);
4106
+ settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
4107
+
4108
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
4109
+ _fnAddData( settings, data[i] );
4110
+ }
4111
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
4112
+
4113
+ settings.bAjaxDataGet = false;
4114
+ _fnDraw( settings );
4115
+
4116
+ if ( ! settings._bInitComplete ) {
4117
+ _fnInitComplete( settings, json );
4118
+ }
4119
+
4120
+ settings.bAjaxDataGet = true;
4121
+ _fnProcessingDisplay( settings, false );
4122
+ }
4123
+
4124
+
4125
+ /**
4126
+ * Get the data from the JSON data source to use for drawing a table. Using
4127
+ * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
4128
+ * source object, or from a processing function.
4129
+ * @param {object} oSettings dataTables settings object
4130
+ * @param {object} json Data source object / array from the server
4131
+ * @return {array} Array of data to use
4132
+ */
4133
+ function _fnAjaxDataSrc ( oSettings, json )
4134
+ {
4135
+ var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
4136
+ oSettings.ajax.dataSrc :
4137
+ oSettings.sAjaxDataProp; // Compatibility with 1.9-.
4138
+
4139
+ // Compatibility with 1.9-. In order to read from aaData, check if the
4140
+ // default has been changed, if not, check for aaData
4141
+ if ( dataSrc === 'data' ) {
4142
+ return json.aaData || json[dataSrc];
4143
+ }
4144
+
4145
+ return dataSrc !== "" ?
4146
+ _fnGetObjectDataFn( dataSrc )( json ) :
4147
+ json;
4148
+ }
4149
+
4150
+ /**
4151
+ * Generate the node required for filtering text
4152
+ * @returns {node} Filter control element
4153
+ * @param {object} oSettings dataTables settings object
4154
+ * @memberof DataTable#oApi
4155
+ */
4156
+ function _fnFeatureHtmlFilter ( settings )
4157
+ {
4158
+ var classes = settings.oClasses;
4159
+ var tableId = settings.sTableId;
4160
+ var language = settings.oLanguage;
4161
+ var previousSearch = settings.oPreviousSearch;
4162
+ var features = settings.aanFeatures;
4163
+ var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
4164
+
4165
+ var str = language.sSearch;
4166
+ str = str.match(/_INPUT_/) ?
4167
+ str.replace('_INPUT_', input) :
4168
+ str+input;
4169
+
4170
+ var filter = $('<div/>', {
4171
+ 'id': ! features.f ? tableId+'_filter' : null,
4172
+ 'class': classes.sFilter
4173
+ } )
4174
+ .append( $('<label/>' ).append( str ) );
4175
+
4176
+ var searchFn = function() {
4177
+ /* Update all other filter input elements for the new display */
4178
+ var n = features.f;
4179
+ var val = !this.value ? "" : this.value; // mental IE8 fix :-(
4180
+
4181
+ /* Now do the filter */
4182
+ if ( val != previousSearch.sSearch ) {
4183
+ _fnFilterComplete( settings, {
4184
+ "sSearch": val,
4185
+ "bRegex": previousSearch.bRegex,
4186
+ "bSmart": previousSearch.bSmart ,
4187
+ "bCaseInsensitive": previousSearch.bCaseInsensitive
4188
+ } );
4189
+
4190
+ // Need to redraw, without resorting
4191
+ settings._iDisplayStart = 0;
4192
+ _fnDraw( settings );
4193
+ }
4194
+ };
4195
+
4196
+ var searchDelay = settings.searchDelay !== null ?
4197
+ settings.searchDelay :
4198
+ _fnDataSource( settings ) === 'ssp' ?
4199
+ 400 :
4200
+ 0;
4201
+
4202
+ var jqFilter = $('input', filter)
4203
+ .val( previousSearch.sSearch )
4204
+ .attr( 'placeholder', language.sSearchPlaceholder )
4205
+ .bind(
4206
+ 'keyup.DT search.DT input.DT paste.DT cut.DT',
4207
+ searchDelay ?
4208
+ _fnThrottle( searchFn, searchDelay ) :
4209
+ searchFn
4210
+ )
4211
+ .bind( 'keypress.DT', function(e) {
4212
+ /* Prevent form submission */
4213
+ if ( e.keyCode == 13 ) {
4214
+ return false;
4215
+ }
4216
+ } )
4217
+ .attr('aria-controls', tableId);
4218
+
4219
+ // Update the input elements whenever the table is filtered
4220
+ $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
4221
+ if ( settings === s ) {
4222
+ // IE9 throws an 'unknown error' if document.activeElement is used
4223
+ // inside an iframe or frame...
4224
+ try {
4225
+ if ( jqFilter[0] !== document.activeElement ) {
4226
+ jqFilter.val( previousSearch.sSearch );
4227
+ }
4228
+ }
4229
+ catch ( e ) {}
4230
+ }
4231
+ } );
4232
+
4233
+ return filter[0];
4234
+ }
4235
+
4236
+
4237
+ /**
4238
+ * Filter the table using both the global filter and column based filtering
4239
+ * @param {object} oSettings dataTables settings object
4240
+ * @param {object} oSearch search information
4241
+ * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
4242
+ * @memberof DataTable#oApi
4243
+ */
4244
+ function _fnFilterComplete ( oSettings, oInput, iForce )
4245
+ {
4246
+ var oPrevSearch = oSettings.oPreviousSearch;
4247
+ var aoPrevSearch = oSettings.aoPreSearchCols;
4248
+ var fnSaveFilter = function ( oFilter ) {
4249
+ /* Save the filtering values */
4250
+ oPrevSearch.sSearch = oFilter.sSearch;
4251
+ oPrevSearch.bRegex = oFilter.bRegex;
4252
+ oPrevSearch.bSmart = oFilter.bSmart;
4253
+ oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
4254
+ };
4255
+ var fnRegex = function ( o ) {
4256
+ // Backwards compatibility with the bEscapeRegex option
4257
+ return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
4258
+ };
4259
+
4260
+ // Resolve any column types that are unknown due to addition or invalidation
4261
+ // @todo As per sort - can this be moved into an event handler?
4262
+ _fnColumnTypes( oSettings );
4263
+
4264
+ /* In server-side processing all filtering is done by the server, so no point hanging around here */
4265
+ if ( _fnDataSource( oSettings ) != 'ssp' )
4266
+ {
4267
+ /* Global filter */
4268
+ _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
4269
+ fnSaveFilter( oInput );
4270
+
4271
+ /* Now do the individual column filter */
4272
+ for ( var i=0 ; i<aoPrevSearch.length ; i++ )
4273
+ {
4274
+ _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
4275
+ aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
4276
+ }
4277
+
4278
+ /* Custom filtering */
4279
+ _fnFilterCustom( oSettings );
4280
+ }
4281
+ else
4282
+ {
4283
+ fnSaveFilter( oInput );
4284
+ }
4285
+
4286
+ /* Tell the draw function we have been filtering */
4287
+ oSettings.bFiltered = true;
4288
+ _fnCallbackFire( oSettings, null, 'search', [oSettings] );
4289
+ }
4290
+
4291
+
4292
+ /**
4293
+ * Apply custom filtering functions
4294
+ * @param {object} oSettings dataTables settings object
4295
+ * @memberof DataTable#oApi
4296
+ */
4297
+ function _fnFilterCustom( settings )
4298
+ {
4299
+ var filters = DataTable.ext.search;
4300
+ var displayRows = settings.aiDisplay;
4301
+ var row, rowIdx;
4302
+
4303
+ for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
4304
+ var rows = [];
4305
+
4306
+ // Loop over each row and see if it should be included
4307
+ for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
4308
+ rowIdx = displayRows[ j ];
4309
+ row = settings.aoData[ rowIdx ];
4310
+
4311
+ if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
4312
+ rows.push( rowIdx );
4313
+ }
4314
+ }
4315
+
4316
+ // So the array reference doesn't break set the results into the
4317
+ // existing array
4318
+ displayRows.length = 0;
4319
+ $.merge( displayRows, rows );
4320
+ }
4321
+ }
4322
+
4323
+
4324
+ /**
4325
+ * Filter the table on a per-column basis
4326
+ * @param {object} oSettings dataTables settings object
4327
+ * @param {string} sInput string to filter on
4328
+ * @param {int} iColumn column to filter
4329
+ * @param {bool} bRegex treat search string as a regular expression or not
4330
+ * @param {bool} bSmart use smart filtering or not
4331
+ * @param {bool} bCaseInsensitive Do case insenstive matching or not
4332
+ * @memberof DataTable#oApi
4333
+ */
4334
+ function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
4335
+ {
4336
+ if ( searchStr === '' ) {
4337
+ return;
4338
+ }
4339
+
4340
+ var data;
4341
+ var display = settings.aiDisplay;
4342
+ var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
4343
+
4344
+ for ( var i=display.length-1 ; i>=0 ; i-- ) {
4345
+ data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
4346
+
4347
+ if ( ! rpSearch.test( data ) ) {
4348
+ display.splice( i, 1 );
4349
+ }
4350
+ }
4351
+ }
4352
+
4353
+
4354
+ /**
4355
+ * Filter the data table based on user input and draw the table
4356
+ * @param {object} settings dataTables settings object
4357
+ * @param {string} input string to filter on
4358
+ * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
4359
+ * @param {bool} regex treat as a regular expression or not
4360
+ * @param {bool} smart perform smart filtering or not
4361
+ * @param {bool} caseInsensitive Do case insenstive matching or not
4362
+ * @memberof DataTable#oApi
4363
+ */
4364
+ function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
4365
+ {
4366
+ var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
4367
+ var prevSearch = settings.oPreviousSearch.sSearch;
4368
+ var displayMaster = settings.aiDisplayMaster;
4369
+ var display, invalidated, i;
4370
+
4371
+ // Need to take account of custom filtering functions - always filter
4372
+ if ( DataTable.ext.search.length !== 0 ) {
4373
+ force = true;
4374
+ }
4375
+
4376
+ // Check if any of the rows were invalidated
4377
+ invalidated = _fnFilterData( settings );
4378
+
4379
+ // If the input is blank - we just want the full data set
4380
+ if ( input.length <= 0 ) {
4381
+ settings.aiDisplay = displayMaster.slice();
4382
+ }
4383
+ else {
4384
+ // New search - start from the master array
4385
+ if ( invalidated ||
4386
+ force ||
4387
+ prevSearch.length > input.length ||
4388
+ input.indexOf(prevSearch) !== 0 ||
4389
+ settings.bSorted // On resort, the display master needs to be
4390
+ // re-filtered since indexes will have changed
4391
+ ) {
4392
+ settings.aiDisplay = displayMaster.slice();
4393
+ }
4394
+
4395
+ // Search the display array
4396
+ display = settings.aiDisplay;
4397
+
4398
+ for ( i=display.length-1 ; i>=0 ; i-- ) {
4399
+ if ( ! rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
4400
+ display.splice( i, 1 );
4401
+ }
4402
+ }
4403
+ }
4404
+ }
4405
+
4406
+
4407
+ /**
4408
+ * Build a regular expression object suitable for searching a table
4409
+ * @param {string} sSearch string to search for
4410
+ * @param {bool} bRegex treat as a regular expression or not
4411
+ * @param {bool} bSmart perform smart filtering or not
4412
+ * @param {bool} bCaseInsensitive Do case insensitive matching or not
4413
+ * @returns {RegExp} constructed object
4414
+ * @memberof DataTable#oApi
4415
+ */
4416
+ function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
4417
+ {
4418
+ search = regex ?
4419
+ search :
4420
+ _fnEscapeRegex( search );
4421
+
4422
+ if ( smart ) {
4423
+ /* For smart filtering we want to allow the search to work regardless of
4424
+ * word order. We also want double quoted text to be preserved, so word
4425
+ * order is important - a la google. So this is what we want to
4426
+ * generate:
4427
+ *
4428
+ * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
4429
+ */
4430
+ var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
4431
+ if ( word.charAt(0) === '"' ) {
4432
+ var m = word.match( /^"(.*)"$/ );
4433
+ word = m ? m[1] : word;
4434
+ }
4435
+
4436
+ return word.replace('"', '');
4437
+ } );
4438
+
4439
+ search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
4440
+ }
4441
+
4442
+ return new RegExp( search, caseInsensitive ? 'i' : '' );
4443
+ }
4444
+
4445
+
4446
+ /**
4447
+ * Escape a string such that it can be used in a regular expression
4448
+ * @param {string} sVal string to escape
4449
+ * @returns {string} escaped string
4450
+ * @memberof DataTable#oApi
4451
+ */
4452
+ var _fnEscapeRegex = DataTable.util.escapeRegex;
4453
+
4454
+ var __filter_div = $('<div>')[0];
4455
+ var __filter_div_textContent = __filter_div.textContent !== undefined;
4456
+
4457
+ // Update the filtering data for each row if needed (by invalidation or first run)
4458
+ function _fnFilterData ( settings )
4459
+ {
4460
+ var columns = settings.aoColumns;
4461
+ var column;
4462
+ var i, j, ien, jen, filterData, cellData, row;
4463
+ var fomatters = DataTable.ext.type.search;
4464
+ var wasInvalidated = false;
4465
+
4466
+ for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4467
+ row = settings.aoData[i];
4468
+
4469
+ if ( ! row._aFilterData ) {
4470
+ filterData = [];
4471
+
4472
+ for ( j=0, jen=columns.length ; j<jen ; j++ ) {
4473
+ column = columns[j];
4474
+
4475
+ if ( column.bSearchable ) {
4476
+ cellData = _fnGetCellData( settings, i, j, 'filter' );
4477
+
4478
+ if ( fomatters[ column.sType ] ) {
4479
+ cellData = fomatters[ column.sType ]( cellData );
4480
+ }
4481
+
4482
+ // Search in DataTables 1.10 is string based. In 1.11 this
4483
+ // should be altered to also allow strict type checking.
4484
+ if ( cellData === null ) {
4485
+ cellData = '';
4486
+ }
4487
+
4488
+ if ( typeof cellData !== 'string' && cellData.toString ) {
4489
+ cellData = cellData.toString();
4490
+ }
4491
+ }
4492
+ else {
4493
+ cellData = '';
4494
+ }
4495
+
4496
+ // If it looks like there is an HTML entity in the string,
4497
+ // attempt to decode it so sorting works as expected. Note that
4498
+ // we could use a single line of jQuery to do this, but the DOM
4499
+ // method used here is much faster http://jsperf.com/html-decode
4500
+ if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
4501
+ __filter_div.innerHTML = cellData;
4502
+ cellData = __filter_div_textContent ?
4503
+ __filter_div.textContent :
4504
+ __filter_div.innerText;
4505
+ }
4506
+
4507
+ if ( cellData.replace ) {
4508
+ cellData = cellData.replace(/[\r\n]/g, '');
4509
+ }
4510
+
4511
+ filterData.push( cellData );
4512
+ }
4513
+
4514
+ row._aFilterData = filterData;
4515
+ row._sFilterRow = filterData.join(' ');
4516
+ wasInvalidated = true;
4517
+ }
4518
+ }
4519
+
4520
+ return wasInvalidated;
4521
+ }
4522
+
4523
+
4524
+ /**
4525
+ * Convert from the internal Hungarian notation to camelCase for external
4526
+ * interaction
4527
+ * @param {object} obj Object to convert
4528
+ * @returns {object} Inverted object
4529
+ * @memberof DataTable#oApi
4530
+ */
4531
+ function _fnSearchToCamel ( obj )
4532
+ {
4533
+ return {
4534
+ search: obj.sSearch,
4535
+ smart: obj.bSmart,
4536
+ regex: obj.bRegex,
4537
+ caseInsensitive: obj.bCaseInsensitive
4538
+ };
4539
+ }
4540
+
4541
+
4542
+
4543
+ /**
4544
+ * Convert from camelCase notation to the internal Hungarian. We could use the
4545
+ * Hungarian convert function here, but this is cleaner
4546
+ * @param {object} obj Object to convert
4547
+ * @returns {object} Inverted object
4548
+ * @memberof DataTable#oApi
4549
+ */
4550
+ function _fnSearchToHung ( obj )
4551
+ {
4552
+ return {
4553
+ sSearch: obj.search,
4554
+ bSmart: obj.smart,
4555
+ bRegex: obj.regex,
4556
+ bCaseInsensitive: obj.caseInsensitive
4557
+ };
4558
+ }
4559
+
4560
+ /**
4561
+ * Generate the node required for the info display
4562
+ * @param {object} oSettings dataTables settings object
4563
+ * @returns {node} Information element
4564
+ * @memberof DataTable#oApi
4565
+ */
4566
+ function _fnFeatureHtmlInfo ( settings )
4567
+ {
4568
+ var
4569
+ tid = settings.sTableId,
4570
+ nodes = settings.aanFeatures.i,
4571
+ n = $('<div/>', {
4572
+ 'class': settings.oClasses.sInfo,
4573
+ 'id': ! nodes ? tid+'_info' : null
4574
+ } );
4575
+
4576
+ if ( ! nodes ) {
4577
+ // Update display on each draw
4578
+ settings.aoDrawCallback.push( {
4579
+ "fn": _fnUpdateInfo,
4580
+ "sName": "information"
4581
+ } );
4582
+
4583
+ n
4584
+ .attr( 'role', 'status' )
4585
+ .attr( 'aria-live', 'polite' );
4586
+
4587
+ // Table is described by our info div
4588
+ $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
4589
+ }
4590
+
4591
+ return n[0];
4592
+ }
4593
+
4594
+
4595
+ /**
4596
+ * Update the information elements in the display
4597
+ * @param {object} settings dataTables settings object
4598
+ * @memberof DataTable#oApi
4599
+ */
4600
+ function _fnUpdateInfo ( settings )
4601
+ {
4602
+ /* Show information about the table */
4603
+ var nodes = settings.aanFeatures.i;
4604
+ if ( nodes.length === 0 ) {
4605
+ return;
4606
+ }
4607
+
4608
+ var
4609
+ lang = settings.oLanguage,
4610
+ start = settings._iDisplayStart+1,
4611
+ end = settings.fnDisplayEnd(),
4612
+ max = settings.fnRecordsTotal(),
4613
+ total = settings.fnRecordsDisplay(),
4614
+ out = total ?
4615
+ lang.sInfo :
4616
+ lang.sInfoEmpty;
4617
+
4618
+ if ( total !== max ) {
4619
+ /* Record set after filtering */
4620
+ out += ' ' + lang.sInfoFiltered;
4621
+ }
4622
+
4623
+ // Convert the macros
4624
+ out += lang.sInfoPostFix;
4625
+ out = _fnInfoMacros( settings, out );
4626
+
4627
+ var callback = lang.fnInfoCallback;
4628
+ if ( callback !== null ) {
4629
+ out = callback.call( settings.oInstance,
4630
+ settings, start, end, max, total, out
4631
+ );
4632
+ }
4633
+
4634
+ $(nodes).html( out );
4635
+ }
4636
+
4637
+
4638
+ function _fnInfoMacros ( settings, str )
4639
+ {
4640
+ // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
4641
+ // internally
4642
+ var
4643
+ formatter = settings.fnFormatNumber,
4644
+ start = settings._iDisplayStart+1,
4645
+ len = settings._iDisplayLength,
4646
+ vis = settings.fnRecordsDisplay(),
4647
+ all = len === -1;
4648
+
4649
+ return str.
4650
+ replace(/_START_/g, formatter.call( settings, start ) ).
4651
+ replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
4652
+ replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
4653
+ replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
4654
+ replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
4655
+ replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
4656
+ }
4657
+
4658
+
4659
+
4660
+ /**
4661
+ * Draw the table for the first time, adding all required features
4662
+ * @param {object} settings dataTables settings object
4663
+ * @memberof DataTable#oApi
4664
+ */
4665
+ function _fnInitialise ( settings )
4666
+ {
4667
+ var i, iLen, iAjaxStart=settings.iInitDisplayStart;
4668
+ var columns = settings.aoColumns, column;
4669
+ var features = settings.oFeatures;
4670
+ var deferLoading = settings.bDeferLoading; // value modified by the draw
4671
+
4672
+ /* Ensure that the table data is fully initialised */
4673
+ if ( ! settings.bInitialised ) {
4674
+ setTimeout( function(){ _fnInitialise( settings ); }, 200 );
4675
+ return;
4676
+ }
4677
+
4678
+ /* Show the display HTML options */
4679
+ _fnAddOptionsHtml( settings );
4680
+
4681
+ /* Build and draw the header / footer for the table */
4682
+ _fnBuildHead( settings );
4683
+ _fnDrawHead( settings, settings.aoHeader );
4684
+ _fnDrawHead( settings, settings.aoFooter );
4685
+
4686
+ /* Okay to show that something is going on now */
4687
+ _fnProcessingDisplay( settings, true );
4688
+
4689
+ /* Calculate sizes for columns */
4690
+ if ( features.bAutoWidth ) {
4691
+ _fnCalculateColumnWidths( settings );
4692
+ }
4693
+
4694
+ for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
4695
+ column = columns[i];
4696
+
4697
+ if ( column.sWidth ) {
4698
+ column.nTh.style.width = _fnStringToCss( column.sWidth );
4699
+ }
4700
+ }
4701
+
4702
+ _fnCallbackFire( settings, null, 'preInit', [settings] );
4703
+
4704
+ // If there is default sorting required - let's do it. The sort function
4705
+ // will do the drawing for us. Otherwise we draw the table regardless of the
4706
+ // Ajax source - this allows the table to look initialised for Ajax sourcing
4707
+ // data (show 'loading' message possibly)
4708
+ _fnReDraw( settings );
4709
+
4710
+ // Server-side processing init complete is done by _fnAjaxUpdateDraw
4711
+ var dataSrc = _fnDataSource( settings );
4712
+ if ( dataSrc != 'ssp' || deferLoading ) {
4713
+ // if there is an ajax source load the data
4714
+ if ( dataSrc == 'ajax' ) {
4715
+ _fnBuildAjax( settings, [], function(json) {
4716
+ var aData = _fnAjaxDataSrc( settings, json );
4717
+
4718
+ // Got the data - add it to the table
4719
+ for ( i=0 ; i<aData.length ; i++ ) {
4720
+ _fnAddData( settings, aData[i] );
4721
+ }
4722
+
4723
+ // Reset the init display for cookie saving. We've already done
4724
+ // a filter, and therefore cleared it before. So we need to make
4725
+ // it appear 'fresh'
4726
+ settings.iInitDisplayStart = iAjaxStart;
4727
+
4728
+ _fnReDraw( settings );
4729
+
4730
+ _fnProcessingDisplay( settings, false );
4731
+ _fnInitComplete( settings, json );
4732
+ }, settings );
4733
+ }
4734
+ else {
4735
+ _fnProcessingDisplay( settings, false );
4736
+ _fnInitComplete( settings );
4737
+ }
4738
+ }
4739
+ }
4740
+
4741
+
4742
+ /**
4743
+ * Draw the table for the first time, adding all required features
4744
+ * @param {object} oSettings dataTables settings object
4745
+ * @param {object} [json] JSON from the server that completed the table, if using Ajax source
4746
+ * with client-side processing (optional)
4747
+ * @memberof DataTable#oApi
4748
+ */
4749
+ function _fnInitComplete ( settings, json )
4750
+ {
4751
+ settings._bInitComplete = true;
4752
+
4753
+ // When data was added after the initialisation (data or Ajax) we need to
4754
+ // calculate the column sizing
4755
+ if ( json || settings.oInit.aaData ) {
4756
+ _fnAdjustColumnSizing( settings );
4757
+ }
4758
+
4759
+ _fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
4760
+ _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
4761
+ }
4762
+
4763
+
4764
+ function _fnLengthChange ( settings, val )
4765
+ {
4766
+ var len = parseInt( val, 10 );
4767
+ settings._iDisplayLength = len;
4768
+
4769
+ _fnLengthOverflow( settings );
4770
+
4771
+ // Fire length change event
4772
+ _fnCallbackFire( settings, null, 'length', [settings, len] );
4773
+ }
4774
+
4775
+
4776
+ /**
4777
+ * Generate the node required for user display length changing
4778
+ * @param {object} settings dataTables settings object
4779
+ * @returns {node} Display length feature node
4780
+ * @memberof DataTable#oApi
4781
+ */
4782
+ function _fnFeatureHtmlLength ( settings )
4783
+ {
4784
+ var
4785
+ classes = settings.oClasses,
4786
+ tableId = settings.sTableId,
4787
+ menu = settings.aLengthMenu,
4788
+ d2 = $.isArray( menu[0] ),
4789
+ lengths = d2 ? menu[0] : menu,
4790
+ language = d2 ? menu[1] : menu;
4791
+
4792
+ var select = $('<select/>', {
4793
+ 'name': tableId+'_length',
4794
+ 'aria-controls': tableId,
4795
+ 'class': classes.sLengthSelect
4796
+ } );
4797
+
4798
+ for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
4799
+ select[0][ i ] = new Option( language[i], lengths[i] );
4800
+ }
4801
+
4802
+ var div = $('<div><label/></div>').addClass( classes.sLength );
4803
+ if ( ! settings.aanFeatures.l ) {
4804
+ div[0].id = tableId+'_length';
4805
+ }
4806
+
4807
+ div.children().append(
4808
+ settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
4809
+ );
4810
+
4811
+ // Can't use `select` variable as user might provide their own and the
4812
+ // reference is broken by the use of outerHTML
4813
+ $('select', div)
4814
+ .val( settings._iDisplayLength )
4815
+ .bind( 'change.DT', function(e) {
4816
+ _fnLengthChange( settings, $(this).val() );
4817
+ _fnDraw( settings );
4818
+ } );
4819
+
4820
+ // Update node value whenever anything changes the table's length
4821
+ $(settings.nTable).bind( 'length.dt.DT', function (e, s, len) {
4822
+ if ( settings === s ) {
4823
+ $('select', div).val( len );
4824
+ }
4825
+ } );
4826
+
4827
+ return div[0];
4828
+ }
4829
+
4830
+
4831
+
4832
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4833
+ * Note that most of the paging logic is done in
4834
+ * DataTable.ext.pager
4835
+ */
4836
+
4837
+ /**
4838
+ * Generate the node required for default pagination
4839
+ * @param {object} oSettings dataTables settings object
4840
+ * @returns {node} Pagination feature node
4841
+ * @memberof DataTable#oApi
4842
+ */
4843
+ function _fnFeatureHtmlPaginate ( settings )
4844
+ {
4845
+ var
4846
+ type = settings.sPaginationType,
4847
+ plugin = DataTable.ext.pager[ type ],
4848
+ modern = typeof plugin === 'function',
4849
+ redraw = function( settings ) {
4850
+ _fnDraw( settings );
4851
+ },
4852
+ node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
4853
+ features = settings.aanFeatures;
4854
+
4855
+ if ( ! modern ) {
4856
+ plugin.fnInit( settings, node, redraw );
4857
+ }
4858
+
4859
+ /* Add a draw callback for the pagination on first instance, to update the paging display */
4860
+ if ( ! features.p )
4861
+ {
4862
+ node.id = settings.sTableId+'_paginate';
4863
+
4864
+ settings.aoDrawCallback.push( {
4865
+ "fn": function( settings ) {
4866
+ if ( modern ) {
4867
+ var
4868
+ start = settings._iDisplayStart,
4869
+ len = settings._iDisplayLength,
4870
+ visRecords = settings.fnRecordsDisplay(),
4871
+ all = len === -1,
4872
+ page = all ? 0 : Math.ceil( start / len ),
4873
+ pages = all ? 1 : Math.ceil( visRecords / len ),
4874
+ buttons = plugin(page, pages),
4875
+ i, ien;
4876
+
4877
+ for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
4878
+ _fnRenderer( settings, 'pageButton' )(
4879
+ settings, features.p[i], i, buttons, page, pages
4880
+ );
4881
+ }
4882
+ }
4883
+ else {
4884
+ plugin.fnUpdate( settings, redraw );
4885
+ }
4886
+ },
4887
+ "sName": "pagination"
4888
+ } );
4889
+ }
4890
+
4891
+ return node;
4892
+ }
4893
+
4894
+
4895
+ /**
4896
+ * Alter the display settings to change the page
4897
+ * @param {object} settings DataTables settings object
4898
+ * @param {string|int} action Paging action to take: "first", "previous",
4899
+ * "next" or "last" or page number to jump to (integer)
4900
+ * @param [bool] redraw Automatically draw the update or not
4901
+ * @returns {bool} true page has changed, false - no change
4902
+ * @memberof DataTable#oApi
4903
+ */
4904
+ function _fnPageChange ( settings, action, redraw )
4905
+ {
4906
+ var
4907
+ start = settings._iDisplayStart,
4908
+ len = settings._iDisplayLength,
4909
+ records = settings.fnRecordsDisplay();
4910
+
4911
+ if ( records === 0 || len === -1 )
4912
+ {
4913
+ start = 0;
4914
+ }
4915
+ else if ( typeof action === "number" )
4916
+ {
4917
+ start = action * len;
4918
+
4919
+ if ( start > records )
4920
+ {
4921
+ start = 0;
4922
+ }
4923
+ }
4924
+ else if ( action == "first" )
4925
+ {
4926
+ start = 0;
4927
+ }
4928
+ else if ( action == "previous" )
4929
+ {
4930
+ start = len >= 0 ?
4931
+ start - len :
4932
+ 0;
4933
+
4934
+ if ( start < 0 )
4935
+ {
4936
+ start = 0;
4937
+ }
4938
+ }
4939
+ else if ( action == "next" )
4940
+ {
4941
+ if ( start + len < records )
4942
+ {
4943
+ start += len;
4944
+ }
4945
+ }
4946
+ else if ( action == "last" )
4947
+ {
4948
+ start = Math.floor( (records-1) / len) * len;
4949
+ }
4950
+ else
4951
+ {
4952
+ _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
4953
+ }
4954
+
4955
+ var changed = settings._iDisplayStart !== start;
4956
+ settings._iDisplayStart = start;
4957
+
4958
+ if ( changed ) {
4959
+ _fnCallbackFire( settings, null, 'page', [settings] );
4960
+
4961
+ if ( redraw ) {
4962
+ _fnDraw( settings );
4963
+ }
4964
+ }
4965
+
4966
+ return changed;
4967
+ }
4968
+
4969
+
4970
+
4971
+ /**
4972
+ * Generate the node required for the processing node
4973
+ * @param {object} settings dataTables settings object
4974
+ * @returns {node} Processing element
4975
+ * @memberof DataTable#oApi
4976
+ */
4977
+ function _fnFeatureHtmlProcessing ( settings )
4978
+ {
4979
+ return $('<div/>', {
4980
+ 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
4981
+ 'class': settings.oClasses.sProcessing
4982
+ } )
4983
+ .html( settings.oLanguage.sProcessing )
4984
+ .insertBefore( settings.nTable )[0];
4985
+ }
4986
+
4987
+
4988
+ /**
4989
+ * Display or hide the processing indicator
4990
+ * @param {object} settings dataTables settings object
4991
+ * @param {bool} show Show the processing indicator (true) or not (false)
4992
+ * @memberof DataTable#oApi
4993
+ */
4994
+ function _fnProcessingDisplay ( settings, show )
4995
+ {
4996
+ if ( settings.oFeatures.bProcessing ) {
4997
+ $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
4998
+ }
4999
+
5000
+ _fnCallbackFire( settings, null, 'processing', [settings, show] );
5001
+ }
5002
+
5003
+ /**
5004
+ * Add any control elements for the table - specifically scrolling
5005
+ * @param {object} settings dataTables settings object
5006
+ * @returns {node} Node to add to the DOM
5007
+ * @memberof DataTable#oApi
5008
+ */
5009
+ function _fnFeatureHtmlTable ( settings )
5010
+ {
5011
+ var table = $(settings.nTable);
5012
+
5013
+ // Add the ARIA grid role to the table
5014
+ table.attr( 'role', 'grid' );
5015
+
5016
+ // Scrolling from here on in
5017
+ var scroll = settings.oScroll;
5018
+
5019
+ if ( scroll.sX === '' && scroll.sY === '' ) {
5020
+ return settings.nTable;
5021
+ }
5022
+
5023
+ var scrollX = scroll.sX;
5024
+ var scrollY = scroll.sY;
5025
+ var classes = settings.oClasses;
5026
+ var caption = table.children('caption');
5027
+ var captionSide = caption.length ? caption[0]._captionSide : null;
5028
+ var headerClone = $( table[0].cloneNode(false) );
5029
+ var footerClone = $( table[0].cloneNode(false) );
5030
+ var footer = table.children('tfoot');
5031
+ var _div = '<div/>';
5032
+ var size = function ( s ) {
5033
+ return !s ? null : _fnStringToCss( s );
5034
+ };
5035
+
5036
+ if ( ! footer.length ) {
5037
+ footer = null;
5038
+ }
5039
+
5040
+ /*
5041
+ * The HTML structure that we want to generate in this function is:
5042
+ * div - scroller
5043
+ * div - scroll head
5044
+ * div - scroll head inner
5045
+ * table - scroll head table
5046
+ * thead - thead
5047
+ * div - scroll body
5048
+ * table - table (master table)
5049
+ * thead - thead clone for sizing
5050
+ * tbody - tbody
5051
+ * div - scroll foot
5052
+ * div - scroll foot inner
5053
+ * table - scroll foot table
5054
+ * tfoot - tfoot
5055
+ */
5056
+ var scroller = $( _div, { 'class': classes.sScrollWrapper } )
5057
+ .append(
5058
+ $(_div, { 'class': classes.sScrollHead } )
5059
+ .css( {
5060
+ overflow: 'hidden',
5061
+ position: 'relative',
5062
+ border: 0,
5063
+ width: scrollX ? size(scrollX) : '100%'
5064
+ } )
5065
+ .append(
5066
+ $(_div, { 'class': classes.sScrollHeadInner } )
5067
+ .css( {
5068
+ 'box-sizing': 'content-box',
5069
+ width: scroll.sXInner || '100%'
5070
+ } )
5071
+ .append(
5072
+ headerClone
5073
+ .removeAttr('id')
5074
+ .css( 'margin-left', 0 )
5075
+ .append( captionSide === 'top' ? caption : null )
5076
+ .append(
5077
+ table.children('thead')
5078
+ )
5079
+ )
5080
+ )
5081
+ )
5082
+ .append(
5083
+ $(_div, { 'class': classes.sScrollBody } )
5084
+ .css( {
5085
+ position: 'relative',
5086
+ overflow: 'auto',
5087
+ width: size( scrollX )
5088
+ } )
5089
+ .append( table )
5090
+ );
5091
+
5092
+ if ( footer ) {
5093
+ scroller.append(
5094
+ $(_div, { 'class': classes.sScrollFoot } )
5095
+ .css( {
5096
+ overflow: 'hidden',
5097
+ border: 0,
5098
+ width: scrollX ? size(scrollX) : '100%'
5099
+ } )
5100
+ .append(
5101
+ $(_div, { 'class': classes.sScrollFootInner } )
5102
+ .append(
5103
+ footerClone
5104
+ .removeAttr('id')
5105
+ .css( 'margin-left', 0 )
5106
+ .append( captionSide === 'bottom' ? caption : null )
5107
+ .append(
5108
+ table.children('tfoot')
5109
+ )
5110
+ )
5111
+ )
5112
+ );
5113
+ }
5114
+
5115
+ var children = scroller.children();
5116
+ var scrollHead = children[0];
5117
+ var scrollBody = children[1];
5118
+ var scrollFoot = footer ? children[2] : null;
5119
+
5120
+ // When the body is scrolled, then we also want to scroll the headers
5121
+ if ( scrollX ) {
5122
+ $(scrollBody).on( 'scroll.DT', function (e) {
5123
+ var scrollLeft = this.scrollLeft;
5124
+
5125
+ scrollHead.scrollLeft = scrollLeft;
5126
+
5127
+ if ( footer ) {
5128
+ scrollFoot.scrollLeft = scrollLeft;
5129
+ }
5130
+ } );
5131
+ }
5132
+
5133
+ $(scrollBody).css(
5134
+ scrollY && scroll.bCollapse ? 'max-height' : 'height',
5135
+ scrollY
5136
+ );
5137
+
5138
+ settings.nScrollHead = scrollHead;
5139
+ settings.nScrollBody = scrollBody;
5140
+ settings.nScrollFoot = scrollFoot;
5141
+
5142
+ // On redraw - align columns
5143
+ settings.aoDrawCallback.push( {
5144
+ "fn": _fnScrollDraw,
5145
+ "sName": "scrolling"
5146
+ } );
5147
+
5148
+ return scroller[0];
5149
+ }
5150
+
5151
+
5152
+
5153
+ /**
5154
+ * Update the header, footer and body tables for resizing - i.e. column
5155
+ * alignment.
5156
+ *
5157
+ * Welcome to the most horrible function DataTables. The process that this
5158
+ * function follows is basically:
5159
+ * 1. Re-create the table inside the scrolling div
5160
+ * 2. Take live measurements from the DOM
5161
+ * 3. Apply the measurements to align the columns
5162
+ * 4. Clean up
5163
+ *
5164
+ * @param {object} settings dataTables settings object
5165
+ * @memberof DataTable#oApi
5166
+ */
5167
+ function _fnScrollDraw ( settings )
5168
+ {
5169
+ // Given that this is such a monster function, a lot of variables are use
5170
+ // to try and keep the minimised size as small as possible
5171
+ var
5172
+ scroll = settings.oScroll,
5173
+ scrollX = scroll.sX,
5174
+ scrollXInner = scroll.sXInner,
5175
+ scrollY = scroll.sY,
5176
+ barWidth = scroll.iBarWidth,
5177
+ divHeader = $(settings.nScrollHead),
5178
+ divHeaderStyle = divHeader[0].style,
5179
+ divHeaderInner = divHeader.children('div'),
5180
+ divHeaderInnerStyle = divHeaderInner[0].style,
5181
+ divHeaderTable = divHeaderInner.children('table'),
5182
+ divBodyEl = settings.nScrollBody,
5183
+ divBody = $(divBodyEl),
5184
+ divBodyStyle = divBodyEl.style,
5185
+ divFooter = $(settings.nScrollFoot),
5186
+ divFooterInner = divFooter.children('div'),
5187
+ divFooterTable = divFooterInner.children('table'),
5188
+ header = $(settings.nTHead),
5189
+ table = $(settings.nTable),
5190
+ tableEl = table[0],
5191
+ tableStyle = tableEl.style,
5192
+ footer = settings.nTFoot ? $(settings.nTFoot) : null,
5193
+ browser = settings.oBrowser,
5194
+ ie67 = browser.bScrollOversize,
5195
+ dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ),
5196
+ headerTrgEls, footerTrgEls,
5197
+ headerSrcEls, footerSrcEls,
5198
+ headerCopy, footerCopy,
5199
+ headerWidths=[], footerWidths=[],
5200
+ headerContent=[], footerContent=[],
5201
+ idx, correction, sanityWidth,
5202
+ zeroOut = function(nSizer) {
5203
+ var style = nSizer.style;
5204
+ style.paddingTop = "0";
5205
+ style.paddingBottom = "0";
5206
+ style.borderTopWidth = "0";
5207
+ style.borderBottomWidth = "0";
5208
+ style.height = 0;
5209
+ };
5210
+
5211
+ // If the scrollbar visibility has changed from the last draw, we need to
5212
+ // adjust the column sizes as the table width will have changed to account
5213
+ // for the scrollbar
5214
+ var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
5215
+
5216
+ if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
5217
+ settings.scrollBarVis = scrollBarVis;
5218
+ _fnAdjustColumnSizing( settings );
5219
+ return; // adjust column sizing will call this function again
5220
+ }
5221
+ else {
5222
+ settings.scrollBarVis = scrollBarVis;
5223
+ }
5224
+
5225
+ /*
5226
+ * 1. Re-create the table inside the scrolling div
5227
+ */
5228
+
5229
+ // Remove the old minimised thead and tfoot elements in the inner table
5230
+ table.children('thead, tfoot').remove();
5231
+
5232
+ if ( footer ) {
5233
+ footerCopy = footer.clone().prependTo( table );
5234
+ footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
5235
+ footerSrcEls = footerCopy.find('tr');
5236
+ }
5237
+
5238
+ // Clone the current header and footer elements and then place it into the inner table
5239
+ headerCopy = header.clone().prependTo( table );
5240
+ headerTrgEls = header.find('tr'); // original header is in its own table
5241
+ headerSrcEls = headerCopy.find('tr');
5242
+ headerCopy.find('th, td').removeAttr('tabindex');
5243
+
5244
+
5245
+ /*
5246
+ * 2. Take live measurements from the DOM - do not alter the DOM itself!
5247
+ */
5248
+
5249
+ // Remove old sizing and apply the calculated column widths
5250
+ // Get the unique column headers in the newly created (cloned) header. We want to apply the
5251
+ // calculated sizes to this header
5252
+ if ( ! scrollX )
5253
+ {
5254
+ divBodyStyle.width = '100%';
5255
+ divHeader[0].style.width = '100%';
5256
+ }
5257
+
5258
+ $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
5259
+ idx = _fnVisibleToColumnIndex( settings, i );
5260
+ el.style.width = settings.aoColumns[idx].sWidth;
5261
+ } );
5262
+
5263
+ if ( footer ) {
5264
+ _fnApplyToChildren( function(n) {
5265
+ n.style.width = "";
5266
+ }, footerSrcEls );
5267
+ }
5268
+
5269
+ // Size the table as a whole
5270
+ sanityWidth = table.outerWidth();
5271
+ if ( scrollX === "" ) {
5272
+ // No x scrolling
5273
+ tableStyle.width = "100%";
5274
+
5275
+ // IE7 will make the width of the table when 100% include the scrollbar
5276
+ // - which is shouldn't. When there is a scrollbar we need to take this
5277
+ // into account.
5278
+ if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
5279
+ divBody.css('overflow-y') == "scroll")
5280
+ ) {
5281
+ tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
5282
+ }
5283
+
5284
+ // Recalculate the sanity width
5285
+ sanityWidth = table.outerWidth();
5286
+ }
5287
+ else if ( scrollXInner !== "" ) {
5288
+ // legacy x scroll inner has been given - use it
5289
+ tableStyle.width = _fnStringToCss(scrollXInner);
5290
+
5291
+ // Recalculate the sanity width
5292
+ sanityWidth = table.outerWidth();
5293
+ }
5294
+
5295
+ // Hidden header should have zero height, so remove padding and borders. Then
5296
+ // set the width based on the real headers
5297
+
5298
+ // Apply all styles in one pass
5299
+ _fnApplyToChildren( zeroOut, headerSrcEls );
5300
+
5301
+ // Read all widths in next pass
5302
+ _fnApplyToChildren( function(nSizer) {
5303
+ headerContent.push( nSizer.innerHTML );
5304
+ headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
5305
+ }, headerSrcEls );
5306
+
5307
+ // Apply all widths in final pass
5308
+ _fnApplyToChildren( function(nToSize, i) {
5309
+ // Only apply widths to the DataTables detected header cells - this
5310
+ // prevents complex headers from having contradictory sizes applied
5311
+ if ( $.inArray( nToSize, dtHeaderCells ) !== -1 ) {
5312
+ nToSize.style.width = headerWidths[i];
5313
+ }
5314
+ }, headerTrgEls );
5315
+
5316
+ $(headerSrcEls).height(0);
5317
+
5318
+ /* Same again with the footer if we have one */
5319
+ if ( footer )
5320
+ {
5321
+ _fnApplyToChildren( zeroOut, footerSrcEls );
5322
+
5323
+ _fnApplyToChildren( function(nSizer) {
5324
+ footerContent.push( nSizer.innerHTML );
5325
+ footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
5326
+ }, footerSrcEls );
5327
+
5328
+ _fnApplyToChildren( function(nToSize, i) {
5329
+ nToSize.style.width = footerWidths[i];
5330
+ }, footerTrgEls );
5331
+
5332
+ $(footerSrcEls).height(0);
5333
+ }
5334
+
5335
+
5336
+ /*
5337
+ * 3. Apply the measurements
5338
+ */
5339
+
5340
+ // "Hide" the header and footer that we used for the sizing. We need to keep
5341
+ // the content of the cell so that the width applied to the header and body
5342
+ // both match, but we want to hide it completely. We want to also fix their
5343
+ // width to what they currently are
5344
+ _fnApplyToChildren( function(nSizer, i) {
5345
+ nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+headerContent[i]+'</div>';
5346
+ nSizer.style.width = headerWidths[i];
5347
+ }, headerSrcEls );
5348
+
5349
+ if ( footer )
5350
+ {
5351
+ _fnApplyToChildren( function(nSizer, i) {
5352
+ nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+footerContent[i]+'</div>';
5353
+ nSizer.style.width = footerWidths[i];
5354
+ }, footerSrcEls );
5355
+ }
5356
+
5357
+ // Sanity check that the table is of a sensible width. If not then we are going to get
5358
+ // misalignment - try to prevent this by not allowing the table to shrink below its min width
5359
+ if ( table.outerWidth() < sanityWidth )
5360
+ {
5361
+ // The min width depends upon if we have a vertical scrollbar visible or not */
5362
+ correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
5363
+ divBody.css('overflow-y') == "scroll")) ?
5364
+ sanityWidth+barWidth :
5365
+ sanityWidth;
5366
+
5367
+ // IE6/7 are a law unto themselves...
5368
+ if ( ie67 && (divBodyEl.scrollHeight >
5369
+ divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
5370
+ ) {
5371
+ tableStyle.width = _fnStringToCss( correction-barWidth );
5372
+ }
5373
+
5374
+ // And give the user a warning that we've stopped the table getting too small
5375
+ if ( scrollX === "" || scrollXInner !== "" ) {
5376
+ _fnLog( settings, 1, 'Possible column misalignment', 6 );
5377
+ }
5378
+ }
5379
+ else
5380
+ {
5381
+ correction = '100%';
5382
+ }
5383
+
5384
+ // Apply to the container elements
5385
+ divBodyStyle.width = _fnStringToCss( correction );
5386
+ divHeaderStyle.width = _fnStringToCss( correction );
5387
+
5388
+ if ( footer ) {
5389
+ settings.nScrollFoot.style.width = _fnStringToCss( correction );
5390
+ }
5391
+
5392
+
5393
+ /*
5394
+ * 4. Clean up
5395
+ */
5396
+ if ( ! scrollY ) {
5397
+ /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
5398
+ * the scrollbar height from the visible display, rather than adding it on. We need to
5399
+ * set the height in order to sort this. Don't want to do it in any other browsers.
5400
+ */
5401
+ if ( ie67 ) {
5402
+ divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
5403
+ }
5404
+ }
5405
+
5406
+ /* Finally set the width's of the header and footer tables */
5407
+ var iOuterWidth = table.outerWidth();
5408
+ divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
5409
+ divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
5410
+
5411
+ // Figure out if there are scrollbar present - if so then we need a the header and footer to
5412
+ // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
5413
+ var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
5414
+ var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
5415
+ divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
5416
+
5417
+ if ( footer ) {
5418
+ divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
5419
+ divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
5420
+ divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
5421
+ }
5422
+
5423
+ // Correct DOM ordering for colgroup - comes before the thead
5424
+ table.children('colgroup').insertBefore( table.children('thead') );
5425
+
5426
+ /* Adjust the position of the header in case we loose the y-scrollbar */
5427
+ divBody.scroll();
5428
+
5429
+ // If sorting or filtering has occurred, jump the scrolling back to the top
5430
+ // only if we aren't holding the position
5431
+ if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
5432
+ divBodyEl.scrollTop = 0;
5433
+ }
5434
+ }
5435
+
5436
+
5437
+
5438
+ /**
5439
+ * Apply a given function to the display child nodes of an element array (typically
5440
+ * TD children of TR rows
5441
+ * @param {function} fn Method to apply to the objects
5442
+ * @param array {nodes} an1 List of elements to look through for display children
5443
+ * @param array {nodes} an2 Another list (identical structure to the first) - optional
5444
+ * @memberof DataTable#oApi
5445
+ */
5446
+ function _fnApplyToChildren( fn, an1, an2 )
5447
+ {
5448
+ var index=0, i=0, iLen=an1.length;
5449
+ var nNode1, nNode2;
5450
+
5451
+ while ( i < iLen ) {
5452
+ nNode1 = an1[i].firstChild;
5453
+ nNode2 = an2 ? an2[i].firstChild : null;
5454
+
5455
+ while ( nNode1 ) {
5456
+ if ( nNode1.nodeType === 1 ) {
5457
+ if ( an2 ) {
5458
+ fn( nNode1, nNode2, index );
5459
+ }
5460
+ else {
5461
+ fn( nNode1, index );
5462
+ }
5463
+
5464
+ index++;
5465
+ }
5466
+
5467
+ nNode1 = nNode1.nextSibling;
5468
+ nNode2 = an2 ? nNode2.nextSibling : null;
5469
+ }
5470
+
5471
+ i++;
5472
+ }
5473
+ }
5474
+
5475
+
5476
+
5477
+ var __re_html_remove = /<.*?>/g;
5478
+
5479
+
5480
+ /**
5481
+ * Calculate the width of columns for the table
5482
+ * @param {object} oSettings dataTables settings object
5483
+ * @memberof DataTable#oApi
5484
+ */
5485
+ function _fnCalculateColumnWidths ( oSettings )
5486
+ {
5487
+ var
5488
+ table = oSettings.nTable,
5489
+ columns = oSettings.aoColumns,
5490
+ scroll = oSettings.oScroll,
5491
+ scrollY = scroll.sY,
5492
+ scrollX = scroll.sX,
5493
+ scrollXInner = scroll.sXInner,
5494
+ columnCount = columns.length,
5495
+ visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
5496
+ headerCells = $('th', oSettings.nTHead),
5497
+ tableWidthAttr = table.getAttribute('width'), // from DOM element
5498
+ tableContainer = table.parentNode,
5499
+ userInputs = false,
5500
+ i, column, columnIdx, width, outerWidth,
5501
+ browser = oSettings.oBrowser,
5502
+ ie67 = browser.bScrollOversize;
5503
+
5504
+ var styleWidth = table.style.width;
5505
+ if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
5506
+ tableWidthAttr = styleWidth;
5507
+ }
5508
+
5509
+ /* Convert any user input sizes into pixel sizes */
5510
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
5511
+ column = columns[ visibleColumns[i] ];
5512
+
5513
+ if ( column.sWidth !== null ) {
5514
+ column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
5515
+
5516
+ userInputs = true;
5517
+ }
5518
+ }
5519
+
5520
+ /* If the number of columns in the DOM equals the number that we have to
5521
+ * process in DataTables, then we can use the offsets that are created by
5522
+ * the web- browser. No custom sizes can be set in order for this to happen,
5523
+ * nor scrolling used
5524
+ */
5525
+ if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
5526
+ columnCount == _fnVisbleColumns( oSettings ) &&
5527
+ columnCount == headerCells.length
5528
+ ) {
5529
+ for ( i=0 ; i<columnCount ; i++ ) {
5530
+ var colIdx = _fnVisibleToColumnIndex( oSettings, i );
5531
+
5532
+ if ( colIdx !== null ) {
5533
+ columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
5534
+ }
5535
+ }
5536
+ }
5537
+ else
5538
+ {
5539
+ // Otherwise construct a single row, worst case, table with the widest
5540
+ // node in the data, assign any user defined widths, then insert it into
5541
+ // the DOM and allow the browser to do all the hard work of calculating
5542
+ // table widths
5543
+ var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
5544
+ .css( 'visibility', 'hidden' )
5545
+ .removeAttr( 'id' );
5546
+
5547
+ // Clean up the table body
5548
+ tmpTable.find('tbody tr').remove();
5549
+ var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
5550
+
5551
+ // Clone the table header and footer - we can't use the header / footer
5552
+ // from the cloned table, since if scrolling is active, the table's
5553
+ // real header and footer are contained in different table tags
5554
+ tmpTable.find('thead, tfoot').remove();
5555
+ tmpTable
5556
+ .append( $(oSettings.nTHead).clone() )
5557
+ .append( $(oSettings.nTFoot).clone() );
5558
+
5559
+ // Remove any assigned widths from the footer (from scrolling)
5560
+ tmpTable.find('tfoot th, tfoot td').css('width', '');
5561
+
5562
+ // Apply custom sizing to the cloned header
5563
+ headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
5564
+
5565
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
5566
+ column = columns[ visibleColumns[i] ];
5567
+
5568
+ headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
5569
+ _fnStringToCss( column.sWidthOrig ) :
5570
+ '';
5571
+
5572
+ // For scrollX we need to force the column width otherwise the
5573
+ // browser will collapse it. If this width is smaller than the
5574
+ // width the column requires, then it will have no effect
5575
+ if ( column.sWidthOrig && scrollX ) {
5576
+ $( headerCells[i] ).append( $('<div/>').css( {
5577
+ width: column.sWidthOrig,
5578
+ margin: 0,
5579
+ padding: 0,
5580
+ border: 0,
5581
+ height: 1
5582
+ } ) );
5583
+ }
5584
+ }
5585
+
5586
+ // Find the widest cell for each column and put it into the table
5587
+ if ( oSettings.aoData.length ) {
5588
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
5589
+ columnIdx = visibleColumns[i];
5590
+ column = columns[ columnIdx ];
5591
+
5592
+ $( _fnGetWidestNode( oSettings, columnIdx ) )
5593
+ .clone( false )
5594
+ .append( column.sContentPadding )
5595
+ .appendTo( tr );
5596
+ }
5597
+ }
5598
+
5599
+ // Tidy the temporary table - remove name attributes so there aren't
5600
+ // duplicated in the dom (radio elements for example)
5601
+ $('[name]', tmpTable).removeAttr('name');
5602
+
5603
+ // Table has been built, attach to the document so we can work with it.
5604
+ // A holding element is used, positioned at the top of the container
5605
+ // with minimal height, so it has no effect on if the container scrolls
5606
+ // or not. Otherwise it might trigger scrolling when it actually isn't
5607
+ // needed
5608
+ var holder = $('<div/>').css( scrollX || scrollY ?
5609
+ {
5610
+ position: 'absolute',
5611
+ top: 0,
5612
+ left: 0,
5613
+ height: 1,
5614
+ right: 0,
5615
+ overflow: 'hidden'
5616
+ } :
5617
+ {}
5618
+ )
5619
+ .append( tmpTable )
5620
+ .appendTo( tableContainer );
5621
+
5622
+ // When scrolling (X or Y) we want to set the width of the table as
5623
+ // appropriate. However, when not scrolling leave the table width as it
5624
+ // is. This results in slightly different, but I think correct behaviour
5625
+ if ( scrollX && scrollXInner ) {
5626
+ tmpTable.width( scrollXInner );
5627
+ }
5628
+ else if ( scrollX ) {
5629
+ tmpTable.css( 'width', 'auto' );
5630
+ tmpTable.removeAttr('width');
5631
+
5632
+ // If there is no width attribute or style, then allow the table to
5633
+ // collapse
5634
+ if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
5635
+ tmpTable.width( tableContainer.clientWidth );
5636
+ }
5637
+ }
5638
+ else if ( scrollY ) {
5639
+ tmpTable.width( tableContainer.clientWidth );
5640
+ }
5641
+ else if ( tableWidthAttr ) {
5642
+ tmpTable.width( tableWidthAttr );
5643
+ }
5644
+
5645
+ // Get the width of each column in the constructed table - we need to
5646
+ // know the inner width (so it can be assigned to the other table's
5647
+ // cells) and the outer width so we can calculate the full width of the
5648
+ // table. This is safe since DataTables requires a unique cell for each
5649
+ // column, but if ever a header can span multiple columns, this will
5650
+ // need to be modified.
5651
+ var total = 0;
5652
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
5653
+ var cell = $(headerCells[i]);
5654
+ var border = cell.outerWidth() - cell.width();
5655
+
5656
+ // Use getBounding... where possible (not IE8-) because it can give
5657
+ // sub-pixel accuracy, which we then want to round up!
5658
+ var bounding = browser.bBounding ?
5659
+ Math.ceil( headerCells[i].getBoundingClientRect().width ) :
5660
+ cell.outerWidth();
5661
+
5662
+ // Total is tracked to remove any sub-pixel errors as the outerWidth
5663
+ // of the table might not equal the total given here (IE!).
5664
+ total += bounding;
5665
+
5666
+ // Width for each column to use
5667
+ columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
5668
+ }
5669
+
5670
+ table.style.width = _fnStringToCss( total );
5671
+
5672
+ // Finished with the table - ditch it
5673
+ holder.remove();
5674
+ }
5675
+
5676
+ // If there is a width attr, we want to attach an event listener which
5677
+ // allows the table sizing to automatically adjust when the window is
5678
+ // resized. Use the width attr rather than CSS, since we can't know if the
5679
+ // CSS is a relative value or absolute - DOM read is always px.
5680
+ if ( tableWidthAttr ) {
5681
+ table.style.width = _fnStringToCss( tableWidthAttr );
5682
+ }
5683
+
5684
+ if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
5685
+ var bindResize = function () {
5686
+ $(window).bind('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
5687
+ _fnAdjustColumnSizing( oSettings );
5688
+ } ) );
5689
+ };
5690
+
5691
+ // IE6/7 will crash if we bind a resize event handler on page load.
5692
+ // To be removed in 1.11 which drops IE6/7 support
5693
+ if ( ie67 ) {
5694
+ setTimeout( bindResize, 1000 );
5695
+ }
5696
+ else {
5697
+ bindResize();
5698
+ }
5699
+
5700
+ oSettings._reszEvt = true;
5701
+ }
5702
+ }
5703
+
5704
+
5705
+ /**
5706
+ * Throttle the calls to a function. Arguments and context are maintained for
5707
+ * the throttled function
5708
+ * @param {function} fn Function to be called
5709
+ * @param {int} [freq=200] call frequency in mS
5710
+ * @returns {function} wrapped function
5711
+ * @memberof DataTable#oApi
5712
+ */
5713
+ var _fnThrottle = DataTable.util.throttle;
5714
+
5715
+
5716
+ /**
5717
+ * Convert a CSS unit width to pixels (e.g. 2em)
5718
+ * @param {string} width width to be converted
5719
+ * @param {node} parent parent to get the with for (required for relative widths) - optional
5720
+ * @returns {int} width in pixels
5721
+ * @memberof DataTable#oApi
5722
+ */
5723
+ function _fnConvertToWidth ( width, parent )
5724
+ {
5725
+ if ( ! width ) {
5726
+ return 0;
5727
+ }
5728
+
5729
+ var n = $('<div/>')
5730
+ .css( 'width', _fnStringToCss( width ) )
5731
+ .appendTo( parent || document.body );
5732
+
5733
+ var val = n[0].offsetWidth;
5734
+ n.remove();
5735
+
5736
+ return val;
5737
+ }
5738
+
5739
+
5740
+ /**
5741
+ * Get the widest node
5742
+ * @param {object} settings dataTables settings object
5743
+ * @param {int} colIdx column of interest
5744
+ * @returns {node} widest table node
5745
+ * @memberof DataTable#oApi
5746
+ */
5747
+ function _fnGetWidestNode( settings, colIdx )
5748
+ {
5749
+ var idx = _fnGetMaxLenString( settings, colIdx );
5750
+ if ( idx < 0 ) {
5751
+ return null;
5752
+ }
5753
+
5754
+ var data = settings.aoData[ idx ];
5755
+ return ! data.nTr ? // Might not have been created when deferred rendering
5756
+ $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
5757
+ data.anCells[ colIdx ];
5758
+ }
5759
+
5760
+
5761
+ /**
5762
+ * Get the maximum strlen for each data column
5763
+ * @param {object} settings dataTables settings object
5764
+ * @param {int} colIdx column of interest
5765
+ * @returns {string} max string length for each column
5766
+ * @memberof DataTable#oApi
5767
+ */
5768
+ function _fnGetMaxLenString( settings, colIdx )
5769
+ {
5770
+ var s, max=-1, maxIdx = -1;
5771
+
5772
+ for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
5773
+ s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
5774
+ s = s.replace( __re_html_remove, '' );
5775
+ s = s.replace( /&nbsp;/g, ' ' );
5776
+
5777
+ if ( s.length > max ) {
5778
+ max = s.length;
5779
+ maxIdx = i;
5780
+ }
5781
+ }
5782
+
5783
+ return maxIdx;
5784
+ }
5785
+
5786
+
5787
+ /**
5788
+ * Append a CSS unit (only if required) to a string
5789
+ * @param {string} value to css-ify
5790
+ * @returns {string} value with css unit
5791
+ * @memberof DataTable#oApi
5792
+ */
5793
+ function _fnStringToCss( s )
5794
+ {
5795
+ if ( s === null ) {
5796
+ return '0px';
5797
+ }
5798
+
5799
+ if ( typeof s == 'number' ) {
5800
+ return s < 0 ?
5801
+ '0px' :
5802
+ s+'px';
5803
+ }
5804
+
5805
+ // Check it has a unit character already
5806
+ return s.match(/\d$/) ?
5807
+ s+'px' :
5808
+ s;
5809
+ }
5810
+
5811
+
5812
+
5813
+ function _fnSortFlatten ( settings )
5814
+ {
5815
+ var
5816
+ i, iLen, k, kLen,
5817
+ aSort = [],
5818
+ aiOrig = [],
5819
+ aoColumns = settings.aoColumns,
5820
+ aDataSort, iCol, sType, srcCol,
5821
+ fixed = settings.aaSortingFixed,
5822
+ fixedObj = $.isPlainObject( fixed ),
5823
+ nestedSort = [],
5824
+ add = function ( a ) {
5825
+ if ( a.length && ! $.isArray( a[0] ) ) {
5826
+ // 1D array
5827
+ nestedSort.push( a );
5828
+ }
5829
+ else {
5830
+ // 2D array
5831
+ $.merge( nestedSort, a );
5832
+ }
5833
+ };
5834
+
5835
+ // Build the sort array, with pre-fix and post-fix options if they have been
5836
+ // specified
5837
+ if ( $.isArray( fixed ) ) {
5838
+ add( fixed );
5839
+ }
5840
+
5841
+ if ( fixedObj && fixed.pre ) {
5842
+ add( fixed.pre );
5843
+ }
5844
+
5845
+ add( settings.aaSorting );
5846
+
5847
+ if (fixedObj && fixed.post ) {
5848
+ add( fixed.post );
5849
+ }
5850
+
5851
+ for ( i=0 ; i<nestedSort.length ; i++ )
5852
+ {
5853
+ srcCol = nestedSort[i][0];
5854
+ aDataSort = aoColumns[ srcCol ].aDataSort;
5855
+
5856
+ for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
5857
+ {
5858
+ iCol = aDataSort[k];
5859
+ sType = aoColumns[ iCol ].sType || 'string';
5860
+
5861
+ if ( nestedSort[i]._idx === undefined ) {
5862
+ nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
5863
+ }
5864
+
5865
+ aSort.push( {
5866
+ src: srcCol,
5867
+ col: iCol,
5868
+ dir: nestedSort[i][1],
5869
+ index: nestedSort[i]._idx,
5870
+ type: sType,
5871
+ formatter: DataTable.ext.type.order[ sType+"-pre" ]
5872
+ } );
5873
+ }
5874
+ }
5875
+
5876
+ return aSort;
5877
+ }
5878
+
5879
+ /**
5880
+ * Change the order of the table
5881
+ * @param {object} oSettings dataTables settings object
5882
+ * @memberof DataTable#oApi
5883
+ * @todo This really needs split up!
5884
+ */
5885
+ function _fnSort ( oSettings )
5886
+ {
5887
+ var
5888
+ i, ien, iLen, j, jLen, k, kLen,
5889
+ sDataType, nTh,
5890
+ aiOrig = [],
5891
+ oExtSort = DataTable.ext.type.order,
5892
+ aoData = oSettings.aoData,
5893
+ aoColumns = oSettings.aoColumns,
5894
+ aDataSort, data, iCol, sType, oSort,
5895
+ formatters = 0,
5896
+ sortCol,
5897
+ displayMaster = oSettings.aiDisplayMaster,
5898
+ aSort;
5899
+
5900
+ // Resolve any column types that are unknown due to addition or invalidation
5901
+ // @todo Can this be moved into a 'data-ready' handler which is called when
5902
+ // data is going to be used in the table?
5903
+ _fnColumnTypes( oSettings );
5904
+
5905
+ aSort = _fnSortFlatten( oSettings );
5906
+
5907
+ for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
5908
+ sortCol = aSort[i];
5909
+
5910
+ // Track if we can use the fast sort algorithm
5911
+ if ( sortCol.formatter ) {
5912
+ formatters++;
5913
+ }
5914
+
5915
+ // Load the data needed for the sort, for each cell
5916
+ _fnSortData( oSettings, sortCol.col );
5917
+ }
5918
+
5919
+ /* No sorting required if server-side or no sorting array */
5920
+ if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
5921
+ {
5922
+ // Create a value - key array of the current row positions such that we can use their
5923
+ // current position during the sort, if values match, in order to perform stable sorting
5924
+ for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
5925
+ aiOrig[ displayMaster[i] ] = i;
5926
+ }
5927
+
5928
+ /* Do the sort - here we want multi-column sorting based on a given data source (column)
5929
+ * and sorting function (from oSort) in a certain direction. It's reasonably complex to
5930
+ * follow on it's own, but this is what we want (example two column sorting):
5931
+ * fnLocalSorting = function(a,b){
5932
+ * var iTest;
5933
+ * iTest = oSort['string-asc']('data11', 'data12');
5934
+ * if (iTest !== 0)
5935
+ * return iTest;
5936
+ * iTest = oSort['numeric-desc']('data21', 'data22');
5937
+ * if (iTest !== 0)
5938
+ * return iTest;
5939
+ * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
5940
+ * }
5941
+ * Basically we have a test for each sorting column, if the data in that column is equal,
5942
+ * test the next column. If all columns match, then we use a numeric sort on the row
5943
+ * positions in the original data array to provide a stable sort.
5944
+ *
5945
+ * Note - I know it seems excessive to have two sorting methods, but the first is around
5946
+ * 15% faster, so the second is only maintained for backwards compatibility with sorting
5947
+ * methods which do not have a pre-sort formatting function.
5948
+ */
5949
+ if ( formatters === aSort.length ) {
5950
+ // All sort types have formatting functions
5951
+ displayMaster.sort( function ( a, b ) {
5952
+ var
5953
+ x, y, k, test, sort,
5954
+ len=aSort.length,
5955
+ dataA = aoData[a]._aSortData,
5956
+ dataB = aoData[b]._aSortData;
5957
+
5958
+ for ( k=0 ; k<len ; k++ ) {
5959
+ sort = aSort[k];
5960
+
5961
+ x = dataA[ sort.col ];
5962
+ y = dataB[ sort.col ];
5963
+
5964
+ test = x<y ? -1 : x>y ? 1 : 0;
5965
+ if ( test !== 0 ) {
5966
+ return sort.dir === 'asc' ? test : -test;
5967
+ }
5968
+ }
5969
+
5970
+ x = aiOrig[a];
5971
+ y = aiOrig[b];
5972
+ return x<y ? -1 : x>y ? 1 : 0;
5973
+ } );
5974
+ }
5975
+ else {
5976
+ // Depreciated - remove in 1.11 (providing a plug-in option)
5977
+ // Not all sort types have formatting methods, so we have to call their sorting
5978
+ // methods.
5979
+ displayMaster.sort( function ( a, b ) {
5980
+ var
5981
+ x, y, k, l, test, sort, fn,
5982
+ len=aSort.length,
5983
+ dataA = aoData[a]._aSortData,
5984
+ dataB = aoData[b]._aSortData;
5985
+
5986
+ for ( k=0 ; k<len ; k++ ) {
5987
+ sort = aSort[k];
5988
+
5989
+ x = dataA[ sort.col ];
5990
+ y = dataB[ sort.col ];
5991
+
5992
+ fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
5993
+ test = fn( x, y );
5994
+ if ( test !== 0 ) {
5995
+ return test;
5996
+ }
5997
+ }
5998
+
5999
+ x = aiOrig[a];
6000
+ y = aiOrig[b];
6001
+ return x<y ? -1 : x>y ? 1 : 0;
6002
+ } );
6003
+ }
6004
+ }
6005
+
6006
+ /* Tell the draw function that we have sorted the data */
6007
+ oSettings.bSorted = true;
6008
+ }
6009
+
6010
+
6011
+ function _fnSortAria ( settings )
6012
+ {
6013
+ var label;
6014
+ var nextSort;
6015
+ var columns = settings.aoColumns;
6016
+ var aSort = _fnSortFlatten( settings );
6017
+ var oAria = settings.oLanguage.oAria;
6018
+
6019
+ // ARIA attributes - need to loop all columns, to update all (removing old
6020
+ // attributes as needed)
6021
+ for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
6022
+ {
6023
+ var col = columns[i];
6024
+ var asSorting = col.asSorting;
6025
+ var sTitle = col.sTitle.replace( /<.*?>/g, "" );
6026
+ var th = col.nTh;
6027
+
6028
+ // IE7 is throwing an error when setting these properties with jQuery's
6029
+ // attr() and removeAttr() methods...
6030
+ th.removeAttribute('aria-sort');
6031
+
6032
+ /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
6033
+ if ( col.bSortable ) {
6034
+ if ( aSort.length > 0 && aSort[0].col == i ) {
6035
+ th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
6036
+ nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
6037
+ }
6038
+ else {
6039
+ nextSort = asSorting[0];
6040
+ }
6041
+
6042
+ label = sTitle + ( nextSort === "asc" ?
6043
+ oAria.sSortAscending :
6044
+ oAria.sSortDescending
6045
+ );
6046
+ }
6047
+ else {
6048
+ label = sTitle;
6049
+ }
6050
+
6051
+ th.setAttribute('aria-label', label);
6052
+ }
6053
+ }
6054
+
6055
+
6056
+ /**
6057
+ * Function to run on user sort request
6058
+ * @param {object} settings dataTables settings object
6059
+ * @param {node} attachTo node to attach the handler to
6060
+ * @param {int} colIdx column sorting index
6061
+ * @param {boolean} [append=false] Append the requested sort to the existing
6062
+ * sort if true (i.e. multi-column sort)
6063
+ * @param {function} [callback] callback function
6064
+ * @memberof DataTable#oApi
6065
+ */
6066
+ function _fnSortListener ( settings, colIdx, append, callback )
6067
+ {
6068
+ var col = settings.aoColumns[ colIdx ];
6069
+ var sorting = settings.aaSorting;
6070
+ var asSorting = col.asSorting;
6071
+ var nextSortIdx;
6072
+ var next = function ( a, overflow ) {
6073
+ var idx = a._idx;
6074
+ if ( idx === undefined ) {
6075
+ idx = $.inArray( a[1], asSorting );
6076
+ }
6077
+
6078
+ return idx+1 < asSorting.length ?
6079
+ idx+1 :
6080
+ overflow ?
6081
+ null :
6082
+ 0;
6083
+ };
6084
+
6085
+ // Convert to 2D array if needed
6086
+ if ( typeof sorting[0] === 'number' ) {
6087
+ sorting = settings.aaSorting = [ sorting ];
6088
+ }
6089
+
6090
+ // If appending the sort then we are multi-column sorting
6091
+ if ( append && settings.oFeatures.bSortMulti ) {
6092
+ // Are we already doing some kind of sort on this column?
6093
+ var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
6094
+
6095
+ if ( sortIdx !== -1 ) {
6096
+ // Yes, modify the sort
6097
+ nextSortIdx = next( sorting[sortIdx], true );
6098
+
6099
+ if ( nextSortIdx === null && sorting.length === 1 ) {
6100
+ nextSortIdx = 0; // can't remove sorting completely
6101
+ }
6102
+
6103
+ if ( nextSortIdx === null ) {
6104
+ sorting.splice( sortIdx, 1 );
6105
+ }
6106
+ else {
6107
+ sorting[sortIdx][1] = asSorting[ nextSortIdx ];
6108
+ sorting[sortIdx]._idx = nextSortIdx;
6109
+ }
6110
+ }
6111
+ else {
6112
+ // No sort on this column yet
6113
+ sorting.push( [ colIdx, asSorting[0], 0 ] );
6114
+ sorting[sorting.length-1]._idx = 0;
6115
+ }
6116
+ }
6117
+ else if ( sorting.length && sorting[0][0] == colIdx ) {
6118
+ // Single column - already sorting on this column, modify the sort
6119
+ nextSortIdx = next( sorting[0] );
6120
+
6121
+ sorting.length = 1;
6122
+ sorting[0][1] = asSorting[ nextSortIdx ];
6123
+ sorting[0]._idx = nextSortIdx;
6124
+ }
6125
+ else {
6126
+ // Single column - sort only on this column
6127
+ sorting.length = 0;
6128
+ sorting.push( [ colIdx, asSorting[0] ] );
6129
+ sorting[0]._idx = 0;
6130
+ }
6131
+
6132
+ // Run the sort by calling a full redraw
6133
+ _fnReDraw( settings );
6134
+
6135
+ // callback used for async user interaction
6136
+ if ( typeof callback == 'function' ) {
6137
+ callback( settings );
6138
+ }
6139
+ }
6140
+
6141
+
6142
+ /**
6143
+ * Attach a sort handler (click) to a node
6144
+ * @param {object} settings dataTables settings object
6145
+ * @param {node} attachTo node to attach the handler to
6146
+ * @param {int} colIdx column sorting index
6147
+ * @param {function} [callback] callback function
6148
+ * @memberof DataTable#oApi
6149
+ */
6150
+ function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
6151
+ {
6152
+ var col = settings.aoColumns[ colIdx ];
6153
+
6154
+ _fnBindAction( attachTo, {}, function (e) {
6155
+ /* If the column is not sortable - don't to anything */
6156
+ if ( col.bSortable === false ) {
6157
+ return;
6158
+ }
6159
+
6160
+ // If processing is enabled use a timeout to allow the processing
6161
+ // display to be shown - otherwise to it synchronously
6162
+ if ( settings.oFeatures.bProcessing ) {
6163
+ _fnProcessingDisplay( settings, true );
6164
+
6165
+ setTimeout( function() {
6166
+ _fnSortListener( settings, colIdx, e.shiftKey, callback );
6167
+
6168
+ // In server-side processing, the draw callback will remove the
6169
+ // processing display
6170
+ if ( _fnDataSource( settings ) !== 'ssp' ) {
6171
+ _fnProcessingDisplay( settings, false );
6172
+ }
6173
+ }, 0 );
6174
+ }
6175
+ else {
6176
+ _fnSortListener( settings, colIdx, e.shiftKey, callback );
6177
+ }
6178
+ } );
6179
+ }
6180
+
6181
+
6182
+ /**
6183
+ * Set the sorting classes on table's body, Note: it is safe to call this function
6184
+ * when bSort and bSortClasses are false
6185
+ * @param {object} oSettings dataTables settings object
6186
+ * @memberof DataTable#oApi
6187
+ */
6188
+ function _fnSortingClasses( settings )
6189
+ {
6190
+ var oldSort = settings.aLastSort;
6191
+ var sortClass = settings.oClasses.sSortColumn;
6192
+ var sort = _fnSortFlatten( settings );
6193
+ var features = settings.oFeatures;
6194
+ var i, ien, colIdx;
6195
+
6196
+ if ( features.bSort && features.bSortClasses ) {
6197
+ // Remove old sorting classes
6198
+ for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
6199
+ colIdx = oldSort[i].src;
6200
+
6201
+ // Remove column sorting
6202
+ $( _pluck( settings.aoData, 'anCells', colIdx ) )
6203
+ .removeClass( sortClass + (i<2 ? i+1 : 3) );
6204
+ }
6205
+
6206
+ // Add new column sorting
6207
+ for ( i=0, ien=sort.length ; i<ien ; i++ ) {
6208
+ colIdx = sort[i].src;
6209
+
6210
+ $( _pluck( settings.aoData, 'anCells', colIdx ) )
6211
+ .addClass( sortClass + (i<2 ? i+1 : 3) );
6212
+ }
6213
+ }
6214
+
6215
+ settings.aLastSort = sort;
6216
+ }
6217
+
6218
+
6219
+ // Get the data to sort a column, be it from cache, fresh (populating the
6220
+ // cache), or from a sort formatter
6221
+ function _fnSortData( settings, idx )
6222
+ {
6223
+ // Custom sorting function - provided by the sort data type
6224
+ var column = settings.aoColumns[ idx ];
6225
+ var customSort = DataTable.ext.order[ column.sSortDataType ];
6226
+ var customData;
6227
+
6228
+ if ( customSort ) {
6229
+ customData = customSort.call( settings.oInstance, settings, idx,
6230
+ _fnColumnIndexToVisible( settings, idx )
6231
+ );
6232
+ }
6233
+
6234
+ // Use / populate cache
6235
+ var row, cellData;
6236
+ var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
6237
+
6238
+ for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
6239
+ row = settings.aoData[i];
6240
+
6241
+ if ( ! row._aSortData ) {
6242
+ row._aSortData = [];
6243
+ }
6244
+
6245
+ if ( ! row._aSortData[idx] || customSort ) {
6246
+ cellData = customSort ?
6247
+ customData[i] : // If there was a custom sort function, use data from there
6248
+ _fnGetCellData( settings, i, idx, 'sort' );
6249
+
6250
+ row._aSortData[ idx ] = formatter ?
6251
+ formatter( cellData ) :
6252
+ cellData;
6253
+ }
6254
+ }
6255
+ }
6256
+
6257
+
6258
+
6259
+ /**
6260
+ * Save the state of a table
6261
+ * @param {object} oSettings dataTables settings object
6262
+ * @memberof DataTable#oApi
6263
+ */
6264
+ function _fnSaveState ( settings )
6265
+ {
6266
+ if ( !settings.oFeatures.bStateSave || settings.bDestroying )
6267
+ {
6268
+ return;
6269
+ }
6270
+
6271
+ /* Store the interesting variables */
6272
+ var state = {
6273
+ time: +new Date(),
6274
+ start: settings._iDisplayStart,
6275
+ length: settings._iDisplayLength,
6276
+ order: $.extend( true, [], settings.aaSorting ),
6277
+ search: _fnSearchToCamel( settings.oPreviousSearch ),
6278
+ columns: $.map( settings.aoColumns, function ( col, i ) {
6279
+ return {
6280
+ visible: col.bVisible,
6281
+ search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
6282
+ };
6283
+ } )
6284
+ };
6285
+
6286
+ _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
6287
+
6288
+ settings.oSavedState = state;
6289
+ settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
6290
+ }
6291
+
6292
+
6293
+ /**
6294
+ * Attempt to load a saved table state
6295
+ * @param {object} oSettings dataTables settings object
6296
+ * @param {object} oInit DataTables init object so we can override settings
6297
+ * @memberof DataTable#oApi
6298
+ */
6299
+ function _fnLoadState ( settings, oInit )
6300
+ {
6301
+ var i, ien;
6302
+ var columns = settings.aoColumns;
6303
+
6304
+ if ( ! settings.oFeatures.bStateSave ) {
6305
+ return;
6306
+ }
6307
+
6308
+ var state = settings.fnStateLoadCallback.call( settings.oInstance, settings );
6309
+ if ( ! state || ! state.time ) {
6310
+ return;
6311
+ }
6312
+
6313
+ /* Allow custom and plug-in manipulation functions to alter the saved data set and
6314
+ * cancelling of loading by returning false
6315
+ */
6316
+ var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state] );
6317
+ if ( $.inArray( false, abStateLoad ) !== -1 ) {
6318
+ return;
6319
+ }
6320
+
6321
+ /* Reject old data */
6322
+ var duration = settings.iStateDuration;
6323
+ if ( duration > 0 && state.time < +new Date() - (duration*1000) ) {
6324
+ return;
6325
+ }
6326
+
6327
+ // Number of columns have changed - all bets are off, no restore of settings
6328
+ if ( columns.length !== state.columns.length ) {
6329
+ return;
6330
+ }
6331
+
6332
+ // Store the saved state so it might be accessed at any time
6333
+ settings.oLoadedState = $.extend( true, {}, state );
6334
+
6335
+ // Restore key features - todo - for 1.11 this needs to be done by
6336
+ // subscribed events
6337
+ if ( state.start !== undefined ) {
6338
+ settings._iDisplayStart = state.start;
6339
+ settings.iInitDisplayStart = state.start;
6340
+ }
6341
+ if ( state.length !== undefined ) {
6342
+ settings._iDisplayLength = state.length;
6343
+ }
6344
+
6345
+ // Order
6346
+ if ( state.order !== undefined ) {
6347
+ settings.aaSorting = [];
6348
+ $.each( state.order, function ( i, col ) {
6349
+ settings.aaSorting.push( col[0] >= columns.length ?
6350
+ [ 0, col[1] ] :
6351
+ col
6352
+ );
6353
+ } );
6354
+ }
6355
+
6356
+ // Search
6357
+ if ( state.search !== undefined ) {
6358
+ $.extend( settings.oPreviousSearch, _fnSearchToHung( state.search ) );
6359
+ }
6360
+
6361
+ // Columns
6362
+ for ( i=0, ien=state.columns.length ; i<ien ; i++ ) {
6363
+ var col = state.columns[i];
6364
+
6365
+ // Visibility
6366
+ if ( col.visible !== undefined ) {
6367
+ columns[i].bVisible = col.visible;
6368
+ }
6369
+
6370
+ // Search
6371
+ if ( col.search !== undefined ) {
6372
+ $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
6373
+ }
6374
+ }
6375
+
6376
+ _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, state] );
6377
+ }
6378
+
6379
+
6380
+ /**
6381
+ * Return the settings object for a particular table
6382
+ * @param {node} table table we are using as a dataTable
6383
+ * @returns {object} Settings object - or null if not found
6384
+ * @memberof DataTable#oApi
6385
+ */
6386
+ function _fnSettingsFromNode ( table )
6387
+ {
6388
+ var settings = DataTable.settings;
6389
+ var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
6390
+
6391
+ return idx !== -1 ?
6392
+ settings[ idx ] :
6393
+ null;
6394
+ }
6395
+
6396
+
6397
+ /**
6398
+ * Log an error message
6399
+ * @param {object} settings dataTables settings object
6400
+ * @param {int} level log error messages, or display them to the user
6401
+ * @param {string} msg error message
6402
+ * @param {int} tn Technical note id to get more information about the error.
6403
+ * @memberof DataTable#oApi
6404
+ */
6405
+ function _fnLog( settings, level, msg, tn )
6406
+ {
6407
+ msg = 'DataTables warning: '+
6408
+ (settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
6409
+
6410
+ if ( tn ) {
6411
+ msg += '. For more information about this error, please see '+
6412
+ 'http://datatables.net/tn/'+tn;
6413
+ }
6414
+
6415
+ if ( ! level ) {
6416
+ // Backwards compatibility pre 1.10
6417
+ var ext = DataTable.ext;
6418
+ var type = ext.sErrMode || ext.errMode;
6419
+
6420
+ if ( settings ) {
6421
+ _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
6422
+ }
6423
+
6424
+ if ( type == 'alert' ) {
6425
+ alert( msg );
6426
+ }
6427
+ else if ( type == 'throw' ) {
6428
+ throw new Error(msg);
6429
+ }
6430
+ else if ( typeof type == 'function' ) {
6431
+ type( settings, tn, msg );
6432
+ }
6433
+ }
6434
+ else if ( window.console && console.log ) {
6435
+ console.log( msg );
6436
+ }
6437
+ }
6438
+
6439
+
6440
+ /**
6441
+ * See if a property is defined on one object, if so assign it to the other object
6442
+ * @param {object} ret target object
6443
+ * @param {object} src source object
6444
+ * @param {string} name property
6445
+ * @param {string} [mappedName] name to map too - optional, name used if not given
6446
+ * @memberof DataTable#oApi
6447
+ */
6448
+ function _fnMap( ret, src, name, mappedName )
6449
+ {
6450
+ if ( $.isArray( name ) ) {
6451
+ $.each( name, function (i, val) {
6452
+ if ( $.isArray( val ) ) {
6453
+ _fnMap( ret, src, val[0], val[1] );
6454
+ }
6455
+ else {
6456
+ _fnMap( ret, src, val );
6457
+ }
6458
+ } );
6459
+
6460
+ return;
6461
+ }
6462
+
6463
+ if ( mappedName === undefined ) {
6464
+ mappedName = name;
6465
+ }
6466
+
6467
+ if ( src[name] !== undefined ) {
6468
+ ret[mappedName] = src[name];
6469
+ }
6470
+ }
6471
+
6472
+
6473
+ /**
6474
+ * Extend objects - very similar to jQuery.extend, but deep copy objects, and
6475
+ * shallow copy arrays. The reason we need to do this, is that we don't want to
6476
+ * deep copy array init values (such as aaSorting) since the dev wouldn't be
6477
+ * able to override them, but we do want to deep copy arrays.
6478
+ * @param {object} out Object to extend
6479
+ * @param {object} extender Object from which the properties will be applied to
6480
+ * out
6481
+ * @param {boolean} breakRefs If true, then arrays will be sliced to take an
6482
+ * independent copy with the exception of the `data` or `aaData` parameters
6483
+ * if they are present. This is so you can pass in a collection to
6484
+ * DataTables and have that used as your data source without breaking the
6485
+ * references
6486
+ * @returns {object} out Reference, just for convenience - out === the return.
6487
+ * @memberof DataTable#oApi
6488
+ * @todo This doesn't take account of arrays inside the deep copied objects.
6489
+ */
6490
+ function _fnExtend( out, extender, breakRefs )
6491
+ {
6492
+ var val;
6493
+
6494
+ for ( var prop in extender ) {
6495
+ if ( extender.hasOwnProperty(prop) ) {
6496
+ val = extender[prop];
6497
+
6498
+ if ( $.isPlainObject( val ) ) {
6499
+ if ( ! $.isPlainObject( out[prop] ) ) {
6500
+ out[prop] = {};
6501
+ }
6502
+ $.extend( true, out[prop], val );
6503
+ }
6504
+ else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) {
6505
+ out[prop] = val.slice();
6506
+ }
6507
+ else {
6508
+ out[prop] = val;
6509
+ }
6510
+ }
6511
+ }
6512
+
6513
+ return out;
6514
+ }
6515
+
6516
+
6517
+ /**
6518
+ * Bind an event handers to allow a click or return key to activate the callback.
6519
+ * This is good for accessibility since a return on the keyboard will have the
6520
+ * same effect as a click, if the element has focus.
6521
+ * @param {element} n Element to bind the action to
6522
+ * @param {object} oData Data object to pass to the triggered function
6523
+ * @param {function} fn Callback function for when the event is triggered
6524
+ * @memberof DataTable#oApi
6525
+ */
6526
+ function _fnBindAction( n, oData, fn )
6527
+ {
6528
+ $(n)
6529
+ .bind( 'click.DT', oData, function (e) {
6530
+ n.blur(); // Remove focus outline for mouse users
6531
+ fn(e);
6532
+ } )
6533
+ .bind( 'keypress.DT', oData, function (e){
6534
+ if ( e.which === 13 ) {
6535
+ e.preventDefault();
6536
+ fn(e);
6537
+ }
6538
+ } )
6539
+ .bind( 'selectstart.DT', function () {
6540
+ /* Take the brutal approach to cancelling text selection */
6541
+ return false;
6542
+ } );
6543
+ }
6544
+
6545
+
6546
+ /**
6547
+ * Register a callback function. Easily allows a callback function to be added to
6548
+ * an array store of callback functions that can then all be called together.
6549
+ * @param {object} oSettings dataTables settings object
6550
+ * @param {string} sStore Name of the array storage for the callbacks in oSettings
6551
+ * @param {function} fn Function to be called back
6552
+ * @param {string} sName Identifying name for the callback (i.e. a label)
6553
+ * @memberof DataTable#oApi
6554
+ */
6555
+ function _fnCallbackReg( oSettings, sStore, fn, sName )
6556
+ {
6557
+ if ( fn )
6558
+ {
6559
+ oSettings[sStore].push( {
6560
+ "fn": fn,
6561
+ "sName": sName
6562
+ } );
6563
+ }
6564
+ }
6565
+
6566
+
6567
+ /**
6568
+ * Fire callback functions and trigger events. Note that the loop over the
6569
+ * callback array store is done backwards! Further note that you do not want to
6570
+ * fire off triggers in time sensitive applications (for example cell creation)
6571
+ * as its slow.
6572
+ * @param {object} settings dataTables settings object
6573
+ * @param {string} callbackArr Name of the array storage for the callbacks in
6574
+ * oSettings
6575
+ * @param {string} eventName Name of the jQuery custom event to trigger. If
6576
+ * null no trigger is fired
6577
+ * @param {array} args Array of arguments to pass to the callback function /
6578
+ * trigger
6579
+ * @memberof DataTable#oApi
6580
+ */
6581
+ function _fnCallbackFire( settings, callbackArr, eventName, args )
6582
+ {
6583
+ var ret = [];
6584
+
6585
+ if ( callbackArr ) {
6586
+ ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
6587
+ return val.fn.apply( settings.oInstance, args );
6588
+ } );
6589
+ }
6590
+
6591
+ if ( eventName !== null ) {
6592
+ var e = $.Event( eventName+'.dt' );
6593
+
6594
+ $(settings.nTable).trigger( e, args );
6595
+
6596
+ ret.push( e.result );
6597
+ }
6598
+
6599
+ return ret;
6600
+ }
6601
+
6602
+
6603
+ function _fnLengthOverflow ( settings )
6604
+ {
6605
+ var
6606
+ start = settings._iDisplayStart,
6607
+ end = settings.fnDisplayEnd(),
6608
+ len = settings._iDisplayLength;
6609
+
6610
+ /* If we have space to show extra rows (backing up from the end point - then do so */
6611
+ if ( start >= end )
6612
+ {
6613
+ start = end - len;
6614
+ }
6615
+
6616
+ // Keep the start record on the current page
6617
+ start -= (start % len);
6618
+
6619
+ if ( len === -1 || start < 0 )
6620
+ {
6621
+ start = 0;
6622
+ }
6623
+
6624
+ settings._iDisplayStart = start;
6625
+ }
6626
+
6627
+
6628
+ function _fnRenderer( settings, type )
6629
+ {
6630
+ var renderer = settings.renderer;
6631
+ var host = DataTable.ext.renderer[type];
6632
+
6633
+ if ( $.isPlainObject( renderer ) && renderer[type] ) {
6634
+ // Specific renderer for this type. If available use it, otherwise use
6635
+ // the default.
6636
+ return host[renderer[type]] || host._;
6637
+ }
6638
+ else if ( typeof renderer === 'string' ) {
6639
+ // Common renderer - if there is one available for this type use it,
6640
+ // otherwise use the default
6641
+ return host[renderer] || host._;
6642
+ }
6643
+
6644
+ // Use the default
6645
+ return host._;
6646
+ }
6647
+
6648
+
6649
+ /**
6650
+ * Detect the data source being used for the table. Used to simplify the code
6651
+ * a little (ajax) and to make it compress a little smaller.
6652
+ *
6653
+ * @param {object} settings dataTables settings object
6654
+ * @returns {string} Data source
6655
+ * @memberof DataTable#oApi
6656
+ */
6657
+ function _fnDataSource ( settings )
6658
+ {
6659
+ if ( settings.oFeatures.bServerSide ) {
6660
+ return 'ssp';
6661
+ }
6662
+ else if ( settings.ajax || settings.sAjaxSource ) {
6663
+ return 'ajax';
6664
+ }
6665
+ return 'dom';
6666
+ }
6667
+
6668
+
6669
+
6670
+
6671
+ /**
6672
+ * Computed structure of the DataTables API, defined by the options passed to
6673
+ * `DataTable.Api.register()` when building the API.
6674
+ *
6675
+ * The structure is built in order to speed creation and extension of the Api
6676
+ * objects since the extensions are effectively pre-parsed.
6677
+ *
6678
+ * The array is an array of objects with the following structure, where this
6679
+ * base array represents the Api prototype base:
6680
+ *
6681
+ * [
6682
+ * {
6683
+ * name: 'data' -- string - Property name
6684
+ * val: function () {}, -- function - Api method (or undefined if just an object
6685
+ * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
6686
+ * propExt: [ ... ] -- array - Array of Api object definitions to extend the property
6687
+ * },
6688
+ * {
6689
+ * name: 'row'
6690
+ * val: {},
6691
+ * methodExt: [ ... ],
6692
+ * propExt: [
6693
+ * {
6694
+ * name: 'data'
6695
+ * val: function () {},
6696
+ * methodExt: [ ... ],
6697
+ * propExt: [ ... ]
6698
+ * },
6699
+ * ...
6700
+ * ]
6701
+ * }
6702
+ * ]
6703
+ *
6704
+ * @type {Array}
6705
+ * @ignore
6706
+ */
6707
+ var __apiStruct = [];
6708
+
6709
+
6710
+ /**
6711
+ * `Array.prototype` reference.
6712
+ *
6713
+ * @type object
6714
+ * @ignore
6715
+ */
6716
+ var __arrayProto = Array.prototype;
6717
+
6718
+
6719
+ /**
6720
+ * Abstraction for `context` parameter of the `Api` constructor to allow it to
6721
+ * take several different forms for ease of use.
6722
+ *
6723
+ * Each of the input parameter types will be converted to a DataTables settings
6724
+ * object where possible.
6725
+ *
6726
+ * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one
6727
+ * of:
6728
+ *
6729
+ * * `string` - jQuery selector. Any DataTables' matching the given selector
6730
+ * with be found and used.
6731
+ * * `node` - `TABLE` node which has already been formed into a DataTable.
6732
+ * * `jQuery` - A jQuery object of `TABLE` nodes.
6733
+ * * `object` - DataTables settings object
6734
+ * * `DataTables.Api` - API instance
6735
+ * @return {array|null} Matching DataTables settings objects. `null` or
6736
+ * `undefined` is returned if no matching DataTable is found.
6737
+ * @ignore
6738
+ */
6739
+ var _toSettings = function ( mixed )
6740
+ {
6741
+ var idx, jq;
6742
+ var settings = DataTable.settings;
6743
+ var tables = $.map( settings, function (el, i) {
6744
+ return el.nTable;
6745
+ } );
6746
+
6747
+ if ( ! mixed ) {
6748
+ return [];
6749
+ }
6750
+ else if ( mixed.nTable && mixed.oApi ) {
6751
+ // DataTables settings object
6752
+ return [ mixed ];
6753
+ }
6754
+ else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
6755
+ // Table node
6756
+ idx = $.inArray( mixed, tables );
6757
+ return idx !== -1 ? [ settings[idx] ] : null;
6758
+ }
6759
+ else if ( mixed && typeof mixed.settings === 'function' ) {
6760
+ return mixed.settings().toArray();
6761
+ }
6762
+ else if ( typeof mixed === 'string' ) {
6763
+ // jQuery selector
6764
+ jq = $(mixed);
6765
+ }
6766
+ else if ( mixed instanceof $ ) {
6767
+ // jQuery object (also DataTables instance)
6768
+ jq = mixed;
6769
+ }
6770
+
6771
+ if ( jq ) {
6772
+ return jq.map( function(i) {
6773
+ idx = $.inArray( this, tables );
6774
+ return idx !== -1 ? settings[idx] : null;
6775
+ } ).toArray();
6776
+ }
6777
+ };
6778
+
6779
+
6780
+ /**
6781
+ * DataTables API class - used to control and interface with one or more
6782
+ * DataTables enhanced tables.
6783
+ *
6784
+ * The API class is heavily based on jQuery, presenting a chainable interface
6785
+ * that you can use to interact with tables. Each instance of the API class has
6786
+ * a "context" - i.e. the tables that it will operate on. This could be a single
6787
+ * table, all tables on a page or a sub-set thereof.
6788
+ *
6789
+ * Additionally the API is designed to allow you to easily work with the data in
6790
+ * the tables, retrieving and manipulating it as required. This is done by
6791
+ * presenting the API class as an array like interface. The contents of the
6792
+ * array depend upon the actions requested by each method (for example
6793
+ * `rows().nodes()` will return an array of nodes, while `rows().data()` will
6794
+ * return an array of objects or arrays depending upon your table's
6795
+ * configuration). The API object has a number of array like methods (`push`,
6796
+ * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,
6797
+ * `unique` etc) to assist your working with the data held in a table.
6798
+ *
6799
+ * Most methods (those which return an Api instance) are chainable, which means
6800
+ * the return from a method call also has all of the methods available that the
6801
+ * top level object had. For example, these two calls are equivalent:
6802
+ *
6803
+ * // Not chained
6804
+ * api.row.add( {...} );
6805
+ * api.draw();
6806
+ *
6807
+ * // Chained
6808
+ * api.row.add( {...} ).draw();
6809
+ *
6810
+ * @class DataTable.Api
6811
+ * @param {array|object|string|jQuery} context DataTable identifier. This is
6812
+ * used to define which DataTables enhanced tables this API will operate on.
6813
+ * Can be one of:
6814
+ *
6815
+ * * `string` - jQuery selector. Any DataTables' matching the given selector
6816
+ * with be found and used.
6817
+ * * `node` - `TABLE` node which has already been formed into a DataTable.
6818
+ * * `jQuery` - A jQuery object of `TABLE` nodes.
6819
+ * * `object` - DataTables settings object
6820
+ * @param {array} [data] Data to initialise the Api instance with.
6821
+ *
6822
+ * @example
6823
+ * // Direct initialisation during DataTables construction
6824
+ * var api = $('#example').DataTable();
6825
+ *
6826
+ * @example
6827
+ * // Initialisation using a DataTables jQuery object
6828
+ * var api = $('#example').dataTable().api();
6829
+ *
6830
+ * @example
6831
+ * // Initialisation as a constructor
6832
+ * var api = new $.fn.DataTable.Api( 'table.dataTable' );
6833
+ */
6834
+ _Api = function ( context, data )
6835
+ {
6836
+ if ( ! (this instanceof _Api) ) {
6837
+ return new _Api( context, data );
6838
+ }
6839
+
6840
+ var settings = [];
6841
+ var ctxSettings = function ( o ) {
6842
+ var a = _toSettings( o );
6843
+ if ( a ) {
6844
+ settings = settings.concat( a );
6845
+ }
6846
+ };
6847
+
6848
+ if ( $.isArray( context ) ) {
6849
+ for ( var i=0, ien=context.length ; i<ien ; i++ ) {
6850
+ ctxSettings( context[i] );
6851
+ }
6852
+ }
6853
+ else {
6854
+ ctxSettings( context );
6855
+ }
6856
+
6857
+ // Remove duplicates
6858
+ this.context = _unique( settings );
6859
+
6860
+ // Initial data
6861
+ if ( data ) {
6862
+ $.merge( this, data );
6863
+ }
6864
+
6865
+ // selector
6866
+ this.selector = {
6867
+ rows: null,
6868
+ cols: null,
6869
+ opts: null
6870
+ };
6871
+
6872
+ _Api.extend( this, this, __apiStruct );
6873
+ };
6874
+
6875
+ DataTable.Api = _Api;
6876
+
6877
+ // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
6878
+ // isPlainObject.
6879
+ $.extend( _Api.prototype, {
6880
+ any: function ()
6881
+ {
6882
+ return this.count() !== 0;
6883
+ },
6884
+
6885
+
6886
+ concat: __arrayProto.concat,
6887
+
6888
+
6889
+ context: [], // array of table settings objects
6890
+
6891
+
6892
+ count: function ()
6893
+ {
6894
+ return this.flatten().length;
6895
+ },
6896
+
6897
+
6898
+ each: function ( fn )
6899
+ {
6900
+ for ( var i=0, ien=this.length ; i<ien; i++ ) {
6901
+ fn.call( this, this[i], i, this );
6902
+ }
6903
+
6904
+ return this;
6905
+ },
6906
+
6907
+
6908
+ eq: function ( idx )
6909
+ {
6910
+ var ctx = this.context;
6911
+
6912
+ return ctx.length > idx ?
6913
+ new _Api( ctx[idx], this[idx] ) :
6914
+ null;
6915
+ },
6916
+
6917
+
6918
+ filter: function ( fn )
6919
+ {
6920
+ var a = [];
6921
+
6922
+ if ( __arrayProto.filter ) {
6923
+ a = __arrayProto.filter.call( this, fn, this );
6924
+ }
6925
+ else {
6926
+ // Compatibility for browsers without EMCA-252-5 (JS 1.6)
6927
+ for ( var i=0, ien=this.length ; i<ien ; i++ ) {
6928
+ if ( fn.call( this, this[i], i, this ) ) {
6929
+ a.push( this[i] );
6930
+ }
6931
+ }
6932
+ }
6933
+
6934
+ return new _Api( this.context, a );
6935
+ },
6936
+
6937
+
6938
+ flatten: function ()
6939
+ {
6940
+ var a = [];
6941
+ return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
6942
+ },
6943
+
6944
+
6945
+ join: __arrayProto.join,
6946
+
6947
+
6948
+ indexOf: __arrayProto.indexOf || function (obj, start)
6949
+ {
6950
+ for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
6951
+ if ( this[i] === obj ) {
6952
+ return i;
6953
+ }
6954
+ }
6955
+ return -1;
6956
+ },
6957
+
6958
+ iterator: function ( flatten, type, fn, alwaysNew ) {
6959
+ var
6960
+ a = [], ret,
6961
+ i, ien, j, jen,
6962
+ context = this.context,
6963
+ rows, items, item,
6964
+ selector = this.selector;
6965
+
6966
+ // Argument shifting
6967
+ if ( typeof flatten === 'string' ) {
6968
+ alwaysNew = fn;
6969
+ fn = type;
6970
+ type = flatten;
6971
+ flatten = false;
6972
+ }
6973
+
6974
+ for ( i=0, ien=context.length ; i<ien ; i++ ) {
6975
+ var apiInst = new _Api( context[i] );
6976
+
6977
+ if ( type === 'table' ) {
6978
+ ret = fn.call( apiInst, context[i], i );
6979
+
6980
+ if ( ret !== undefined ) {
6981
+ a.push( ret );
6982
+ }
6983
+ }
6984
+ else if ( type === 'columns' || type === 'rows' ) {
6985
+ // this has same length as context - one entry for each table
6986
+ ret = fn.call( apiInst, context[i], this[i], i );
6987
+
6988
+ if ( ret !== undefined ) {
6989
+ a.push( ret );
6990
+ }
6991
+ }
6992
+ else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
6993
+ // columns and rows share the same structure.
6994
+ // 'this' is an array of column indexes for each context
6995
+ items = this[i];
6996
+
6997
+ if ( type === 'column-rows' ) {
6998
+ rows = _selector_row_indexes( context[i], selector.opts );
6999
+ }
7000
+
7001
+ for ( j=0, jen=items.length ; j<jen ; j++ ) {
7002
+ item = items[j];
7003
+
7004
+ if ( type === 'cell' ) {
7005
+ ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
7006
+ }
7007
+ else {
7008
+ ret = fn.call( apiInst, context[i], item, i, j, rows );
7009
+ }
7010
+
7011
+ if ( ret !== undefined ) {
7012
+ a.push( ret );
7013
+ }
7014
+ }
7015
+ }
7016
+ }
7017
+
7018
+ if ( a.length || alwaysNew ) {
7019
+ var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
7020
+ var apiSelector = api.selector;
7021
+ apiSelector.rows = selector.rows;
7022
+ apiSelector.cols = selector.cols;
7023
+ apiSelector.opts = selector.opts;
7024
+ return api;
7025
+ }
7026
+ return this;
7027
+ },
7028
+
7029
+
7030
+ lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
7031
+ {
7032
+ // Bit cheeky...
7033
+ return this.indexOf.apply( this.toArray.reverse(), arguments );
7034
+ },
7035
+
7036
+
7037
+ length: 0,
7038
+
7039
+
7040
+ map: function ( fn )
7041
+ {
7042
+ var a = [];
7043
+
7044
+ if ( __arrayProto.map ) {
7045
+ a = __arrayProto.map.call( this, fn, this );
7046
+ }
7047
+ else {
7048
+ // Compatibility for browsers without EMCA-252-5 (JS 1.6)
7049
+ for ( var i=0, ien=this.length ; i<ien ; i++ ) {
7050
+ a.push( fn.call( this, this[i], i ) );
7051
+ }
7052
+ }
7053
+
7054
+ return new _Api( this.context, a );
7055
+ },
7056
+
7057
+
7058
+ pluck: function ( prop )
7059
+ {
7060
+ return this.map( function ( el ) {
7061
+ return el[ prop ];
7062
+ } );
7063
+ },
7064
+
7065
+ pop: __arrayProto.pop,
7066
+
7067
+
7068
+ push: __arrayProto.push,
7069
+
7070
+
7071
+ // Does not return an API instance
7072
+ reduce: __arrayProto.reduce || function ( fn, init )
7073
+ {
7074
+ return _fnReduce( this, fn, init, 0, this.length, 1 );
7075
+ },
7076
+
7077
+
7078
+ reduceRight: __arrayProto.reduceRight || function ( fn, init )
7079
+ {
7080
+ return _fnReduce( this, fn, init, this.length-1, -1, -1 );
7081
+ },
7082
+
7083
+
7084
+ reverse: __arrayProto.reverse,
7085
+
7086
+
7087
+ // Object with rows, columns and opts
7088
+ selector: null,
7089
+
7090
+
7091
+ shift: __arrayProto.shift,
7092
+
7093
+
7094
+ sort: __arrayProto.sort, // ? name - order?
7095
+
7096
+
7097
+ splice: __arrayProto.splice,
7098
+
7099
+
7100
+ toArray: function ()
7101
+ {
7102
+ return __arrayProto.slice.call( this );
7103
+ },
7104
+
7105
+
7106
+ to$: function ()
7107
+ {
7108
+ return $( this );
7109
+ },
7110
+
7111
+
7112
+ toJQuery: function ()
7113
+ {
7114
+ return $( this );
7115
+ },
7116
+
7117
+
7118
+ unique: function ()
7119
+ {
7120
+ return new _Api( this.context, _unique(this) );
7121
+ },
7122
+
7123
+
7124
+ unshift: __arrayProto.unshift
7125
+ } );
7126
+
7127
+
7128
+ _Api.extend = function ( scope, obj, ext )
7129
+ {
7130
+ // Only extend API instances and static properties of the API
7131
+ if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
7132
+ return;
7133
+ }
7134
+
7135
+ var
7136
+ i, ien,
7137
+ j, jen,
7138
+ struct, inner,
7139
+ methodScoping = function ( scope, fn, struc ) {
7140
+ return function () {
7141
+ var ret = fn.apply( scope, arguments );
7142
+
7143
+ // Method extension
7144
+ _Api.extend( ret, ret, struc.methodExt );
7145
+ return ret;
7146
+ };
7147
+ };
7148
+
7149
+ for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7150
+ struct = ext[i];
7151
+
7152
+ // Value
7153
+ obj[ struct.name ] = typeof struct.val === 'function' ?
7154
+ methodScoping( scope, struct.val, struct ) :
7155
+ $.isPlainObject( struct.val ) ?
7156
+ {} :
7157
+ struct.val;
7158
+
7159
+ obj[ struct.name ].__dt_wrapper = true;
7160
+
7161
+ // Property extension
7162
+ _Api.extend( scope, obj[ struct.name ], struct.propExt );
7163
+ }
7164
+ };
7165
+
7166
+
7167
+ // @todo - Is there need for an augment function?
7168
+ // _Api.augment = function ( inst, name )
7169
+ // {
7170
+ // // Find src object in the structure from the name
7171
+ // var parts = name.split('.');
7172
+
7173
+ // _Api.extend( inst, obj );
7174
+ // };
7175
+
7176
+
7177
+ // [
7178
+ // {
7179
+ // name: 'data' -- string - Property name
7180
+ // val: function () {}, -- function - Api method (or undefined if just an object
7181
+ // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
7182
+ // propExt: [ ... ] -- array - Array of Api object definitions to extend the property
7183
+ // },
7184
+ // {
7185
+ // name: 'row'
7186
+ // val: {},
7187
+ // methodExt: [ ... ],
7188
+ // propExt: [
7189
+ // {
7190
+ // name: 'data'
7191
+ // val: function () {},
7192
+ // methodExt: [ ... ],
7193
+ // propExt: [ ... ]
7194
+ // },
7195
+ // ...
7196
+ // ]
7197
+ // }
7198
+ // ]
7199
+
7200
+ _Api.register = _api_register = function ( name, val )
7201
+ {
7202
+ if ( $.isArray( name ) ) {
7203
+ for ( var j=0, jen=name.length ; j<jen ; j++ ) {
7204
+ _Api.register( name[j], val );
7205
+ }
7206
+ return;
7207
+ }
7208
+
7209
+ var
7210
+ i, ien,
7211
+ heir = name.split('.'),
7212
+ struct = __apiStruct,
7213
+ key, method;
7214
+
7215
+ var find = function ( src, name ) {
7216
+ for ( var i=0, ien=src.length ; i<ien ; i++ ) {
7217
+ if ( src[i].name === name ) {
7218
+ return src[i];
7219
+ }
7220
+ }
7221
+ return null;
7222
+ };
7223
+
7224
+ for ( i=0, ien=heir.length ; i<ien ; i++ ) {
7225
+ method = heir[i].indexOf('()') !== -1;
7226
+ key = method ?
7227
+ heir[i].replace('()', '') :
7228
+ heir[i];
7229
+
7230
+ var src = find( struct, key );
7231
+ if ( ! src ) {
7232
+ src = {
7233
+ name: key,
7234
+ val: {},
7235
+ methodExt: [],
7236
+ propExt: []
7237
+ };
7238
+ struct.push( src );
7239
+ }
7240
+
7241
+ if ( i === ien-1 ) {
7242
+ src.val = val;
7243
+ }
7244
+ else {
7245
+ struct = method ?
7246
+ src.methodExt :
7247
+ src.propExt;
7248
+ }
7249
+ }
7250
+ };
7251
+
7252
+
7253
+ _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
7254
+ _Api.register( pluralName, val );
7255
+
7256
+ _Api.register( singularName, function () {
7257
+ var ret = val.apply( this, arguments );
7258
+
7259
+ if ( ret === this ) {
7260
+ // Returned item is the API instance that was passed in, return it
7261
+ return this;
7262
+ }
7263
+ else if ( ret instanceof _Api ) {
7264
+ // New API instance returned, want the value from the first item
7265
+ // in the returned array for the singular result.
7266
+ return ret.length ?
7267
+ $.isArray( ret[0] ) ?
7268
+ new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
7269
+ ret[0] :
7270
+ undefined;
7271
+ }
7272
+
7273
+ // Non-API return - just fire it back
7274
+ return ret;
7275
+ } );
7276
+ };
7277
+
7278
+
7279
+ /**
7280
+ * Selector for HTML tables. Apply the given selector to the give array of
7281
+ * DataTables settings objects.
7282
+ *
7283
+ * @param {string|integer} [selector] jQuery selector string or integer
7284
+ * @param {array} Array of DataTables settings objects to be filtered
7285
+ * @return {array}
7286
+ * @ignore
7287
+ */
7288
+ var __table_selector = function ( selector, a )
7289
+ {
7290
+ // Integer is used to pick out a table by index
7291
+ if ( typeof selector === 'number' ) {
7292
+ return [ a[ selector ] ];
7293
+ }
7294
+
7295
+ // Perform a jQuery selector on the table nodes
7296
+ var nodes = $.map( a, function (el, i) {
7297
+ return el.nTable;
7298
+ } );
7299
+
7300
+ return $(nodes)
7301
+ .filter( selector )
7302
+ .map( function (i) {
7303
+ // Need to translate back from the table node to the settings
7304
+ var idx = $.inArray( this, nodes );
7305
+ return a[ idx ];
7306
+ } )
7307
+ .toArray();
7308
+ };
7309
+
7310
+
7311
+
7312
+ /**
7313
+ * Context selector for the API's context (i.e. the tables the API instance
7314
+ * refers to.
7315
+ *
7316
+ * @name DataTable.Api#tables
7317
+ * @param {string|integer} [selector] Selector to pick which tables the iterator
7318
+ * should operate on. If not given, all tables in the current context are
7319
+ * used. This can be given as a jQuery selector (for example `':gt(0)'`) to
7320
+ * select multiple tables or as an integer to select a single table.
7321
+ * @returns {DataTable.Api} Returns a new API instance if a selector is given.
7322
+ */
7323
+ _api_register( 'tables()', function ( selector ) {
7324
+ // A new instance is created if there was a selector specified
7325
+ return selector ?
7326
+ new _Api( __table_selector( selector, this.context ) ) :
7327
+ this;
7328
+ } );
7329
+
7330
+
7331
+ _api_register( 'table()', function ( selector ) {
7332
+ var tables = this.tables( selector );
7333
+ var ctx = tables.context;
7334
+
7335
+ // Truncate to the first matched table
7336
+ return ctx.length ?
7337
+ new _Api( ctx[0] ) :
7338
+ tables;
7339
+ } );
7340
+
7341
+
7342
+ _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
7343
+ return this.iterator( 'table', function ( ctx ) {
7344
+ return ctx.nTable;
7345
+ }, 1 );
7346
+ } );
7347
+
7348
+
7349
+ _api_registerPlural( 'tables().body()', 'table().body()' , function () {
7350
+ return this.iterator( 'table', function ( ctx ) {
7351
+ return ctx.nTBody;
7352
+ }, 1 );
7353
+ } );
7354
+
7355
+
7356
+ _api_registerPlural( 'tables().header()', 'table().header()' , function () {
7357
+ return this.iterator( 'table', function ( ctx ) {
7358
+ return ctx.nTHead;
7359
+ }, 1 );
7360
+ } );
7361
+
7362
+
7363
+ _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
7364
+ return this.iterator( 'table', function ( ctx ) {
7365
+ return ctx.nTFoot;
7366
+ }, 1 );
7367
+ } );
7368
+
7369
+
7370
+ _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
7371
+ return this.iterator( 'table', function ( ctx ) {
7372
+ return ctx.nTableWrapper;
7373
+ }, 1 );
7374
+ } );
7375
+
7376
+
7377
+
7378
+ /**
7379
+ * Redraw the tables in the current context.
7380
+ */
7381
+ _api_register( 'draw()', function ( paging ) {
7382
+ return this.iterator( 'table', function ( settings ) {
7383
+ if ( paging === 'page' ) {
7384
+ _fnDraw( settings );
7385
+ }
7386
+ else {
7387
+ if ( typeof paging === 'string' ) {
7388
+ paging = paging === 'full-hold' ?
7389
+ false :
7390
+ true;
7391
+ }
7392
+
7393
+ _fnReDraw( settings, paging===false );
7394
+ }
7395
+ } );
7396
+ } );
7397
+
7398
+
7399
+
7400
+ /**
7401
+ * Get the current page index.
7402
+ *
7403
+ * @return {integer} Current page index (zero based)
7404
+ *//**
7405
+ * Set the current page.
7406
+ *
7407
+ * Note that if you attempt to show a page which does not exist, DataTables will
7408
+ * not throw an error, but rather reset the paging.
7409
+ *
7410
+ * @param {integer|string} action The paging action to take. This can be one of:
7411
+ * * `integer` - The page index to jump to
7412
+ * * `string` - An action to take:
7413
+ * * `first` - Jump to first page.
7414
+ * * `next` - Jump to the next page
7415
+ * * `previous` - Jump to previous page
7416
+ * * `last` - Jump to the last page.
7417
+ * @returns {DataTables.Api} this
7418
+ */
7419
+ _api_register( 'page()', function ( action ) {
7420
+ if ( action === undefined ) {
7421
+ return this.page.info().page; // not an expensive call
7422
+ }
7423
+
7424
+ // else, have an action to take on all tables
7425
+ return this.iterator( 'table', function ( settings ) {
7426
+ _fnPageChange( settings, action );
7427
+ } );
7428
+ } );
7429
+
7430
+
7431
+ /**
7432
+ * Paging information for the first table in the current context.
7433
+ *
7434
+ * If you require paging information for another table, use the `table()` method
7435
+ * with a suitable selector.
7436
+ *
7437
+ * @return {object} Object with the following properties set:
7438
+ * * `page` - Current page index (zero based - i.e. the first page is `0`)
7439
+ * * `pages` - Total number of pages
7440
+ * * `start` - Display index for the first record shown on the current page
7441
+ * * `end` - Display index for the last record shown on the current page
7442
+ * * `length` - Display length (number of records). Note that generally `start
7443
+ * + length = end`, but this is not always true, for example if there are
7444
+ * only 2 records to show on the final page, with a length of 10.
7445
+ * * `recordsTotal` - Full data set length
7446
+ * * `recordsDisplay` - Data set length once the current filtering criterion
7447
+ * are applied.
7448
+ */
7449
+ _api_register( 'page.info()', function ( action ) {
7450
+ if ( this.context.length === 0 ) {
7451
+ return undefined;
7452
+ }
7453
+
7454
+ var
7455
+ settings = this.context[0],
7456
+ start = settings._iDisplayStart,
7457
+ len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
7458
+ visRecords = settings.fnRecordsDisplay(),
7459
+ all = len === -1;
7460
+
7461
+ return {
7462
+ "page": all ? 0 : Math.floor( start / len ),
7463
+ "pages": all ? 1 : Math.ceil( visRecords / len ),
7464
+ "start": start,
7465
+ "end": settings.fnDisplayEnd(),
7466
+ "length": len,
7467
+ "recordsTotal": settings.fnRecordsTotal(),
7468
+ "recordsDisplay": visRecords,
7469
+ "serverSide": _fnDataSource( settings ) === 'ssp'
7470
+ };
7471
+ } );
7472
+
7473
+
7474
+ /**
7475
+ * Get the current page length.
7476
+ *
7477
+ * @return {integer} Current page length. Note `-1` indicates that all records
7478
+ * are to be shown.
7479
+ *//**
7480
+ * Set the current page length.
7481
+ *
7482
+ * @param {integer} Page length to set. Use `-1` to show all records.
7483
+ * @returns {DataTables.Api} this
7484
+ */
7485
+ _api_register( 'page.len()', function ( len ) {
7486
+ // Note that we can't call this function 'length()' because `length`
7487
+ // is a Javascript property of functions which defines how many arguments
7488
+ // the function expects.
7489
+ if ( len === undefined ) {
7490
+ return this.context.length !== 0 ?
7491
+ this.context[0]._iDisplayLength :
7492
+ undefined;
7493
+ }
7494
+
7495
+ // else, set the page length
7496
+ return this.iterator( 'table', function ( settings ) {
7497
+ _fnLengthChange( settings, len );
7498
+ } );
7499
+ } );
7500
+
7501
+
7502
+
7503
+ var __reload = function ( settings, holdPosition, callback ) {
7504
+ // Use the draw event to trigger a callback
7505
+ if ( callback ) {
7506
+ var api = new _Api( settings );
7507
+
7508
+ api.one( 'draw', function () {
7509
+ callback( api.ajax.json() );
7510
+ } );
7511
+ }
7512
+
7513
+ if ( _fnDataSource( settings ) == 'ssp' ) {
7514
+ _fnReDraw( settings, holdPosition );
7515
+ }
7516
+ else {
7517
+ _fnProcessingDisplay( settings, true );
7518
+
7519
+ // Cancel an existing request
7520
+ var xhr = settings.jqXHR;
7521
+ if ( xhr && xhr.readyState !== 4 ) {
7522
+ xhr.abort();
7523
+ }
7524
+
7525
+ // Trigger xhr
7526
+ _fnBuildAjax( settings, [], function( json ) {
7527
+ _fnClearTable( settings );
7528
+
7529
+ var data = _fnAjaxDataSrc( settings, json );
7530
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7531
+ _fnAddData( settings, data[i] );
7532
+ }
7533
+
7534
+ _fnReDraw( settings, holdPosition );
7535
+ _fnProcessingDisplay( settings, false );
7536
+ } );
7537
+ }
7538
+ };
7539
+
7540
+
7541
+ /**
7542
+ * Get the JSON response from the last Ajax request that DataTables made to the
7543
+ * server. Note that this returns the JSON from the first table in the current
7544
+ * context.
7545
+ *
7546
+ * @return {object} JSON received from the server.
7547
+ */
7548
+ _api_register( 'ajax.json()', function () {
7549
+ var ctx = this.context;
7550
+
7551
+ if ( ctx.length > 0 ) {
7552
+ return ctx[0].json;
7553
+ }
7554
+
7555
+ // else return undefined;
7556
+ } );
7557
+
7558
+
7559
+ /**
7560
+ * Get the data submitted in the last Ajax request
7561
+ */
7562
+ _api_register( 'ajax.params()', function () {
7563
+ var ctx = this.context;
7564
+
7565
+ if ( ctx.length > 0 ) {
7566
+ return ctx[0].oAjaxData;
7567
+ }
7568
+
7569
+ // else return undefined;
7570
+ } );
7571
+
7572
+
7573
+ /**
7574
+ * Reload tables from the Ajax data source. Note that this function will
7575
+ * automatically re-draw the table when the remote data has been loaded.
7576
+ *
7577
+ * @param {boolean} [reset=true] Reset (default) or hold the current paging
7578
+ * position. A full re-sort and re-filter is performed when this method is
7579
+ * called, which is why the pagination reset is the default action.
7580
+ * @returns {DataTables.Api} this
7581
+ */
7582
+ _api_register( 'ajax.reload()', function ( callback, resetPaging ) {
7583
+ return this.iterator( 'table', function (settings) {
7584
+ __reload( settings, resetPaging===false, callback );
7585
+ } );
7586
+ } );
7587
+
7588
+
7589
+ /**
7590
+ * Get the current Ajax URL. Note that this returns the URL from the first
7591
+ * table in the current context.
7592
+ *
7593
+ * @return {string} Current Ajax source URL
7594
+ *//**
7595
+ * Set the Ajax URL. Note that this will set the URL for all tables in the
7596
+ * current context.
7597
+ *
7598
+ * @param {string} url URL to set.
7599
+ * @returns {DataTables.Api} this
7600
+ */
7601
+ _api_register( 'ajax.url()', function ( url ) {
7602
+ var ctx = this.context;
7603
+
7604
+ if ( url === undefined ) {
7605
+ // get
7606
+ if ( ctx.length === 0 ) {
7607
+ return undefined;
7608
+ }
7609
+ ctx = ctx[0];
7610
+
7611
+ return ctx.ajax ?
7612
+ $.isPlainObject( ctx.ajax ) ?
7613
+ ctx.ajax.url :
7614
+ ctx.ajax :
7615
+ ctx.sAjaxSource;
7616
+ }
7617
+
7618
+ // set
7619
+ return this.iterator( 'table', function ( settings ) {
7620
+ if ( $.isPlainObject( settings.ajax ) ) {
7621
+ settings.ajax.url = url;
7622
+ }
7623
+ else {
7624
+ settings.ajax = url;
7625
+ }
7626
+ // No need to consider sAjaxSource here since DataTables gives priority
7627
+ // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
7628
+ // value of `sAjaxSource` redundant.
7629
+ } );
7630
+ } );
7631
+
7632
+
7633
+ /**
7634
+ * Load data from the newly set Ajax URL. Note that this method is only
7635
+ * available when `ajax.url()` is used to set a URL. Additionally, this method
7636
+ * has the same effect as calling `ajax.reload()` but is provided for
7637
+ * convenience when setting a new URL. Like `ajax.reload()` it will
7638
+ * automatically redraw the table once the remote data has been loaded.
7639
+ *
7640
+ * @returns {DataTables.Api} this
7641
+ */
7642
+ _api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
7643
+ // Same as a reload, but makes sense to present it for easy access after a
7644
+ // url change
7645
+ return this.iterator( 'table', function ( ctx ) {
7646
+ __reload( ctx, resetPaging===false, callback );
7647
+ } );
7648
+ } );
7649
+
7650
+
7651
+
7652
+
7653
+ var _selector_run = function ( type, selector, selectFn, settings, opts )
7654
+ {
7655
+ var
7656
+ out = [], res,
7657
+ a, i, ien, j, jen,
7658
+ selectorType = typeof selector;
7659
+
7660
+ // Can't just check for isArray here, as an API or jQuery instance might be
7661
+ // given with their array like look
7662
+ if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
7663
+ selector = [ selector ];
7664
+ }
7665
+
7666
+ for ( i=0, ien=selector.length ; i<ien ; i++ ) {
7667
+ a = selector[i] && selector[i].split ?
7668
+ selector[i].split(',') :
7669
+ [ selector[i] ];
7670
+
7671
+ for ( j=0, jen=a.length ; j<jen ; j++ ) {
7672
+ res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
7673
+
7674
+ if ( res && res.length ) {
7675
+ out = out.concat( res );
7676
+ }
7677
+ }
7678
+ }
7679
+
7680
+ // selector extensions
7681
+ var ext = _ext.selector[ type ];
7682
+ if ( ext.length ) {
7683
+ for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7684
+ out = ext[i]( settings, opts, out );
7685
+ }
7686
+ }
7687
+
7688
+ return _unique( out );
7689
+ };
7690
+
7691
+
7692
+ var _selector_opts = function ( opts )
7693
+ {
7694
+ if ( ! opts ) {
7695
+ opts = {};
7696
+ }
7697
+
7698
+ // Backwards compatibility for 1.9- which used the terminology filter rather
7699
+ // than search
7700
+ if ( opts.filter && opts.search === undefined ) {
7701
+ opts.search = opts.filter;
7702
+ }
7703
+
7704
+ return $.extend( {
7705
+ search: 'none',
7706
+ order: 'current',
7707
+ page: 'all'
7708
+ }, opts );
7709
+ };
7710
+
7711
+
7712
+ var _selector_first = function ( inst )
7713
+ {
7714
+ // Reduce the API instance to the first item found
7715
+ for ( var i=0, ien=inst.length ; i<ien ; i++ ) {
7716
+ if ( inst[i].length > 0 ) {
7717
+ // Assign the first element to the first item in the instance
7718
+ // and truncate the instance and context
7719
+ inst[0] = inst[i];
7720
+ inst[0].length = 1;
7721
+ inst.length = 1;
7722
+ inst.context = [ inst.context[i] ];
7723
+
7724
+ return inst;
7725
+ }
7726
+ }
7727
+
7728
+ // Not found - return an empty instance
7729
+ inst.length = 0;
7730
+ return inst;
7731
+ };
7732
+
7733
+
7734
+ var _selector_row_indexes = function ( settings, opts )
7735
+ {
7736
+ var
7737
+ i, ien, tmp, a=[],
7738
+ displayFiltered = settings.aiDisplay,
7739
+ displayMaster = settings.aiDisplayMaster;
7740
+
7741
+ var
7742
+ search = opts.search, // none, applied, removed
7743
+ order = opts.order, // applied, current, index (original - compatibility with 1.9)
7744
+ page = opts.page; // all, current
7745
+
7746
+ if ( _fnDataSource( settings ) == 'ssp' ) {
7747
+ // In server-side processing mode, most options are irrelevant since
7748
+ // rows not shown don't exist and the index order is the applied order
7749
+ // Removed is a special case - for consistency just return an empty
7750
+ // array
7751
+ return search === 'removed' ?
7752
+ [] :
7753
+ _range( 0, displayMaster.length );
7754
+ }
7755
+ else if ( page == 'current' ) {
7756
+ // Current page implies that order=current and fitler=applied, since it is
7757
+ // fairly senseless otherwise, regardless of what order and search actually
7758
+ // are
7759
+ for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
7760
+ a.push( displayFiltered[i] );
7761
+ }
7762
+ }
7763
+ else if ( order == 'current' || order == 'applied' ) {
7764
+ a = search == 'none' ?
7765
+ displayMaster.slice() : // no search
7766
+ search == 'applied' ?
7767
+ displayFiltered.slice() : // applied search
7768
+ $.map( displayMaster, function (el, i) { // removed search
7769
+ return $.inArray( el, displayFiltered ) === -1 ? el : null;
7770
+ } );
7771
+ }
7772
+ else if ( order == 'index' || order == 'original' ) {
7773
+ for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
7774
+ if ( search == 'none' ) {
7775
+ a.push( i );
7776
+ }
7777
+ else { // applied | removed
7778
+ tmp = $.inArray( i, displayFiltered );
7779
+
7780
+ if ((tmp === -1 && search == 'removed') ||
7781
+ (tmp >= 0 && search == 'applied') )
7782
+ {
7783
+ a.push( i );
7784
+ }
7785
+ }
7786
+ }
7787
+ }
7788
+
7789
+ return a;
7790
+ };
7791
+
7792
+
7793
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7794
+ * Rows
7795
+ *
7796
+ * {} - no selector - use all available rows
7797
+ * {integer} - row aoData index
7798
+ * {node} - TR node
7799
+ * {string} - jQuery selector to apply to the TR elements
7800
+ * {array} - jQuery array of nodes, or simply an array of TR nodes
7801
+ *
7802
+ */
7803
+
7804
+
7805
+ var __row_selector = function ( settings, selector, opts )
7806
+ {
7807
+ var run = function ( sel ) {
7808
+ var selInt = _intVal( sel );
7809
+ var i, ien;
7810
+
7811
+ // Short cut - selector is a number and no options provided (default is
7812
+ // all records, so no need to check if the index is in there, since it
7813
+ // must be - dev error if the index doesn't exist).
7814
+ if ( selInt !== null && ! opts ) {
7815
+ return [ selInt ];
7816
+ }
7817
+
7818
+ var rows = _selector_row_indexes( settings, opts );
7819
+
7820
+ if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
7821
+ // Selector - integer
7822
+ return [ selInt ];
7823
+ }
7824
+ else if ( ! sel ) {
7825
+ // Selector - none
7826
+ return rows;
7827
+ }
7828
+
7829
+ // Selector - function
7830
+ if ( typeof sel === 'function' ) {
7831
+ return $.map( rows, function (idx) {
7832
+ var row = settings.aoData[ idx ];
7833
+ return sel( idx, row._aData, row.nTr ) ? idx : null;
7834
+ } );
7835
+ }
7836
+
7837
+ // Get nodes in the order from the `rows` array with null values removed
7838
+ var nodes = _removeEmpty(
7839
+ _pluck_order( settings.aoData, rows, 'nTr' )
7840
+ );
7841
+
7842
+ // Selector - node
7843
+ if ( sel.nodeName ) {
7844
+ if ( sel._DT_RowIndex !== undefined ) {
7845
+ return [ sel._DT_RowIndex ]; // Property added by DT for fast lookup
7846
+ }
7847
+ else if ( sel._DT_CellIndex ) {
7848
+ return [ sel._DT_CellIndex.row ];
7849
+ }
7850
+ else {
7851
+ var host = $(sel).closest('*[data-dt-row]');
7852
+ return host.length ?
7853
+ [ host.data('dt-row') ] :
7854
+ [];
7855
+ }
7856
+ }
7857
+
7858
+ // ID selector. Want to always be able to select rows by id, regardless
7859
+ // of if the tr element has been created or not, so can't rely upon
7860
+ // jQuery here - hence a custom implementation. This does not match
7861
+ // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
7862
+ // but to select it using a CSS selector engine (like Sizzle or
7863
+ // querySelect) it would need to need to be escaped for some characters.
7864
+ // DataTables simplifies this for row selectors since you can select
7865
+ // only a row. A # indicates an id any anything that follows is the id -
7866
+ // unescaped.
7867
+ if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
7868
+ // get row index from id
7869
+ var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
7870
+ if ( rowObj !== undefined ) {
7871
+ return [ rowObj.idx ];
7872
+ }
7873
+
7874
+ // need to fall through to jQuery in case there is DOM id that
7875
+ // matches
7876
+ }
7877
+
7878
+ // Selector - jQuery selector string, array of nodes or jQuery object/
7879
+ // As jQuery's .filter() allows jQuery objects to be passed in filter,
7880
+ // it also allows arrays, so this will cope with all three options
7881
+ return $(nodes)
7882
+ .filter( sel )
7883
+ .map( function () {
7884
+ return this._DT_RowIndex;
7885
+ } )
7886
+ .toArray();
7887
+ };
7888
+
7889
+ return _selector_run( 'row', selector, run, settings, opts );
7890
+ };
7891
+
7892
+
7893
+ _api_register( 'rows()', function ( selector, opts ) {
7894
+ // argument shifting
7895
+ if ( selector === undefined ) {
7896
+ selector = '';
7897
+ }
7898
+ else if ( $.isPlainObject( selector ) ) {
7899
+ opts = selector;
7900
+ selector = '';
7901
+ }
7902
+
7903
+ opts = _selector_opts( opts );
7904
+
7905
+ var inst = this.iterator( 'table', function ( settings ) {
7906
+ return __row_selector( settings, selector, opts );
7907
+ }, 1 );
7908
+
7909
+ // Want argument shifting here and in __row_selector?
7910
+ inst.selector.rows = selector;
7911
+ inst.selector.opts = opts;
7912
+
7913
+ return inst;
7914
+ } );
7915
+
7916
+ _api_register( 'rows().nodes()', function () {
7917
+ return this.iterator( 'row', function ( settings, row ) {
7918
+ return settings.aoData[ row ].nTr || undefined;
7919
+ }, 1 );
7920
+ } );
7921
+
7922
+ _api_register( 'rows().data()', function () {
7923
+ return this.iterator( true, 'rows', function ( settings, rows ) {
7924
+ return _pluck_order( settings.aoData, rows, '_aData' );
7925
+ }, 1 );
7926
+ } );
7927
+
7928
+ _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
7929
+ return this.iterator( 'row', function ( settings, row ) {
7930
+ var r = settings.aoData[ row ];
7931
+ return type === 'search' ? r._aFilterData : r._aSortData;
7932
+ }, 1 );
7933
+ } );
7934
+
7935
+ _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
7936
+ return this.iterator( 'row', function ( settings, row ) {
7937
+ _fnInvalidate( settings, row, src );
7938
+ } );
7939
+ } );
7940
+
7941
+ _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
7942
+ return this.iterator( 'row', function ( settings, row ) {
7943
+ return row;
7944
+ }, 1 );
7945
+ } );
7946
+
7947
+ _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
7948
+ var a = [];
7949
+ var context = this.context;
7950
+
7951
+ // `iterator` will drop undefined values, but in this case we want them
7952
+ for ( var i=0, ien=context.length ; i<ien ; i++ ) {
7953
+ for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
7954
+ var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
7955
+ a.push( (hash === true ? '#' : '' )+ id );
7956
+ }
7957
+ }
7958
+
7959
+ return new _Api( context, a );
7960
+ } );
7961
+
7962
+ _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
7963
+ var that = this;
7964
+
7965
+ this.iterator( 'row', function ( settings, row, thatIdx ) {
7966
+ var data = settings.aoData;
7967
+ var rowData = data[ row ];
7968
+ var i, ien, j, jen;
7969
+ var loopRow, loopCells;
7970
+
7971
+ data.splice( row, 1 );
7972
+
7973
+ // Update the cached indexes
7974
+ for ( i=0, ien=data.length ; i<ien ; i++ ) {
7975
+ loopRow = data[i];
7976
+ loopCells = loopRow.anCells;
7977
+
7978
+ // Rows
7979
+ if ( loopRow.nTr !== null ) {
7980
+ loopRow.nTr._DT_RowIndex = i;
7981
+ }
7982
+
7983
+ // Cells
7984
+ if ( loopCells !== null ) {
7985
+ for ( j=0, jen=loopCells.length ; j<jen ; j++ ) {
7986
+ loopCells[j]._DT_CellIndex.row = i;
7987
+ }
7988
+ }
7989
+ }
7990
+
7991
+ // Delete from the display arrays
7992
+ _fnDeleteIndex( settings.aiDisplayMaster, row );
7993
+ _fnDeleteIndex( settings.aiDisplay, row );
7994
+ _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
7995
+
7996
+ // Check for an 'overflow' they case for displaying the table
7997
+ _fnLengthOverflow( settings );
7998
+
7999
+ // Remove the row's ID reference if there is one
8000
+ var id = settings.rowIdFn( rowData._aData );
8001
+ if ( id !== undefined ) {
8002
+ delete settings.aIds[ id ];
8003
+ }
8004
+ } );
8005
+
8006
+ this.iterator( 'table', function ( settings ) {
8007
+ for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
8008
+ settings.aoData[i].idx = i;
8009
+ }
8010
+ } );
8011
+
8012
+ return this;
8013
+ } );
8014
+
8015
+
8016
+ _api_register( 'rows.add()', function ( rows ) {
8017
+ var newRows = this.iterator( 'table', function ( settings ) {
8018
+ var row, i, ien;
8019
+ var out = [];
8020
+
8021
+ for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8022
+ row = rows[i];
8023
+
8024
+ if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8025
+ out.push( _fnAddTr( settings, row )[0] );
8026
+ }
8027
+ else {
8028
+ out.push( _fnAddData( settings, row ) );
8029
+ }
8030
+ }
8031
+
8032
+ return out;
8033
+ }, 1 );
8034
+
8035
+ // Return an Api.rows() extended instance, so rows().nodes() etc can be used
8036
+ var modRows = this.rows( -1 );
8037
+ modRows.pop();
8038
+ $.merge( modRows, newRows );
8039
+
8040
+ return modRows;
8041
+ } );
8042
+
8043
+
8044
+
8045
+
8046
+
8047
+ /**
8048
+ *
8049
+ */
8050
+ _api_register( 'row()', function ( selector, opts ) {
8051
+ return _selector_first( this.rows( selector, opts ) );
8052
+ } );
8053
+
8054
+
8055
+ _api_register( 'row().data()', function ( data ) {
8056
+ var ctx = this.context;
8057
+
8058
+ if ( data === undefined ) {
8059
+ // Get
8060
+ return ctx.length && this.length ?
8061
+ ctx[0].aoData[ this[0] ]._aData :
8062
+ undefined;
8063
+ }
8064
+
8065
+ // Set
8066
+ ctx[0].aoData[ this[0] ]._aData = data;
8067
+
8068
+ // Automatically invalidate
8069
+ _fnInvalidate( ctx[0], this[0], 'data' );
8070
+
8071
+ return this;
8072
+ } );
8073
+
8074
+
8075
+ _api_register( 'row().node()', function () {
8076
+ var ctx = this.context;
8077
+
8078
+ return ctx.length && this.length ?
8079
+ ctx[0].aoData[ this[0] ].nTr || null :
8080
+ null;
8081
+ } );
8082
+
8083
+
8084
+ _api_register( 'row.add()', function ( row ) {
8085
+ // Allow a jQuery object to be passed in - only a single row is added from
8086
+ // it though - the first element in the set
8087
+ if ( row instanceof $ && row.length ) {
8088
+ row = row[0];
8089
+ }
8090
+
8091
+ var rows = this.iterator( 'table', function ( settings ) {
8092
+ if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8093
+ return _fnAddTr( settings, row )[0];
8094
+ }
8095
+ return _fnAddData( settings, row );
8096
+ } );
8097
+
8098
+ // Return an Api.rows() extended instance, with the newly added row selected
8099
+ return this.row( rows[0] );
8100
+ } );
8101
+
8102
+
8103
+
8104
+ var __details_add = function ( ctx, row, data, klass )
8105
+ {
8106
+ // Convert to array of TR elements
8107
+ var rows = [];
8108
+ var addRow = function ( r, k ) {
8109
+ // Recursion to allow for arrays of jQuery objects
8110
+ if ( $.isArray( r ) || r instanceof $ ) {
8111
+ for ( var i=0, ien=r.length ; i<ien ; i++ ) {
8112
+ addRow( r[i], k );
8113
+ }
8114
+ return;
8115
+ }
8116
+
8117
+ // If we get a TR element, then just add it directly - up to the dev
8118
+ // to add the correct number of columns etc
8119
+ if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
8120
+ rows.push( r );
8121
+ }
8122
+ else {
8123
+ // Otherwise create a row with a wrapper
8124
+ var created = $('<tr><td/></tr>').addClass( k );
8125
+ $('td', created)
8126
+ .addClass( k )
8127
+ .html( r )
8128
+ [0].colSpan = _fnVisbleColumns( ctx );
8129
+
8130
+ rows.push( created[0] );
8131
+ }
8132
+ };
8133
+
8134
+ addRow( data, klass );
8135
+
8136
+ if ( row._details ) {
8137
+ row._details.remove();
8138
+ }
8139
+
8140
+ row._details = $(rows);
8141
+
8142
+ // If the children were already shown, that state should be retained
8143
+ if ( row._detailsShow ) {
8144
+ row._details.insertAfter( row.nTr );
8145
+ }
8146
+ };
8147
+
8148
+
8149
+ var __details_remove = function ( api, idx )
8150
+ {
8151
+ var ctx = api.context;
8152
+
8153
+ if ( ctx.length ) {
8154
+ var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
8155
+
8156
+ if ( row && row._details ) {
8157
+ row._details.remove();
8158
+
8159
+ row._detailsShow = undefined;
8160
+ row._details = undefined;
8161
+ }
8162
+ }
8163
+ };
8164
+
8165
+
8166
+ var __details_display = function ( api, show ) {
8167
+ var ctx = api.context;
8168
+
8169
+ if ( ctx.length && api.length ) {
8170
+ var row = ctx[0].aoData[ api[0] ];
8171
+
8172
+ if ( row._details ) {
8173
+ row._detailsShow = show;
8174
+
8175
+ if ( show ) {
8176
+ row._details.insertAfter( row.nTr );
8177
+ }
8178
+ else {
8179
+ row._details.detach();
8180
+ }
8181
+
8182
+ __details_events( ctx[0] );
8183
+ }
8184
+ }
8185
+ };
8186
+
8187
+
8188
+ var __details_events = function ( settings )
8189
+ {
8190
+ var api = new _Api( settings );
8191
+ var namespace = '.dt.DT_details';
8192
+ var drawEvent = 'draw'+namespace;
8193
+ var colvisEvent = 'column-visibility'+namespace;
8194
+ var destroyEvent = 'destroy'+namespace;
8195
+ var data = settings.aoData;
8196
+
8197
+ api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
8198
+
8199
+ if ( _pluck( data, '_details' ).length > 0 ) {
8200
+ // On each draw, insert the required elements into the document
8201
+ api.on( drawEvent, function ( e, ctx ) {
8202
+ if ( settings !== ctx ) {
8203
+ return;
8204
+ }
8205
+
8206
+ api.rows( {page:'current'} ).eq(0).each( function (idx) {
8207
+ // Internal data grab
8208
+ var row = data[ idx ];
8209
+
8210
+ if ( row._detailsShow ) {
8211
+ row._details.insertAfter( row.nTr );
8212
+ }
8213
+ } );
8214
+ } );
8215
+
8216
+ // Column visibility change - update the colspan
8217
+ api.on( colvisEvent, function ( e, ctx, idx, vis ) {
8218
+ if ( settings !== ctx ) {
8219
+ return;
8220
+ }
8221
+
8222
+ // Update the colspan for the details rows (note, only if it already has
8223
+ // a colspan)
8224
+ var row, visible = _fnVisbleColumns( ctx );
8225
+
8226
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8227
+ row = data[i];
8228
+
8229
+ if ( row._details ) {
8230
+ row._details.children('td[colspan]').attr('colspan', visible );
8231
+ }
8232
+ }
8233
+ } );
8234
+
8235
+ // Table destroyed - nuke any child rows
8236
+ api.on( destroyEvent, function ( e, ctx ) {
8237
+ if ( settings !== ctx ) {
8238
+ return;
8239
+ }
8240
+
8241
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8242
+ if ( data[i]._details ) {
8243
+ __details_remove( api, i );
8244
+ }
8245
+ }
8246
+ } );
8247
+ }
8248
+ };
8249
+
8250
+ // Strings for the method names to help minification
8251
+ var _emp = '';
8252
+ var _child_obj = _emp+'row().child';
8253
+ var _child_mth = _child_obj+'()';
8254
+
8255
+ // data can be:
8256
+ // tr
8257
+ // string
8258
+ // jQuery or array of any of the above
8259
+ _api_register( _child_mth, function ( data, klass ) {
8260
+ var ctx = this.context;
8261
+
8262
+ if ( data === undefined ) {
8263
+ // get
8264
+ return ctx.length && this.length ?
8265
+ ctx[0].aoData[ this[0] ]._details :
8266
+ undefined;
8267
+ }
8268
+ else if ( data === true ) {
8269
+ // show
8270
+ this.child.show();
8271
+ }
8272
+ else if ( data === false ) {
8273
+ // remove
8274
+ __details_remove( this );
8275
+ }
8276
+ else if ( ctx.length && this.length ) {
8277
+ // set
8278
+ __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
8279
+ }
8280
+
8281
+ return this;
8282
+ } );
8283
+
8284
+
8285
+ _api_register( [
8286
+ _child_obj+'.show()',
8287
+ _child_mth+'.show()' // only when `child()` was called with parameters (without
8288
+ ], function ( show ) { // it returns an object and this method is not executed)
8289
+ __details_display( this, true );
8290
+ return this;
8291
+ } );
8292
+
8293
+
8294
+ _api_register( [
8295
+ _child_obj+'.hide()',
8296
+ _child_mth+'.hide()' // only when `child()` was called with parameters (without
8297
+ ], function () { // it returns an object and this method is not executed)
8298
+ __details_display( this, false );
8299
+ return this;
8300
+ } );
8301
+
8302
+
8303
+ _api_register( [
8304
+ _child_obj+'.remove()',
8305
+ _child_mth+'.remove()' // only when `child()` was called with parameters (without
8306
+ ], function () { // it returns an object and this method is not executed)
8307
+ __details_remove( this );
8308
+ return this;
8309
+ } );
8310
+
8311
+
8312
+ _api_register( _child_obj+'.isShown()', function () {
8313
+ var ctx = this.context;
8314
+
8315
+ if ( ctx.length && this.length ) {
8316
+ // _detailsShown as false or undefined will fall through to return false
8317
+ return ctx[0].aoData[ this[0] ]._detailsShow || false;
8318
+ }
8319
+ return false;
8320
+ } );
8321
+
8322
+
8323
+
8324
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8325
+ * Columns
8326
+ *
8327
+ * {integer} - column index (>=0 count from left, <0 count from right)
8328
+ * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
8329
+ * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
8330
+ * "{string}:name" - column name
8331
+ * "{string}" - jQuery selector on column header nodes
8332
+ *
8333
+ */
8334
+
8335
+ // can be an array of these items, comma separated list, or an array of comma
8336
+ // separated lists
8337
+
8338
+ var __re_column_selector = /^(.+):(name|visIdx|visible)$/;
8339
+
8340
+
8341
+ // r1 and r2 are redundant - but it means that the parameters match for the
8342
+ // iterator callback in columns().data()
8343
+ var __columnData = function ( settings, column, r1, r2, rows ) {
8344
+ var a = [];
8345
+ for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
8346
+ a.push( _fnGetCellData( settings, rows[row], column ) );
8347
+ }
8348
+ return a;
8349
+ };
8350
+
8351
+
8352
+ var __column_selector = function ( settings, selector, opts )
8353
+ {
8354
+ var
8355
+ columns = settings.aoColumns,
8356
+ names = _pluck( columns, 'sName' ),
8357
+ nodes = _pluck( columns, 'nTh' );
8358
+
8359
+ var run = function ( s ) {
8360
+ var selInt = _intVal( s );
8361
+
8362
+ // Selector - all
8363
+ if ( s === '' ) {
8364
+ return _range( columns.length );
8365
+ }
8366
+
8367
+ // Selector - index
8368
+ if ( selInt !== null ) {
8369
+ return [ selInt >= 0 ?
8370
+ selInt : // Count from left
8371
+ columns.length + selInt // Count from right (+ because its a negative value)
8372
+ ];
8373
+ }
8374
+
8375
+ // Selector = function
8376
+ if ( typeof s === 'function' ) {
8377
+ var rows = _selector_row_indexes( settings, opts );
8378
+
8379
+ return $.map( columns, function (col, idx) {
8380
+ return s(
8381
+ idx,
8382
+ __columnData( settings, idx, 0, 0, rows ),
8383
+ nodes[ idx ]
8384
+ ) ? idx : null;
8385
+ } );
8386
+ }
8387
+
8388
+ // jQuery or string selector
8389
+ var match = typeof s === 'string' ?
8390
+ s.match( __re_column_selector ) :
8391
+ '';
8392
+
8393
+ if ( match ) {
8394
+ switch( match[2] ) {
8395
+ case 'visIdx':
8396
+ case 'visible':
8397
+ var idx = parseInt( match[1], 10 );
8398
+ // Visible index given, convert to column index
8399
+ if ( idx < 0 ) {
8400
+ // Counting from the right
8401
+ var visColumns = $.map( columns, function (col,i) {
8402
+ return col.bVisible ? i : null;
8403
+ } );
8404
+ return [ visColumns[ visColumns.length + idx ] ];
8405
+ }
8406
+ // Counting from the left
8407
+ return [ _fnVisibleToColumnIndex( settings, idx ) ];
8408
+
8409
+ case 'name':
8410
+ // match by name. `names` is column index complete and in order
8411
+ return $.map( names, function (name, i) {
8412
+ return name === match[1] ? i : null;
8413
+ } );
8414
+
8415
+ default:
8416
+ return [];
8417
+ }
8418
+ }
8419
+
8420
+ // Cell in the table body
8421
+ if ( s.nodeName && s._DT_CellIndex ) {
8422
+ return [ s._DT_CellIndex.column ];
8423
+ }
8424
+
8425
+ // jQuery selector on the TH elements for the columns
8426
+ var jqResult = $( nodes )
8427
+ .filter( s )
8428
+ .map( function () {
8429
+ return $.inArray( this, nodes ); // `nodes` is column index complete and in order
8430
+ } )
8431
+ .toArray();
8432
+
8433
+ if ( jqResult.length || ! s.nodeName ) {
8434
+ return jqResult;
8435
+ }
8436
+
8437
+ // Otherwise a node which might have a `dt-column` data attribute, or be
8438
+ // a child or such an element
8439
+ var host = $(s).closest('*[data-dt-column]');
8440
+ return host.length ?
8441
+ [ host.data('dt-column') ] :
8442
+ [];
8443
+ };
8444
+
8445
+ return _selector_run( 'column', selector, run, settings, opts );
8446
+ };
8447
+
8448
+
8449
+ var __setColumnVis = function ( settings, column, vis ) {
8450
+ var
8451
+ cols = settings.aoColumns,
8452
+ col = cols[ column ],
8453
+ data = settings.aoData,
8454
+ row, cells, i, ien, tr;
8455
+
8456
+ // Get
8457
+ if ( vis === undefined ) {
8458
+ return col.bVisible;
8459
+ }
8460
+
8461
+ // Set
8462
+ // No change
8463
+ if ( col.bVisible === vis ) {
8464
+ return;
8465
+ }
8466
+
8467
+ if ( vis ) {
8468
+ // Insert column
8469
+ // Need to decide if we should use appendChild or insertBefore
8470
+ var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
8471
+
8472
+ for ( i=0, ien=data.length ; i<ien ; i++ ) {
8473
+ tr = data[i].nTr;
8474
+ cells = data[i].anCells;
8475
+
8476
+ if ( tr ) {
8477
+ // insertBefore can act like appendChild if 2nd arg is null
8478
+ tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
8479
+ }
8480
+ }
8481
+ }
8482
+ else {
8483
+ // Remove column
8484
+ $( _pluck( settings.aoData, 'anCells', column ) ).detach();
8485
+ }
8486
+
8487
+ // Common actions
8488
+ col.bVisible = vis;
8489
+ _fnDrawHead( settings, settings.aoHeader );
8490
+ _fnDrawHead( settings, settings.aoFooter );
8491
+
8492
+ _fnSaveState( settings );
8493
+ };
8494
+
8495
+
8496
+ _api_register( 'columns()', function ( selector, opts ) {
8497
+ // argument shifting
8498
+ if ( selector === undefined ) {
8499
+ selector = '';
8500
+ }
8501
+ else if ( $.isPlainObject( selector ) ) {
8502
+ opts = selector;
8503
+ selector = '';
8504
+ }
8505
+
8506
+ opts = _selector_opts( opts );
8507
+
8508
+ var inst = this.iterator( 'table', function ( settings ) {
8509
+ return __column_selector( settings, selector, opts );
8510
+ }, 1 );
8511
+
8512
+ // Want argument shifting here and in _row_selector?
8513
+ inst.selector.cols = selector;
8514
+ inst.selector.opts = opts;
8515
+
8516
+ return inst;
8517
+ } );
8518
+
8519
+ _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
8520
+ return this.iterator( 'column', function ( settings, column ) {
8521
+ return settings.aoColumns[column].nTh;
8522
+ }, 1 );
8523
+ } );
8524
+
8525
+ _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
8526
+ return this.iterator( 'column', function ( settings, column ) {
8527
+ return settings.aoColumns[column].nTf;
8528
+ }, 1 );
8529
+ } );
8530
+
8531
+ _api_registerPlural( 'columns().data()', 'column().data()', function () {
8532
+ return this.iterator( 'column-rows', __columnData, 1 );
8533
+ } );
8534
+
8535
+ _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
8536
+ return this.iterator( 'column', function ( settings, column ) {
8537
+ return settings.aoColumns[column].mData;
8538
+ }, 1 );
8539
+ } );
8540
+
8541
+ _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
8542
+ return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8543
+ return _pluck_order( settings.aoData, rows,
8544
+ type === 'search' ? '_aFilterData' : '_aSortData', column
8545
+ );
8546
+ }, 1 );
8547
+ } );
8548
+
8549
+ _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8550
+ return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8551
+ return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
8552
+ }, 1 );
8553
+ } );
8554
+
8555
+ _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
8556
+ var ret = this.iterator( 'column', function ( settings, column ) {
8557
+ if ( vis === undefined ) {
8558
+ return settings.aoColumns[ column ].bVisible;
8559
+ } // else
8560
+ __setColumnVis( settings, column, vis );
8561
+ } );
8562
+
8563
+ // Group the column visibility changes
8564
+ if ( vis !== undefined ) {
8565
+ // Second loop once the first is done for events
8566
+ this.iterator( 'column', function ( settings, column ) {
8567
+ _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
8568
+ } );
8569
+
8570
+ if ( calc === undefined || calc ) {
8571
+ this.columns.adjust();
8572
+ }
8573
+ }
8574
+
8575
+ return ret;
8576
+ } );
8577
+
8578
+ _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
8579
+ return this.iterator( 'column', function ( settings, column ) {
8580
+ return type === 'visible' ?
8581
+ _fnColumnIndexToVisible( settings, column ) :
8582
+ column;
8583
+ }, 1 );
8584
+ } );
8585
+
8586
+ _api_register( 'columns.adjust()', function () {
8587
+ return this.iterator( 'table', function ( settings ) {
8588
+ _fnAdjustColumnSizing( settings );
8589
+ }, 1 );
8590
+ } );
8591
+
8592
+ _api_register( 'column.index()', function ( type, idx ) {
8593
+ if ( this.context.length !== 0 ) {
8594
+ var ctx = this.context[0];
8595
+
8596
+ if ( type === 'fromVisible' || type === 'toData' ) {
8597
+ return _fnVisibleToColumnIndex( ctx, idx );
8598
+ }
8599
+ else if ( type === 'fromData' || type === 'toVisible' ) {
8600
+ return _fnColumnIndexToVisible( ctx, idx );
8601
+ }
8602
+ }
8603
+ } );
8604
+
8605
+ _api_register( 'column()', function ( selector, opts ) {
8606
+ return _selector_first( this.columns( selector, opts ) );
8607
+ } );
8608
+
8609
+
8610
+
8611
+ var __cell_selector = function ( settings, selector, opts )
8612
+ {
8613
+ var data = settings.aoData;
8614
+ var rows = _selector_row_indexes( settings, opts );
8615
+ var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
8616
+ var allCells = $( [].concat.apply([], cells) );
8617
+ var row;
8618
+ var columns = settings.aoColumns.length;
8619
+ var a, i, ien, j, o, host;
8620
+
8621
+ var run = function ( s ) {
8622
+ var fnSelector = typeof s === 'function';
8623
+
8624
+ if ( s === null || s === undefined || fnSelector ) {
8625
+ // All cells and function selectors
8626
+ a = [];
8627
+
8628
+ for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8629
+ row = rows[i];
8630
+
8631
+ for ( j=0 ; j<columns ; j++ ) {
8632
+ o = {
8633
+ row: row,
8634
+ column: j
8635
+ };
8636
+
8637
+ if ( fnSelector ) {
8638
+ // Selector - function
8639
+ host = data[ row ];
8640
+
8641
+ if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
8642
+ a.push( o );
8643
+ }
8644
+ }
8645
+ else {
8646
+ // Selector - all
8647
+ a.push( o );
8648
+ }
8649
+ }
8650
+ }
8651
+
8652
+ return a;
8653
+ }
8654
+
8655
+ // Selector - index
8656
+ if ( $.isPlainObject( s ) ) {
8657
+ return [s];
8658
+ }
8659
+
8660
+ // Selector - jQuery filtered cells
8661
+ var jqResult = allCells
8662
+ .filter( s )
8663
+ .map( function (i, el) {
8664
+ return { // use a new object, in case someone changes the values
8665
+ row: el._DT_CellIndex.row,
8666
+ column: el._DT_CellIndex.column
8667
+ };
8668
+ } )
8669
+ .toArray();
8670
+
8671
+ if ( jqResult.length || ! s.nodeName ) {
8672
+ return jqResult;
8673
+ }
8674
+
8675
+ // Otherwise the selector is a node, and there is one last option - the
8676
+ // element might be a child of an element which has dt-row and dt-column
8677
+ // data attributes
8678
+ host = $(s).closest('*[data-dt-row]');
8679
+ return host.length ?
8680
+ [ {
8681
+ row: host.data('dt-row'),
8682
+ column: host.data('dt-column')
8683
+ } ] :
8684
+ [];
8685
+ };
8686
+
8687
+ return _selector_run( 'cell', selector, run, settings, opts );
8688
+ };
8689
+
8690
+
8691
+
8692
+
8693
+ _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
8694
+ // Argument shifting
8695
+ if ( $.isPlainObject( rowSelector ) ) {
8696
+ // Indexes
8697
+ if ( rowSelector.row === undefined ) {
8698
+ // Selector options in first parameter
8699
+ opts = rowSelector;
8700
+ rowSelector = null;
8701
+ }
8702
+ else {
8703
+ // Cell index objects in first parameter
8704
+ opts = columnSelector;
8705
+ columnSelector = null;
8706
+ }
8707
+ }
8708
+ if ( $.isPlainObject( columnSelector ) ) {
8709
+ opts = columnSelector;
8710
+ columnSelector = null;
8711
+ }
8712
+
8713
+ // Cell selector
8714
+ if ( columnSelector === null || columnSelector === undefined ) {
8715
+ return this.iterator( 'table', function ( settings ) {
8716
+ return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
8717
+ } );
8718
+ }
8719
+
8720
+ // Row + column selector
8721
+ var columns = this.columns( columnSelector, opts );
8722
+ var rows = this.rows( rowSelector, opts );
8723
+ var a, i, ien, j, jen;
8724
+
8725
+ var cells = this.iterator( 'table', function ( settings, idx ) {
8726
+ a = [];
8727
+
8728
+ for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
8729
+ for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
8730
+ a.push( {
8731
+ row: rows[idx][i],
8732
+ column: columns[idx][j]
8733
+ } );
8734
+ }
8735
+ }
8736
+
8737
+ return a;
8738
+ }, 1 );
8739
+
8740
+ $.extend( cells.selector, {
8741
+ cols: columnSelector,
8742
+ rows: rowSelector,
8743
+ opts: opts
8744
+ } );
8745
+
8746
+ return cells;
8747
+ } );
8748
+
8749
+
8750
+ _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
8751
+ return this.iterator( 'cell', function ( settings, row, column ) {
8752
+ var data = settings.aoData[ row ];
8753
+
8754
+ return data && data.anCells ?
8755
+ data.anCells[ column ] :
8756
+ undefined;
8757
+ }, 1 );
8758
+ } );
8759
+
8760
+
8761
+ _api_register( 'cells().data()', function () {
8762
+ return this.iterator( 'cell', function ( settings, row, column ) {
8763
+ return _fnGetCellData( settings, row, column );
8764
+ }, 1 );
8765
+ } );
8766
+
8767
+
8768
+ _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
8769
+ type = type === 'search' ? '_aFilterData' : '_aSortData';
8770
+
8771
+ return this.iterator( 'cell', function ( settings, row, column ) {
8772
+ return settings.aoData[ row ][ type ][ column ];
8773
+ }, 1 );
8774
+ } );
8775
+
8776
+
8777
+ _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
8778
+ return this.iterator( 'cell', function ( settings, row, column ) {
8779
+ return _fnGetCellData( settings, row, column, type );
8780
+ }, 1 );
8781
+ } );
8782
+
8783
+
8784
+ _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
8785
+ return this.iterator( 'cell', function ( settings, row, column ) {
8786
+ return {
8787
+ row: row,
8788
+ column: column,
8789
+ columnVisible: _fnColumnIndexToVisible( settings, column )
8790
+ };
8791
+ }, 1 );
8792
+ } );
8793
+
8794
+
8795
+ _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
8796
+ return this.iterator( 'cell', function ( settings, row, column ) {
8797
+ _fnInvalidate( settings, row, src, column );
8798
+ } );
8799
+ } );
8800
+
8801
+
8802
+
8803
+ _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
8804
+ return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
8805
+ } );
8806
+
8807
+
8808
+ _api_register( 'cell().data()', function ( data ) {
8809
+ var ctx = this.context;
8810
+ var cell = this[0];
8811
+
8812
+ if ( data === undefined ) {
8813
+ // Get
8814
+ return ctx.length && cell.length ?
8815
+ _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
8816
+ undefined;
8817
+ }
8818
+
8819
+ // Set
8820
+ _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
8821
+ _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
8822
+
8823
+ return this;
8824
+ } );
8825
+
8826
+
8827
+
8828
+ /**
8829
+ * Get current ordering (sorting) that has been applied to the table.
8830
+ *
8831
+ * @returns {array} 2D array containing the sorting information for the first
8832
+ * table in the current context. Each element in the parent array represents
8833
+ * a column being sorted upon (i.e. multi-sorting with two columns would have
8834
+ * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is
8835
+ * the column index that the sorting condition applies to, the second is the
8836
+ * direction of the sort (`desc` or `asc`) and, optionally, the third is the
8837
+ * index of the sorting order from the `column.sorting` initialisation array.
8838
+ *//**
8839
+ * Set the ordering for the table.
8840
+ *
8841
+ * @param {integer} order Column index to sort upon.
8842
+ * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
8843
+ * @returns {DataTables.Api} this
8844
+ *//**
8845
+ * Set the ordering for the table.
8846
+ *
8847
+ * @param {array} order 1D array of sorting information to be applied.
8848
+ * @param {array} [...] Optional additional sorting conditions
8849
+ * @returns {DataTables.Api} this
8850
+ *//**
8851
+ * Set the ordering for the table.
8852
+ *
8853
+ * @param {array} order 2D array of sorting information to be applied.
8854
+ * @returns {DataTables.Api} this
8855
+ */
8856
+ _api_register( 'order()', function ( order, dir ) {
8857
+ var ctx = this.context;
8858
+
8859
+ if ( order === undefined ) {
8860
+ // get
8861
+ return ctx.length !== 0 ?
8862
+ ctx[0].aaSorting :
8863
+ undefined;
8864
+ }
8865
+
8866
+ // set
8867
+ if ( typeof order === 'number' ) {
8868
+ // Simple column / direction passed in
8869
+ order = [ [ order, dir ] ];
8870
+ }
8871
+ else if ( order.length && ! $.isArray( order[0] ) ) {
8872
+ // Arguments passed in (list of 1D arrays)
8873
+ order = Array.prototype.slice.call( arguments );
8874
+ }
8875
+ // otherwise a 2D array was passed in
8876
+
8877
+ return this.iterator( 'table', function ( settings ) {
8878
+ settings.aaSorting = order.slice();
8879
+ } );
8880
+ } );
8881
+
8882
+
8883
+ /**
8884
+ * Attach a sort listener to an element for a given column
8885
+ *
8886
+ * @param {node|jQuery|string} node Identifier for the element(s) to attach the
8887
+ * listener to. This can take the form of a single DOM node, a jQuery
8888
+ * collection of nodes or a jQuery selector which will identify the node(s).
8889
+ * @param {integer} column the column that a click on this node will sort on
8890
+ * @param {function} [callback] callback function when sort is run
8891
+ * @returns {DataTables.Api} this
8892
+ */
8893
+ _api_register( 'order.listener()', function ( node, column, callback ) {
8894
+ return this.iterator( 'table', function ( settings ) {
8895
+ _fnSortAttachListener( settings, node, column, callback );
8896
+ } );
8897
+ } );
8898
+
8899
+
8900
+ _api_register( 'order.fixed()', function ( set ) {
8901
+ if ( ! set ) {
8902
+ var ctx = this.context;
8903
+ var fixed = ctx.length ?
8904
+ ctx[0].aaSortingFixed :
8905
+ undefined;
8906
+
8907
+ return $.isArray( fixed ) ?
8908
+ { pre: fixed } :
8909
+ fixed;
8910
+ }
8911
+
8912
+ return this.iterator( 'table', function ( settings ) {
8913
+ settings.aaSortingFixed = $.extend( true, {}, set );
8914
+ } );
8915
+ } );
8916
+
8917
+
8918
+ // Order by the selected column(s)
8919
+ _api_register( [
8920
+ 'columns().order()',
8921
+ 'column().order()'
8922
+ ], function ( dir ) {
8923
+ var that = this;
8924
+
8925
+ return this.iterator( 'table', function ( settings, i ) {
8926
+ var sort = [];
8927
+
8928
+ $.each( that[i], function (j, col) {
8929
+ sort.push( [ col, dir ] );
8930
+ } );
8931
+
8932
+ settings.aaSorting = sort;
8933
+ } );
8934
+ } );
8935
+
8936
+
8937
+
8938
+ _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
8939
+ var ctx = this.context;
8940
+
8941
+ if ( input === undefined ) {
8942
+ // get
8943
+ return ctx.length !== 0 ?
8944
+ ctx[0].oPreviousSearch.sSearch :
8945
+ undefined;
8946
+ }
8947
+
8948
+ // set
8949
+ return this.iterator( 'table', function ( settings ) {
8950
+ if ( ! settings.oFeatures.bFilter ) {
8951
+ return;
8952
+ }
8953
+
8954
+ _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
8955
+ "sSearch": input+"",
8956
+ "bRegex": regex === null ? false : regex,
8957
+ "bSmart": smart === null ? true : smart,
8958
+ "bCaseInsensitive": caseInsen === null ? true : caseInsen
8959
+ } ), 1 );
8960
+ } );
8961
+ } );
8962
+
8963
+
8964
+ _api_registerPlural(
8965
+ 'columns().search()',
8966
+ 'column().search()',
8967
+ function ( input, regex, smart, caseInsen ) {
8968
+ return this.iterator( 'column', function ( settings, column ) {
8969
+ var preSearch = settings.aoPreSearchCols;
8970
+
8971
+ if ( input === undefined ) {
8972
+ // get
8973
+ return preSearch[ column ].sSearch;
8974
+ }
8975
+
8976
+ // set
8977
+ if ( ! settings.oFeatures.bFilter ) {
8978
+ return;
8979
+ }
8980
+
8981
+ $.extend( preSearch[ column ], {
8982
+ "sSearch": input+"",
8983
+ "bRegex": regex === null ? false : regex,
8984
+ "bSmart": smart === null ? true : smart,
8985
+ "bCaseInsensitive": caseInsen === null ? true : caseInsen
8986
+ } );
8987
+
8988
+ _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
8989
+ } );
8990
+ }
8991
+ );
8992
+
8993
+ /*
8994
+ * State API methods
8995
+ */
8996
+
8997
+ _api_register( 'state()', function () {
8998
+ return this.context.length ?
8999
+ this.context[0].oSavedState :
9000
+ null;
9001
+ } );
9002
+
9003
+
9004
+ _api_register( 'state.clear()', function () {
9005
+ return this.iterator( 'table', function ( settings ) {
9006
+ // Save an empty object
9007
+ settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
9008
+ } );
9009
+ } );
9010
+
9011
+
9012
+ _api_register( 'state.loaded()', function () {
9013
+ return this.context.length ?
9014
+ this.context[0].oLoadedState :
9015
+ null;
9016
+ } );
9017
+
9018
+
9019
+ _api_register( 'state.save()', function () {
9020
+ return this.iterator( 'table', function ( settings ) {
9021
+ _fnSaveState( settings );
9022
+ } );
9023
+ } );
9024
+
9025
+
9026
+
9027
+ /**
9028
+ * Provide a common method for plug-ins to check the version of DataTables being
9029
+ * used, in order to ensure compatibility.
9030
+ *
9031
+ * @param {string} version Version string to check for, in the format "X.Y.Z".
9032
+ * Note that the formats "X" and "X.Y" are also acceptable.
9033
+ * @returns {boolean} true if this version of DataTables is greater or equal to
9034
+ * the required version, or false if this version of DataTales is not
9035
+ * suitable
9036
+ * @static
9037
+ * @dtopt API-Static
9038
+ *
9039
+ * @example
9040
+ * alert( $.fn.dataTable.versionCheck( '1.9.0' ) );
9041
+ */
9042
+ DataTable.versionCheck = DataTable.fnVersionCheck = function( version )
9043
+ {
9044
+ var aThis = DataTable.version.split('.');
9045
+ var aThat = version.split('.');
9046
+ var iThis, iThat;
9047
+
9048
+ for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
9049
+ iThis = parseInt( aThis[i], 10 ) || 0;
9050
+ iThat = parseInt( aThat[i], 10 ) || 0;
9051
+
9052
+ // Parts are the same, keep comparing
9053
+ if (iThis === iThat) {
9054
+ continue;
9055
+ }
9056
+
9057
+ // Parts are different, return immediately
9058
+ return iThis > iThat;
9059
+ }
9060
+
9061
+ return true;
9062
+ };
9063
+
9064
+
9065
+ /**
9066
+ * Check if a `<table>` node is a DataTable table already or not.
9067
+ *
9068
+ * @param {node|jquery|string} table Table node, jQuery object or jQuery
9069
+ * selector for the table to test. Note that if more than more than one
9070
+ * table is passed on, only the first will be checked
9071
+ * @returns {boolean} true the table given is a DataTable, or false otherwise
9072
+ * @static
9073
+ * @dtopt API-Static
9074
+ *
9075
+ * @example
9076
+ * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {
9077
+ * $('#example').dataTable();
9078
+ * }
9079
+ */
9080
+ DataTable.isDataTable = DataTable.fnIsDataTable = function ( table )
9081
+ {
9082
+ var t = $(table).get(0);
9083
+ var is = false;
9084
+
9085
+ $.each( DataTable.settings, function (i, o) {
9086
+ var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
9087
+ var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
9088
+
9089
+ if ( o.nTable === t || head === t || foot === t ) {
9090
+ is = true;
9091
+ }
9092
+ } );
9093
+
9094
+ return is;
9095
+ };
9096
+
9097
+
9098
+ /**
9099
+ * Get all DataTable tables that have been initialised - optionally you can
9100
+ * select to get only currently visible tables.
9101
+ *
9102
+ * @param {boolean} [visible=false] Flag to indicate if you want all (default)
9103
+ * or visible tables only.
9104
+ * @returns {array} Array of `table` nodes (not DataTable instances) which are
9105
+ * DataTables
9106
+ * @static
9107
+ * @dtopt API-Static
9108
+ *
9109
+ * @example
9110
+ * $.each( $.fn.dataTable.tables(true), function () {
9111
+ * $(table).DataTable().columns.adjust();
9112
+ * } );
9113
+ */
9114
+ DataTable.tables = DataTable.fnTables = function ( visible )
9115
+ {
9116
+ var api = false;
9117
+
9118
+ if ( $.isPlainObject( visible ) ) {
9119
+ api = visible.api;
9120
+ visible = visible.visible;
9121
+ }
9122
+
9123
+ var a = $.map( DataTable.settings, function (o) {
9124
+ if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
9125
+ return o.nTable;
9126
+ }
9127
+ } );
9128
+
9129
+ return api ?
9130
+ new _Api( a ) :
9131
+ a;
9132
+ };
9133
+
9134
+
9135
+ /**
9136
+ * Convert from camel case parameters to Hungarian notation. This is made public
9137
+ * for the extensions to provide the same ability as DataTables core to accept
9138
+ * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
9139
+ * parameters.
9140
+ *
9141
+ * @param {object} src The model object which holds all parameters that can be
9142
+ * mapped.
9143
+ * @param {object} user The object to convert from camel case to Hungarian.
9144
+ * @param {boolean} force When set to `true`, properties which already have a
9145
+ * Hungarian value in the `user` object will be overwritten. Otherwise they
9146
+ * won't be.
9147
+ */
9148
+ DataTable.camelToHungarian = _fnCamelToHungarian;
9149
+
9150
+
9151
+
9152
+ /**
9153
+ *
9154
+ */
9155
+ _api_register( '$()', function ( selector, opts ) {
9156
+ var
9157
+ rows = this.rows( opts ).nodes(), // Get all rows
9158
+ jqRows = $(rows);
9159
+
9160
+ return $( [].concat(
9161
+ jqRows.filter( selector ).toArray(),
9162
+ jqRows.find( selector ).toArray()
9163
+ ) );
9164
+ } );
9165
+
9166
+
9167
+ // jQuery functions to operate on the tables
9168
+ $.each( [ 'on', 'one', 'off' ], function (i, key) {
9169
+ _api_register( key+'()', function ( /* event, handler */ ) {
9170
+ var args = Array.prototype.slice.call(arguments);
9171
+
9172
+ // Add the `dt` namespace automatically if it isn't already present
9173
+ if ( ! args[0].match(/\.dt\b/) ) {
9174
+ args[0] += '.dt';
9175
+ }
9176
+
9177
+ var inst = $( this.tables().nodes() );
9178
+ inst[key].apply( inst, args );
9179
+ return this;
9180
+ } );
9181
+ } );
9182
+
9183
+
9184
+ _api_register( 'clear()', function () {
9185
+ return this.iterator( 'table', function ( settings ) {
9186
+ _fnClearTable( settings );
9187
+ } );
9188
+ } );
9189
+
9190
+
9191
+ _api_register( 'settings()', function () {
9192
+ return new _Api( this.context, this.context );
9193
+ } );
9194
+
9195
+
9196
+ _api_register( 'init()', function () {
9197
+ var ctx = this.context;
9198
+ return ctx.length ? ctx[0].oInit : null;
9199
+ } );
9200
+
9201
+
9202
+ _api_register( 'data()', function () {
9203
+ return this.iterator( 'table', function ( settings ) {
9204
+ return _pluck( settings.aoData, '_aData' );
9205
+ } ).flatten();
9206
+ } );
9207
+
9208
+
9209
+ _api_register( 'destroy()', function ( remove ) {
9210
+ remove = remove || false;
9211
+
9212
+ return this.iterator( 'table', function ( settings ) {
9213
+ var orig = settings.nTableWrapper.parentNode;
9214
+ var classes = settings.oClasses;
9215
+ var table = settings.nTable;
9216
+ var tbody = settings.nTBody;
9217
+ var thead = settings.nTHead;
9218
+ var tfoot = settings.nTFoot;
9219
+ var jqTable = $(table);
9220
+ var jqTbody = $(tbody);
9221
+ var jqWrapper = $(settings.nTableWrapper);
9222
+ var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
9223
+ var i, ien;
9224
+
9225
+ // Flag to note that the table is currently being destroyed - no action
9226
+ // should be taken
9227
+ settings.bDestroying = true;
9228
+
9229
+ // Fire off the destroy callbacks for plug-ins etc
9230
+ _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
9231
+
9232
+ // If not being removed from the document, make all columns visible
9233
+ if ( ! remove ) {
9234
+ new _Api( settings ).columns().visible( true );
9235
+ }
9236
+
9237
+ // Blitz all `DT` namespaced events (these are internal events, the
9238
+ // lowercase, `dt` events are user subscribed and they are responsible
9239
+ // for removing them
9240
+ jqWrapper.unbind('.DT').find(':not(tbody *)').unbind('.DT');
9241
+ $(window).unbind('.DT-'+settings.sInstance);
9242
+
9243
+ // When scrolling we had to break the table up - restore it
9244
+ if ( table != thead.parentNode ) {
9245
+ jqTable.children('thead').detach();
9246
+ jqTable.append( thead );
9247
+ }
9248
+
9249
+ if ( tfoot && table != tfoot.parentNode ) {
9250
+ jqTable.children('tfoot').detach();
9251
+ jqTable.append( tfoot );
9252
+ }
9253
+
9254
+ settings.aaSorting = [];
9255
+ settings.aaSortingFixed = [];
9256
+ _fnSortingClasses( settings );
9257
+
9258
+ $( rows ).removeClass( settings.asStripeClasses.join(' ') );
9259
+
9260
+ $('th, td', thead).removeClass( classes.sSortable+' '+
9261
+ classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
9262
+ );
9263
+
9264
+ if ( settings.bJUI ) {
9265
+ $('th span.'+classes.sSortIcon+ ', td span.'+classes.sSortIcon, thead).detach();
9266
+ $('th, td', thead).each( function () {
9267
+ var wrapper = $('div.'+classes.sSortJUIWrapper, this);
9268
+ $(this).append( wrapper.contents() );
9269
+ wrapper.detach();
9270
+ } );
9271
+ }
9272
+
9273
+ // Add the TR elements back into the table in their original order
9274
+ jqTbody.children().detach();
9275
+ jqTbody.append( rows );
9276
+
9277
+ // Remove the DataTables generated nodes, events and classes
9278
+ var removedMethod = remove ? 'remove' : 'detach';
9279
+ jqTable[ removedMethod ]();
9280
+ jqWrapper[ removedMethod ]();
9281
+
9282
+ // If we need to reattach the table to the document
9283
+ if ( ! remove && orig ) {
9284
+ // insertBefore acts like appendChild if !arg[1]
9285
+ orig.insertBefore( table, settings.nTableReinsertBefore );
9286
+
9287
+ // Restore the width of the original table - was read from the style property,
9288
+ // so we can restore directly to that
9289
+ jqTable
9290
+ .css( 'width', settings.sDestroyWidth )
9291
+ .removeClass( classes.sTable );
9292
+
9293
+ // If the were originally stripe classes - then we add them back here.
9294
+ // Note this is not fool proof (for example if not all rows had stripe
9295
+ // classes - but it's a good effort without getting carried away
9296
+ ien = settings.asDestroyStripes.length;
9297
+
9298
+ if ( ien ) {
9299
+ jqTbody.children().each( function (i) {
9300
+ $(this).addClass( settings.asDestroyStripes[i % ien] );
9301
+ } );
9302
+ }
9303
+ }
9304
+
9305
+ /* Remove the settings object from the settings array */
9306
+ var idx = $.inArray( settings, DataTable.settings );
9307
+ if ( idx !== -1 ) {
9308
+ DataTable.settings.splice( idx, 1 );
9309
+ }
9310
+ } );
9311
+ } );
9312
+
9313
+
9314
+ // Add the `every()` method for rows, columns and cells in a compact form
9315
+ $.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
9316
+ _api_register( type+'s().every()', function ( fn ) {
9317
+ var opts = this.selector.opts;
9318
+ var api = this;
9319
+
9320
+ return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {
9321
+ // Rows and columns:
9322
+ // arg1 - index
9323
+ // arg2 - table counter
9324
+ // arg3 - loop counter
9325
+ // arg4 - undefined
9326
+ // Cells:
9327
+ // arg1 - row index
9328
+ // arg2 - column index
9329
+ // arg3 - table counter
9330
+ // arg4 - loop counter
9331
+ fn.call(
9332
+ api[ type ](
9333
+ arg1,
9334
+ type==='cell' ? arg2 : opts,
9335
+ type==='cell' ? opts : undefined
9336
+ ),
9337
+ arg1, arg2, arg3, arg4
9338
+ );
9339
+ } );
9340
+ } );
9341
+ } );
9342
+
9343
+
9344
+ // i18n method for extensions to be able to use the language object from the
9345
+ // DataTable
9346
+ _api_register( 'i18n()', function ( token, def, plural ) {
9347
+ var ctx = this.context[0];
9348
+ var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
9349
+
9350
+ if ( resolved === undefined ) {
9351
+ resolved = def;
9352
+ }
9353
+
9354
+ if ( plural !== undefined && $.isPlainObject( resolved ) ) {
9355
+ resolved = resolved[ plural ] !== undefined ?
9356
+ resolved[ plural ] :
9357
+ resolved._;
9358
+ }
9359
+
9360
+ return resolved.replace( '%d', plural ); // nb: plural might be undefined,
9361
+ } );
9362
+
9363
+ /**
9364
+ * Version string for plug-ins to check compatibility. Allowed format is
9365
+ * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
9366
+ * only for non-release builds. See http://semver.org/ for more information.
9367
+ * @member
9368
+ * @type string
9369
+ * @default Version number
9370
+ */
9371
+ DataTable.version = "1.10.12";
9372
+
9373
+ /**
9374
+ * Private data store, containing all of the settings objects that are
9375
+ * created for the tables on a given page.
9376
+ *
9377
+ * Note that the `DataTable.settings` object is aliased to
9378
+ * `jQuery.fn.dataTableExt` through which it may be accessed and
9379
+ * manipulated, or `jQuery.fn.dataTable.settings`.
9380
+ * @member
9381
+ * @type array
9382
+ * @default []
9383
+ * @private
9384
+ */
9385
+ DataTable.settings = [];
9386
+
9387
+ /**
9388
+ * Object models container, for the various models that DataTables has
9389
+ * available to it. These models define the objects that are used to hold
9390
+ * the active state and configuration of the table.
9391
+ * @namespace
9392
+ */
9393
+ DataTable.models = {};
9394
+
9395
+
9396
+
9397
+ /**
9398
+ * Template object for the way in which DataTables holds information about
9399
+ * search information for the global filter and individual column filters.
9400
+ * @namespace
9401
+ */
9402
+ DataTable.models.oSearch = {
9403
+ /**
9404
+ * Flag to indicate if the filtering should be case insensitive or not
9405
+ * @type boolean
9406
+ * @default true
9407
+ */
9408
+ "bCaseInsensitive": true,
9409
+
9410
+ /**
9411
+ * Applied search term
9412
+ * @type string
9413
+ * @default <i>Empty string</i>
9414
+ */
9415
+ "sSearch": "",
9416
+
9417
+ /**
9418
+ * Flag to indicate if the search term should be interpreted as a
9419
+ * regular expression (true) or not (false) and therefore and special
9420
+ * regex characters escaped.
9421
+ * @type boolean
9422
+ * @default false
9423
+ */
9424
+ "bRegex": false,
9425
+
9426
+ /**
9427
+ * Flag to indicate if DataTables is to use its smart filtering or not.
9428
+ * @type boolean
9429
+ * @default true
9430
+ */
9431
+ "bSmart": true
9432
+ };
9433
+
9434
+
9435
+
9436
+
9437
+ /**
9438
+ * Template object for the way in which DataTables holds information about
9439
+ * each individual row. This is the object format used for the settings
9440
+ * aoData array.
9441
+ * @namespace
9442
+ */
9443
+ DataTable.models.oRow = {
9444
+ /**
9445
+ * TR element for the row
9446
+ * @type node
9447
+ * @default null
9448
+ */
9449
+ "nTr": null,
9450
+
9451
+ /**
9452
+ * Array of TD elements for each row. This is null until the row has been
9453
+ * created.
9454
+ * @type array nodes
9455
+ * @default []
9456
+ */
9457
+ "anCells": null,
9458
+
9459
+ /**
9460
+ * Data object from the original data source for the row. This is either
9461
+ * an array if using the traditional form of DataTables, or an object if
9462
+ * using mData options. The exact type will depend on the passed in
9463
+ * data from the data source, or will be an array if using DOM a data
9464
+ * source.
9465
+ * @type array|object
9466
+ * @default []
9467
+ */
9468
+ "_aData": [],
9469
+
9470
+ /**
9471
+ * Sorting data cache - this array is ostensibly the same length as the
9472
+ * number of columns (although each index is generated only as it is
9473
+ * needed), and holds the data that is used for sorting each column in the
9474
+ * row. We do this cache generation at the start of the sort in order that
9475
+ * the formatting of the sort data need be done only once for each cell
9476
+ * per sort. This array should not be read from or written to by anything
9477
+ * other than the master sorting methods.
9478
+ * @type array
9479
+ * @default null
9480
+ * @private
9481
+ */
9482
+ "_aSortData": null,
9483
+
9484
+ /**
9485
+ * Per cell filtering data cache. As per the sort data cache, used to
9486
+ * increase the performance of the filtering in DataTables
9487
+ * @type array
9488
+ * @default null
9489
+ * @private
9490
+ */
9491
+ "_aFilterData": null,
9492
+
9493
+ /**
9494
+ * Filtering data cache. This is the same as the cell filtering cache, but
9495
+ * in this case a string rather than an array. This is easily computed with
9496
+ * a join on `_aFilterData`, but is provided as a cache so the join isn't
9497
+ * needed on every search (memory traded for performance)
9498
+ * @type array
9499
+ * @default null
9500
+ * @private
9501
+ */
9502
+ "_sFilterRow": null,
9503
+
9504
+ /**
9505
+ * Cache of the class name that DataTables has applied to the row, so we
9506
+ * can quickly look at this variable rather than needing to do a DOM check
9507
+ * on className for the nTr property.
9508
+ * @type string
9509
+ * @default <i>Empty string</i>
9510
+ * @private
9511
+ */
9512
+ "_sRowStripe": "",
9513
+
9514
+ /**
9515
+ * Denote if the original data source was from the DOM, or the data source
9516
+ * object. This is used for invalidating data, so DataTables can
9517
+ * automatically read data from the original source, unless uninstructed
9518
+ * otherwise.
9519
+ * @type string
9520
+ * @default null
9521
+ * @private
9522
+ */
9523
+ "src": null,
9524
+
9525
+ /**
9526
+ * Index in the aoData array. This saves an indexOf lookup when we have the
9527
+ * object, but want to know the index
9528
+ * @type integer
9529
+ * @default -1
9530
+ * @private
9531
+ */
9532
+ "idx": -1
9533
+ };
9534
+
9535
+
9536
+ /**
9537
+ * Template object for the column information object in DataTables. This object
9538
+ * is held in the settings aoColumns array and contains all the information that
9539
+ * DataTables needs about each individual column.
9540
+ *
9541
+ * Note that this object is related to {@link DataTable.defaults.column}
9542
+ * but this one is the internal data store for DataTables's cache of columns.
9543
+ * It should NOT be manipulated outside of DataTables. Any configuration should
9544
+ * be done through the initialisation options.
9545
+ * @namespace
9546
+ */
9547
+ DataTable.models.oColumn = {
9548
+ /**
9549
+ * Column index. This could be worked out on-the-fly with $.inArray, but it
9550
+ * is faster to just hold it as a variable
9551
+ * @type integer
9552
+ * @default null
9553
+ */
9554
+ "idx": null,
9555
+
9556
+ /**
9557
+ * A list of the columns that sorting should occur on when this column
9558
+ * is sorted. That this property is an array allows multi-column sorting
9559
+ * to be defined for a column (for example first name / last name columns
9560
+ * would benefit from this). The values are integers pointing to the
9561
+ * columns to be sorted on (typically it will be a single integer pointing
9562
+ * at itself, but that doesn't need to be the case).
9563
+ * @type array
9564
+ */
9565
+ "aDataSort": null,
9566
+
9567
+ /**
9568
+ * Define the sorting directions that are applied to the column, in sequence
9569
+ * as the column is repeatedly sorted upon - i.e. the first value is used
9570
+ * as the sorting direction when the column if first sorted (clicked on).
9571
+ * Sort it again (click again) and it will move on to the next index.
9572
+ * Repeat until loop.
9573
+ * @type array
9574
+ */
9575
+ "asSorting": null,
9576
+
9577
+ /**
9578
+ * Flag to indicate if the column is searchable, and thus should be included
9579
+ * in the filtering or not.
9580
+ * @type boolean
9581
+ */
9582
+ "bSearchable": null,
9583
+
9584
+ /**
9585
+ * Flag to indicate if the column is sortable or not.
9586
+ * @type boolean
9587
+ */
9588
+ "bSortable": null,
9589
+
9590
+ /**
9591
+ * Flag to indicate if the column is currently visible in the table or not
9592
+ * @type boolean
9593
+ */
9594
+ "bVisible": null,
9595
+
9596
+ /**
9597
+ * Store for manual type assignment using the `column.type` option. This
9598
+ * is held in store so we can manipulate the column's `sType` property.
9599
+ * @type string
9600
+ * @default null
9601
+ * @private
9602
+ */
9603
+ "_sManualType": null,
9604
+
9605
+ /**
9606
+ * Flag to indicate if HTML5 data attributes should be used as the data
9607
+ * source for filtering or sorting. True is either are.
9608
+ * @type boolean
9609
+ * @default false
9610
+ * @private
9611
+ */
9612
+ "_bAttrSrc": false,
9613
+
9614
+ /**
9615
+ * Developer definable function that is called whenever a cell is created (Ajax source,
9616
+ * etc) or processed for input (DOM source). This can be used as a compliment to mRender
9617
+ * allowing you to modify the DOM element (add background colour for example) when the
9618
+ * element is available.
9619
+ * @type function
9620
+ * @param {element} nTd The TD node that has been created
9621
+ * @param {*} sData The Data for the cell
9622
+ * @param {array|object} oData The data for the whole row
9623
+ * @param {int} iRow The row index for the aoData data store
9624
+ * @default null
9625
+ */
9626
+ "fnCreatedCell": null,
9627
+
9628
+ /**
9629
+ * Function to get data from a cell in a column. You should <b>never</b>
9630
+ * access data directly through _aData internally in DataTables - always use
9631
+ * the method attached to this property. It allows mData to function as
9632
+ * required. This function is automatically assigned by the column
9633
+ * initialisation method
9634
+ * @type function
9635
+ * @param {array|object} oData The data array/object for the array
9636
+ * (i.e. aoData[]._aData)
9637
+ * @param {string} sSpecific The specific data type you want to get -
9638
+ * 'display', 'type' 'filter' 'sort'
9639
+ * @returns {*} The data for the cell from the given row's data
9640
+ * @default null
9641
+ */
9642
+ "fnGetData": null,
9643
+
9644
+ /**
9645
+ * Function to set data for a cell in the column. You should <b>never</b>
9646
+ * set the data directly to _aData internally in DataTables - always use
9647
+ * this method. It allows mData to function as required. This function
9648
+ * is automatically assigned by the column initialisation method
9649
+ * @type function
9650
+ * @param {array|object} oData The data array/object for the array
9651
+ * (i.e. aoData[]._aData)
9652
+ * @param {*} sValue Value to set
9653
+ * @default null
9654
+ */
9655
+ "fnSetData": null,
9656
+
9657
+ /**
9658
+ * Property to read the value for the cells in the column from the data
9659
+ * source array / object. If null, then the default content is used, if a
9660
+ * function is given then the return from the function is used.
9661
+ * @type function|int|string|null
9662
+ * @default null
9663
+ */
9664
+ "mData": null,
9665
+
9666
+ /**
9667
+ * Partner property to mData which is used (only when defined) to get
9668
+ * the data - i.e. it is basically the same as mData, but without the
9669
+ * 'set' option, and also the data fed to it is the result from mData.
9670
+ * This is the rendering method to match the data method of mData.
9671
+ * @type function|int|string|null
9672
+ * @default null
9673
+ */
9674
+ "mRender": null,
9675
+
9676
+ /**
9677
+ * Unique header TH/TD element for this column - this is what the sorting
9678
+ * listener is attached to (if sorting is enabled.)
9679
+ * @type node
9680
+ * @default null
9681
+ */
9682
+ "nTh": null,
9683
+
9684
+ /**
9685
+ * Unique footer TH/TD element for this column (if there is one). Not used
9686
+ * in DataTables as such, but can be used for plug-ins to reference the
9687
+ * footer for each column.
9688
+ * @type node
9689
+ * @default null
9690
+ */
9691
+ "nTf": null,
9692
+
9693
+ /**
9694
+ * The class to apply to all TD elements in the table's TBODY for the column
9695
+ * @type string
9696
+ * @default null
9697
+ */
9698
+ "sClass": null,
9699
+
9700
+ /**
9701
+ * When DataTables calculates the column widths to assign to each column,
9702
+ * it finds the longest string in each column and then constructs a
9703
+ * temporary table and reads the widths from that. The problem with this
9704
+ * is that "mmm" is much wider then "iiii", but the latter is a longer
9705
+ * string - thus the calculation can go wrong (doing it properly and putting
9706
+ * it into an DOM object and measuring that is horribly(!) slow). Thus as
9707
+ * a "work around" we provide this option. It will append its value to the
9708
+ * text that is found to be the longest string for the column - i.e. padding.
9709
+ * @type string
9710
+ */
9711
+ "sContentPadding": null,
9712
+
9713
+ /**
9714
+ * Allows a default value to be given for a column's data, and will be used
9715
+ * whenever a null data source is encountered (this can be because mData
9716
+ * is set to null, or because the data source itself is null).
9717
+ * @type string
9718
+ * @default null
9719
+ */
9720
+ "sDefaultContent": null,
9721
+
9722
+ /**
9723
+ * Name for the column, allowing reference to the column by name as well as
9724
+ * by index (needs a lookup to work by name).
9725
+ * @type string
9726
+ */
9727
+ "sName": null,
9728
+
9729
+ /**
9730
+ * Custom sorting data type - defines which of the available plug-ins in
9731
+ * afnSortData the custom sorting will use - if any is defined.
9732
+ * @type string
9733
+ * @default std
9734
+ */
9735
+ "sSortDataType": 'std',
9736
+
9737
+ /**
9738
+ * Class to be applied to the header element when sorting on this column
9739
+ * @type string
9740
+ * @default null
9741
+ */
9742
+ "sSortingClass": null,
9743
+
9744
+ /**
9745
+ * Class to be applied to the header element when sorting on this column -
9746
+ * when jQuery UI theming is used.
9747
+ * @type string
9748
+ * @default null
9749
+ */
9750
+ "sSortingClassJUI": null,
9751
+
9752
+ /**
9753
+ * Title of the column - what is seen in the TH element (nTh).
9754
+ * @type string
9755
+ */
9756
+ "sTitle": null,
9757
+
9758
+ /**
9759
+ * Column sorting and filtering type
9760
+ * @type string
9761
+ * @default null
9762
+ */
9763
+ "sType": null,
9764
+
9765
+ /**
9766
+ * Width of the column
9767
+ * @type string
9768
+ * @default null
9769
+ */
9770
+ "sWidth": null,
9771
+
9772
+ /**
9773
+ * Width of the column when it was first "encountered"
9774
+ * @type string
9775
+ * @default null
9776
+ */
9777
+ "sWidthOrig": null
9778
+ };
9779
+
9780
+
9781
+ /*
9782
+ * Developer note: The properties of the object below are given in Hungarian
9783
+ * notation, that was used as the interface for DataTables prior to v1.10, however
9784
+ * from v1.10 onwards the primary interface is camel case. In order to avoid
9785
+ * breaking backwards compatibility utterly with this change, the Hungarian
9786
+ * version is still, internally the primary interface, but is is not documented
9787
+ * - hence the @name tags in each doc comment. This allows a Javascript function
9788
+ * to create a map from Hungarian notation to camel case (going the other direction
9789
+ * would require each property to be listed, which would at around 3K to the size
9790
+ * of DataTables, while this method is about a 0.5K hit.
9791
+ *
9792
+ * Ultimately this does pave the way for Hungarian notation to be dropped
9793
+ * completely, but that is a massive amount of work and will break current
9794
+ * installs (therefore is on-hold until v2).
9795
+ */
9796
+
9797
+ /**
9798
+ * Initialisation options that can be given to DataTables at initialisation
9799
+ * time.
9800
+ * @namespace
9801
+ */
9802
+ DataTable.defaults = {
9803
+ /**
9804
+ * An array of data to use for the table, passed in at initialisation which
9805
+ * will be used in preference to any data which is already in the DOM. This is
9806
+ * particularly useful for constructing tables purely in Javascript, for
9807
+ * example with a custom Ajax call.
9808
+ * @type array
9809
+ * @default null
9810
+ *
9811
+ * @dtopt Option
9812
+ * @name DataTable.defaults.data
9813
+ *
9814
+ * @example
9815
+ * // Using a 2D array data source
9816
+ * $(document).ready( function () {
9817
+ * $('#example').dataTable( {
9818
+ * "data": [
9819
+ * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
9820
+ * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
9821
+ * ],
9822
+ * "columns": [
9823
+ * { "title": "Engine" },
9824
+ * { "title": "Browser" },
9825
+ * { "title": "Platform" },
9826
+ * { "title": "Version" },
9827
+ * { "title": "Grade" }
9828
+ * ]
9829
+ * } );
9830
+ * } );
9831
+ *
9832
+ * @example
9833
+ * // Using an array of objects as a data source (`data`)
9834
+ * $(document).ready( function () {
9835
+ * $('#example').dataTable( {
9836
+ * "data": [
9837
+ * {
9838
+ * "engine": "Trident",
9839
+ * "browser": "Internet Explorer 4.0",
9840
+ * "platform": "Win 95+",
9841
+ * "version": 4,
9842
+ * "grade": "X"
9843
+ * },
9844
+ * {
9845
+ * "engine": "Trident",
9846
+ * "browser": "Internet Explorer 5.0",
9847
+ * "platform": "Win 95+",
9848
+ * "version": 5,
9849
+ * "grade": "C"
9850
+ * }
9851
+ * ],
9852
+ * "columns": [
9853
+ * { "title": "Engine", "data": "engine" },
9854
+ * { "title": "Browser", "data": "browser" },
9855
+ * { "title": "Platform", "data": "platform" },
9856
+ * { "title": "Version", "data": "version" },
9857
+ * { "title": "Grade", "data": "grade" }
9858
+ * ]
9859
+ * } );
9860
+ * } );
9861
+ */
9862
+ "aaData": null,
9863
+
9864
+
9865
+ /**
9866
+ * If ordering is enabled, then DataTables will perform a first pass sort on
9867
+ * initialisation. You can define which column(s) the sort is performed
9868
+ * upon, and the sorting direction, with this variable. The `sorting` array
9869
+ * should contain an array for each column to be sorted initially containing
9870
+ * the column's index and a direction string ('asc' or 'desc').
9871
+ * @type array
9872
+ * @default [[0,'asc']]
9873
+ *
9874
+ * @dtopt Option
9875
+ * @name DataTable.defaults.order
9876
+ *
9877
+ * @example
9878
+ * // Sort by 3rd column first, and then 4th column
9879
+ * $(document).ready( function() {
9880
+ * $('#example').dataTable( {
9881
+ * "order": [[2,'asc'], [3,'desc']]
9882
+ * } );
9883
+ * } );
9884
+ *
9885
+ * // No initial sorting
9886
+ * $(document).ready( function() {
9887
+ * $('#example').dataTable( {
9888
+ * "order": []
9889
+ * } );
9890
+ * } );
9891
+ */
9892
+ "aaSorting": [[0,'asc']],
9893
+
9894
+
9895
+ /**
9896
+ * This parameter is basically identical to the `sorting` parameter, but
9897
+ * cannot be overridden by user interaction with the table. What this means
9898
+ * is that you could have a column (visible or hidden) which the sorting
9899
+ * will always be forced on first - any sorting after that (from the user)
9900
+ * will then be performed as required. This can be useful for grouping rows
9901
+ * together.
9902
+ * @type array
9903
+ * @default null
9904
+ *
9905
+ * @dtopt Option
9906
+ * @name DataTable.defaults.orderFixed
9907
+ *
9908
+ * @example
9909
+ * $(document).ready( function() {
9910
+ * $('#example').dataTable( {
9911
+ * "orderFixed": [[0,'asc']]
9912
+ * } );
9913
+ * } )
9914
+ */
9915
+ "aaSortingFixed": [],
9916
+
9917
+
9918
+ /**
9919
+ * DataTables can be instructed to load data to display in the table from a
9920
+ * Ajax source. This option defines how that Ajax call is made and where to.
9921
+ *
9922
+ * The `ajax` property has three different modes of operation, depending on
9923
+ * how it is defined. These are:
9924
+ *
9925
+ * * `string` - Set the URL from where the data should be loaded from.
9926
+ * * `object` - Define properties for `jQuery.ajax`.
9927
+ * * `function` - Custom data get function
9928
+ *
9929
+ * `string`
9930
+ * --------
9931
+ *
9932
+ * As a string, the `ajax` property simply defines the URL from which
9933
+ * DataTables will load data.
9934
+ *
9935
+ * `object`
9936
+ * --------
9937
+ *
9938
+ * As an object, the parameters in the object are passed to
9939
+ * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
9940
+ * of the Ajax request. DataTables has a number of default parameters which
9941
+ * you can override using this option. Please refer to the jQuery
9942
+ * documentation for a full description of the options available, although
9943
+ * the following parameters provide additional options in DataTables or
9944
+ * require special consideration:
9945
+ *
9946
+ * * `data` - As with jQuery, `data` can be provided as an object, but it
9947
+ * can also be used as a function to manipulate the data DataTables sends
9948
+ * to the server. The function takes a single parameter, an object of
9949
+ * parameters with the values that DataTables has readied for sending. An
9950
+ * object may be returned which will be merged into the DataTables
9951
+ * defaults, or you can add the items to the object that was passed in and
9952
+ * not return anything from the function. This supersedes `fnServerParams`
9953
+ * from DataTables 1.9-.
9954
+ *
9955
+ * * `dataSrc` - By default DataTables will look for the property `data` (or
9956
+ * `aaData` for compatibility with DataTables 1.9-) when obtaining data
9957
+ * from an Ajax source or for server-side processing - this parameter
9958
+ * allows that property to be changed. You can use Javascript dotted
9959
+ * object notation to get a data source for multiple levels of nesting, or
9960
+ * it my be used as a function. As a function it takes a single parameter,
9961
+ * the JSON returned from the server, which can be manipulated as
9962
+ * required, with the returned value being that used by DataTables as the
9963
+ * data source for the table. This supersedes `sAjaxDataProp` from
9964
+ * DataTables 1.9-.
9965
+ *
9966
+ * * `success` - Should not be overridden it is used internally in
9967
+ * DataTables. To manipulate / transform the data returned by the server
9968
+ * use `ajax.dataSrc`, or use `ajax` as a function (see below).
9969
+ *
9970
+ * `function`
9971
+ * ----------
9972
+ *
9973
+ * As a function, making the Ajax call is left up to yourself allowing
9974
+ * complete control of the Ajax request. Indeed, if desired, a method other
9975
+ * than Ajax could be used to obtain the required data, such as Web storage
9976
+ * or an AIR database.
9977
+ *
9978
+ * The function is given four parameters and no return is required. The
9979
+ * parameters are:
9980
+ *
9981
+ * 1. _object_ - Data to send to the server
9982
+ * 2. _function_ - Callback function that must be executed when the required
9983
+ * data has been obtained. That data should be passed into the callback
9984
+ * as the only parameter
9985
+ * 3. _object_ - DataTables settings object for the table
9986
+ *
9987
+ * Note that this supersedes `fnServerData` from DataTables 1.9-.
9988
+ *
9989
+ * @type string|object|function
9990
+ * @default null
9991
+ *
9992
+ * @dtopt Option
9993
+ * @name DataTable.defaults.ajax
9994
+ * @since 1.10.0
9995
+ *
9996
+ * @example
9997
+ * // Get JSON data from a file via Ajax.
9998
+ * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).
9999
+ * $('#example').dataTable( {
10000
+ * "ajax": "data.json"
10001
+ * } );
10002
+ *
10003
+ * @example
10004
+ * // Get JSON data from a file via Ajax, using `dataSrc` to change
10005
+ * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
10006
+ * $('#example').dataTable( {
10007
+ * "ajax": {
10008
+ * "url": "data.json",
10009
+ * "dataSrc": "tableData"
10010
+ * }
10011
+ * } );
10012
+ *
10013
+ * @example
10014
+ * // Get JSON data from a file via Ajax, using `dataSrc` to read data
10015
+ * // from a plain array rather than an array in an object
10016
+ * $('#example').dataTable( {
10017
+ * "ajax": {
10018
+ * "url": "data.json",
10019
+ * "dataSrc": ""
10020
+ * }
10021
+ * } );
10022
+ *
10023
+ * @example
10024
+ * // Manipulate the data returned from the server - add a link to data
10025
+ * // (note this can, should, be done using `render` for the column - this
10026
+ * // is just a simple example of how the data can be manipulated).
10027
+ * $('#example').dataTable( {
10028
+ * "ajax": {
10029
+ * "url": "data.json",
10030
+ * "dataSrc": function ( json ) {
10031
+ * for ( var i=0, ien=json.length ; i<ien ; i++ ) {
10032
+ * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
10033
+ * }
10034
+ * return json;
10035
+ * }
10036
+ * }
10037
+ * } );
10038
+ *
10039
+ * @example
10040
+ * // Add data to the request
10041
+ * $('#example').dataTable( {
10042
+ * "ajax": {
10043
+ * "url": "data.json",
10044
+ * "data": function ( d ) {
10045
+ * return {
10046
+ * "extra_search": $('#extra').val()
10047
+ * };
10048
+ * }
10049
+ * }
10050
+ * } );
10051
+ *
10052
+ * @example
10053
+ * // Send request as POST
10054
+ * $('#example').dataTable( {
10055
+ * "ajax": {
10056
+ * "url": "data.json",
10057
+ * "type": "POST"
10058
+ * }
10059
+ * } );
10060
+ *
10061
+ * @example
10062
+ * // Get the data from localStorage (could interface with a form for
10063
+ * // adding, editing and removing rows).
10064
+ * $('#example').dataTable( {
10065
+ * "ajax": function (data, callback, settings) {
10066
+ * callback(
10067
+ * JSON.parse( localStorage.getItem('dataTablesData') )
10068
+ * );
10069
+ * }
10070
+ * } );
10071
+ */
10072
+ "ajax": null,
10073
+
10074
+
10075
+ /**
10076
+ * This parameter allows you to readily specify the entries in the length drop
10077
+ * down menu that DataTables shows when pagination is enabled. It can be
10078
+ * either a 1D array of options which will be used for both the displayed
10079
+ * option and the value, or a 2D array which will use the array in the first
10080
+ * position as the value, and the array in the second position as the
10081
+ * displayed options (useful for language strings such as 'All').
10082
+ *
10083
+ * Note that the `pageLength` property will be automatically set to the
10084
+ * first value given in this array, unless `pageLength` is also provided.
10085
+ * @type array
10086
+ * @default [ 10, 25, 50, 100 ]
10087
+ *
10088
+ * @dtopt Option
10089
+ * @name DataTable.defaults.lengthMenu
10090
+ *
10091
+ * @example
10092
+ * $(document).ready( function() {
10093
+ * $('#example').dataTable( {
10094
+ * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
10095
+ * } );
10096
+ * } );
10097
+ */
10098
+ "aLengthMenu": [ 10, 25, 50, 100 ],
10099
+
10100
+
10101
+ /**
10102
+ * The `columns` option in the initialisation parameter allows you to define
10103
+ * details about the way individual columns behave. For a full list of
10104
+ * column options that can be set, please see
10105
+ * {@link DataTable.defaults.column}. Note that if you use `columns` to
10106
+ * define your columns, you must have an entry in the array for every single
10107
+ * column that you have in your table (these can be null if you don't which
10108
+ * to specify any options).
10109
+ * @member
10110
+ *
10111
+ * @name DataTable.defaults.column
10112
+ */
10113
+ "aoColumns": null,
10114
+
10115
+ /**
10116
+ * Very similar to `columns`, `columnDefs` allows you to target a specific
10117
+ * column, multiple columns, or all columns, using the `targets` property of
10118
+ * each object in the array. This allows great flexibility when creating
10119
+ * tables, as the `columnDefs` arrays can be of any length, targeting the
10120
+ * columns you specifically want. `columnDefs` may use any of the column
10121
+ * options available: {@link DataTable.defaults.column}, but it _must_
10122
+ * have `targets` defined in each object in the array. Values in the `targets`
10123
+ * array may be:
10124
+ * <ul>
10125
+ * <li>a string - class name will be matched on the TH for the column</li>
10126
+ * <li>0 or a positive integer - column index counting from the left</li>
10127
+ * <li>a negative integer - column index counting from the right</li>
10128
+ * <li>the string "_all" - all columns (i.e. assign a default)</li>
10129
+ * </ul>
10130
+ * @member
10131
+ *
10132
+ * @name DataTable.defaults.columnDefs
10133
+ */
10134
+ "aoColumnDefs": null,
10135
+
10136
+
10137
+ /**
10138
+ * Basically the same as `search`, this parameter defines the individual column
10139
+ * filtering state at initialisation time. The array must be of the same size
10140
+ * as the number of columns, and each element be an object with the parameters
10141
+ * `search` and `escapeRegex` (the latter is optional). 'null' is also
10142
+ * accepted and the default will be used.
10143
+ * @type array
10144
+ * @default []
10145
+ *
10146
+ * @dtopt Option
10147
+ * @name DataTable.defaults.searchCols
10148
+ *
10149
+ * @example
10150
+ * $(document).ready( function() {
10151
+ * $('#example').dataTable( {
10152
+ * "searchCols": [
10153
+ * null,
10154
+ * { "search": "My filter" },
10155
+ * null,
10156
+ * { "search": "^[0-9]", "escapeRegex": false }
10157
+ * ]
10158
+ * } );
10159
+ * } )
10160
+ */
10161
+ "aoSearchCols": [],
10162
+
10163
+
10164
+ /**
10165
+ * An array of CSS classes that should be applied to displayed rows. This
10166
+ * array may be of any length, and DataTables will apply each class
10167
+ * sequentially, looping when required.
10168
+ * @type array
10169
+ * @default null <i>Will take the values determined by the `oClasses.stripe*`
10170
+ * options</i>
10171
+ *
10172
+ * @dtopt Option
10173
+ * @name DataTable.defaults.stripeClasses
10174
+ *
10175
+ * @example
10176
+ * $(document).ready( function() {
10177
+ * $('#example').dataTable( {
10178
+ * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
10179
+ * } );
10180
+ * } )
10181
+ */
10182
+ "asStripeClasses": null,
10183
+
10184
+
10185
+ /**
10186
+ * Enable or disable automatic column width calculation. This can be disabled
10187
+ * as an optimisation (it takes some time to calculate the widths) if the
10188
+ * tables widths are passed in using `columns`.
10189
+ * @type boolean
10190
+ * @default true
10191
+ *
10192
+ * @dtopt Features
10193
+ * @name DataTable.defaults.autoWidth
10194
+ *
10195
+ * @example
10196
+ * $(document).ready( function () {
10197
+ * $('#example').dataTable( {
10198
+ * "autoWidth": false
10199
+ * } );
10200
+ * } );
10201
+ */
10202
+ "bAutoWidth": true,
10203
+
10204
+
10205
+ /**
10206
+ * Deferred rendering can provide DataTables with a huge speed boost when you
10207
+ * are using an Ajax or JS data source for the table. This option, when set to
10208
+ * true, will cause DataTables to defer the creation of the table elements for
10209
+ * each row until they are needed for a draw - saving a significant amount of
10210
+ * time.
10211
+ * @type boolean
10212
+ * @default false
10213
+ *
10214
+ * @dtopt Features
10215
+ * @name DataTable.defaults.deferRender
10216
+ *
10217
+ * @example
10218
+ * $(document).ready( function() {
10219
+ * $('#example').dataTable( {
10220
+ * "ajax": "sources/arrays.txt",
10221
+ * "deferRender": true
10222
+ * } );
10223
+ * } );
10224
+ */
10225
+ "bDeferRender": false,
10226
+
10227
+
10228
+ /**
10229
+ * Replace a DataTable which matches the given selector and replace it with
10230
+ * one which has the properties of the new initialisation object passed. If no
10231
+ * table matches the selector, then the new DataTable will be constructed as
10232
+ * per normal.
10233
+ * @type boolean
10234
+ * @default false
10235
+ *
10236
+ * @dtopt Options
10237
+ * @name DataTable.defaults.destroy
10238
+ *
10239
+ * @example
10240
+ * $(document).ready( function() {
10241
+ * $('#example').dataTable( {
10242
+ * "srollY": "200px",
10243
+ * "paginate": false
10244
+ * } );
10245
+ *
10246
+ * // Some time later....
10247
+ * $('#example').dataTable( {
10248
+ * "filter": false,
10249
+ * "destroy": true
10250
+ * } );
10251
+ * } );
10252
+ */
10253
+ "bDestroy": false,
10254
+
10255
+
10256
+ /**
10257
+ * Enable or disable filtering of data. Filtering in DataTables is "smart" in
10258
+ * that it allows the end user to input multiple words (space separated) and
10259
+ * will match a row containing those words, even if not in the order that was
10260
+ * specified (this allow matching across multiple columns). Note that if you
10261
+ * wish to use filtering in DataTables this must remain 'true' - to remove the
10262
+ * default filtering input box and retain filtering abilities, please use
10263
+ * {@link DataTable.defaults.dom}.
10264
+ * @type boolean
10265
+ * @default true
10266
+ *
10267
+ * @dtopt Features
10268
+ * @name DataTable.defaults.searching
10269
+ *
10270
+ * @example
10271
+ * $(document).ready( function () {
10272
+ * $('#example').dataTable( {
10273
+ * "searching": false
10274
+ * } );
10275
+ * } );
10276
+ */
10277
+ "bFilter": true,
10278
+
10279
+
10280
+ /**
10281
+ * Enable or disable the table information display. This shows information
10282
+ * about the data that is currently visible on the page, including information
10283
+ * about filtered data if that action is being performed.
10284
+ * @type boolean
10285
+ * @default true
10286
+ *
10287
+ * @dtopt Features
10288
+ * @name DataTable.defaults.info
10289
+ *
10290
+ * @example
10291
+ * $(document).ready( function () {
10292
+ * $('#example').dataTable( {
10293
+ * "info": false
10294
+ * } );
10295
+ * } );
10296
+ */
10297
+ "bInfo": true,
10298
+
10299
+
10300
+ /**
10301
+ * Enable jQuery UI ThemeRoller support (required as ThemeRoller requires some
10302
+ * slightly different and additional mark-up from what DataTables has
10303
+ * traditionally used).
10304
+ * @type boolean
10305
+ * @default false
10306
+ *
10307
+ * @dtopt Features
10308
+ * @name DataTable.defaults.jQueryUI
10309
+ *
10310
+ * @example
10311
+ * $(document).ready( function() {
10312
+ * $('#example').dataTable( {
10313
+ * "jQueryUI": true
10314
+ * } );
10315
+ * } );
10316
+ */
10317
+ "bJQueryUI": false,
10318
+
10319
+
10320
+ /**
10321
+ * Allows the end user to select the size of a formatted page from a select
10322
+ * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
10323
+ * @type boolean
10324
+ * @default true
10325
+ *
10326
+ * @dtopt Features
10327
+ * @name DataTable.defaults.lengthChange
10328
+ *
10329
+ * @example
10330
+ * $(document).ready( function () {
10331
+ * $('#example').dataTable( {
10332
+ * "lengthChange": false
10333
+ * } );
10334
+ * } );
10335
+ */
10336
+ "bLengthChange": true,
10337
+
10338
+
10339
+ /**
10340
+ * Enable or disable pagination.
10341
+ * @type boolean
10342
+ * @default true
10343
+ *
10344
+ * @dtopt Features
10345
+ * @name DataTable.defaults.paging
10346
+ *
10347
+ * @example
10348
+ * $(document).ready( function () {
10349
+ * $('#example').dataTable( {
10350
+ * "paging": false
10351
+ * } );
10352
+ * } );
10353
+ */
10354
+ "bPaginate": true,
10355
+
10356
+
10357
+ /**
10358
+ * Enable or disable the display of a 'processing' indicator when the table is
10359
+ * being processed (e.g. a sort). This is particularly useful for tables with
10360
+ * large amounts of data where it can take a noticeable amount of time to sort
10361
+ * the entries.
10362
+ * @type boolean
10363
+ * @default false
10364
+ *
10365
+ * @dtopt Features
10366
+ * @name DataTable.defaults.processing
10367
+ *
10368
+ * @example
10369
+ * $(document).ready( function () {
10370
+ * $('#example').dataTable( {
10371
+ * "processing": true
10372
+ * } );
10373
+ * } );
10374
+ */
10375
+ "bProcessing": false,
10376
+
10377
+
10378
+ /**
10379
+ * Retrieve the DataTables object for the given selector. Note that if the
10380
+ * table has already been initialised, this parameter will cause DataTables
10381
+ * to simply return the object that has already been set up - it will not take
10382
+ * account of any changes you might have made to the initialisation object
10383
+ * passed to DataTables (setting this parameter to true is an acknowledgement
10384
+ * that you understand this). `destroy` can be used to reinitialise a table if
10385
+ * you need.
10386
+ * @type boolean
10387
+ * @default false
10388
+ *
10389
+ * @dtopt Options
10390
+ * @name DataTable.defaults.retrieve
10391
+ *
10392
+ * @example
10393
+ * $(document).ready( function() {
10394
+ * initTable();
10395
+ * tableActions();
10396
+ * } );
10397
+ *
10398
+ * function initTable ()
10399
+ * {
10400
+ * return $('#example').dataTable( {
10401
+ * "scrollY": "200px",
10402
+ * "paginate": false,
10403
+ * "retrieve": true
10404
+ * } );
10405
+ * }
10406
+ *
10407
+ * function tableActions ()
10408
+ * {
10409
+ * var table = initTable();
10410
+ * // perform API operations with oTable
10411
+ * }
10412
+ */
10413
+ "bRetrieve": false,
10414
+
10415
+
10416
+ /**
10417
+ * When vertical (y) scrolling is enabled, DataTables will force the height of
10418
+ * the table's viewport to the given height at all times (useful for layout).
10419
+ * However, this can look odd when filtering data down to a small data set,
10420
+ * and the footer is left "floating" further down. This parameter (when
10421
+ * enabled) will cause DataTables to collapse the table's viewport down when
10422
+ * the result set will fit within the given Y height.
10423
+ * @type boolean
10424
+ * @default false
10425
+ *
10426
+ * @dtopt Options
10427
+ * @name DataTable.defaults.scrollCollapse
10428
+ *
10429
+ * @example
10430
+ * $(document).ready( function() {
10431
+ * $('#example').dataTable( {
10432
+ * "scrollY": "200",
10433
+ * "scrollCollapse": true
10434
+ * } );
10435
+ * } );
10436
+ */
10437
+ "bScrollCollapse": false,
10438
+
10439
+
10440
+ /**
10441
+ * Configure DataTables to use server-side processing. Note that the
10442
+ * `ajax` parameter must also be given in order to give DataTables a
10443
+ * source to obtain the required data for each draw.
10444
+ * @type boolean
10445
+ * @default false
10446
+ *
10447
+ * @dtopt Features
10448
+ * @dtopt Server-side
10449
+ * @name DataTable.defaults.serverSide
10450
+ *
10451
+ * @example
10452
+ * $(document).ready( function () {
10453
+ * $('#example').dataTable( {
10454
+ * "serverSide": true,
10455
+ * "ajax": "xhr.php"
10456
+ * } );
10457
+ * } );
10458
+ */
10459
+ "bServerSide": false,
10460
+
10461
+
10462
+ /**
10463
+ * Enable or disable sorting of columns. Sorting of individual columns can be
10464
+ * disabled by the `sortable` option for each column.
10465
+ * @type boolean
10466
+ * @default true
10467
+ *
10468
+ * @dtopt Features
10469
+ * @name DataTable.defaults.ordering
10470
+ *
10471
+ * @example
10472
+ * $(document).ready( function () {
10473
+ * $('#example').dataTable( {
10474
+ * "ordering": false
10475
+ * } );
10476
+ * } );
10477
+ */
10478
+ "bSort": true,
10479
+
10480
+
10481
+ /**
10482
+ * Enable or display DataTables' ability to sort multiple columns at the
10483
+ * same time (activated by shift-click by the user).
10484
+ * @type boolean
10485
+ * @default true
10486
+ *
10487
+ * @dtopt Options
10488
+ * @name DataTable.defaults.orderMulti
10489
+ *
10490
+ * @example
10491
+ * // Disable multiple column sorting ability
10492
+ * $(document).ready( function () {
10493
+ * $('#example').dataTable( {
10494
+ * "orderMulti": false
10495
+ * } );
10496
+ * } );
10497
+ */
10498
+ "bSortMulti": true,
10499
+
10500
+
10501
+ /**
10502
+ * Allows control over whether DataTables should use the top (true) unique
10503
+ * cell that is found for a single column, or the bottom (false - default).
10504
+ * This is useful when using complex headers.
10505
+ * @type boolean
10506
+ * @default false
10507
+ *
10508
+ * @dtopt Options
10509
+ * @name DataTable.defaults.orderCellsTop
10510
+ *
10511
+ * @example
10512
+ * $(document).ready( function() {
10513
+ * $('#example').dataTable( {
10514
+ * "orderCellsTop": true
10515
+ * } );
10516
+ * } );
10517
+ */
10518
+ "bSortCellsTop": false,
10519
+
10520
+
10521
+ /**
10522
+ * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
10523
+ * `sorting\_3` to the columns which are currently being sorted on. This is
10524
+ * presented as a feature switch as it can increase processing time (while
10525
+ * classes are removed and added) so for large data sets you might want to
10526
+ * turn this off.
10527
+ * @type boolean
10528
+ * @default true
10529
+ *
10530
+ * @dtopt Features
10531
+ * @name DataTable.defaults.orderClasses
10532
+ *
10533
+ * @example
10534
+ * $(document).ready( function () {
10535
+ * $('#example').dataTable( {
10536
+ * "orderClasses": false
10537
+ * } );
10538
+ * } );
10539
+ */
10540
+ "bSortClasses": true,
10541
+
10542
+
10543
+ /**
10544
+ * Enable or disable state saving. When enabled HTML5 `localStorage` will be
10545
+ * used to save table display information such as pagination information,
10546
+ * display length, filtering and sorting. As such when the end user reloads
10547
+ * the page the display display will match what thy had previously set up.
10548
+ *
10549
+ * Due to the use of `localStorage` the default state saving is not supported
10550
+ * in IE6 or 7. If state saving is required in those browsers, use
10551
+ * `stateSaveCallback` to provide a storage solution such as cookies.
10552
+ * @type boolean
10553
+ * @default false
10554
+ *
10555
+ * @dtopt Features
10556
+ * @name DataTable.defaults.stateSave
10557
+ *
10558
+ * @example
10559
+ * $(document).ready( function () {
10560
+ * $('#example').dataTable( {
10561
+ * "stateSave": true
10562
+ * } );
10563
+ * } );
10564
+ */
10565
+ "bStateSave": false,
10566
+
10567
+
10568
+ /**
10569
+ * This function is called when a TR element is created (and all TD child
10570
+ * elements have been inserted), or registered if using a DOM source, allowing
10571
+ * manipulation of the TR element (adding classes etc).
10572
+ * @type function
10573
+ * @param {node} row "TR" element for the current row
10574
+ * @param {array} data Raw data array for this row
10575
+ * @param {int} dataIndex The index of this row in the internal aoData array
10576
+ *
10577
+ * @dtopt Callbacks
10578
+ * @name DataTable.defaults.createdRow
10579
+ *
10580
+ * @example
10581
+ * $(document).ready( function() {
10582
+ * $('#example').dataTable( {
10583
+ * "createdRow": function( row, data, dataIndex ) {
10584
+ * // Bold the grade for all 'A' grade browsers
10585
+ * if ( data[4] == "A" )
10586
+ * {
10587
+ * $('td:eq(4)', row).html( '<b>A</b>' );
10588
+ * }
10589
+ * }
10590
+ * } );
10591
+ * } );
10592
+ */
10593
+ "fnCreatedRow": null,
10594
+
10595
+
10596
+ /**
10597
+ * This function is called on every 'draw' event, and allows you to
10598
+ * dynamically modify any aspect you want about the created DOM.
10599
+ * @type function
10600
+ * @param {object} settings DataTables settings object
10601
+ *
10602
+ * @dtopt Callbacks
10603
+ * @name DataTable.defaults.drawCallback
10604
+ *
10605
+ * @example
10606
+ * $(document).ready( function() {
10607
+ * $('#example').dataTable( {
10608
+ * "drawCallback": function( settings ) {
10609
+ * alert( 'DataTables has redrawn the table' );
10610
+ * }
10611
+ * } );
10612
+ * } );
10613
+ */
10614
+ "fnDrawCallback": null,
10615
+
10616
+
10617
+ /**
10618
+ * Identical to fnHeaderCallback() but for the table footer this function
10619
+ * allows you to modify the table footer on every 'draw' event.
10620
+ * @type function
10621
+ * @param {node} foot "TR" element for the footer
10622
+ * @param {array} data Full table data (as derived from the original HTML)
10623
+ * @param {int} start Index for the current display starting point in the
10624
+ * display array
10625
+ * @param {int} end Index for the current display ending point in the
10626
+ * display array
10627
+ * @param {array int} display Index array to translate the visual position
10628
+ * to the full data array
10629
+ *
10630
+ * @dtopt Callbacks
10631
+ * @name DataTable.defaults.footerCallback
10632
+ *
10633
+ * @example
10634
+ * $(document).ready( function() {
10635
+ * $('#example').dataTable( {
10636
+ * "footerCallback": function( tfoot, data, start, end, display ) {
10637
+ * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
10638
+ * }
10639
+ * } );
10640
+ * } )
10641
+ */
10642
+ "fnFooterCallback": null,
10643
+
10644
+
10645
+ /**
10646
+ * When rendering large numbers in the information element for the table
10647
+ * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
10648
+ * to have a comma separator for the 'thousands' units (e.g. 1 million is
10649
+ * rendered as "1,000,000") to help readability for the end user. This
10650
+ * function will override the default method DataTables uses.
10651
+ * @type function
10652
+ * @member
10653
+ * @param {int} toFormat number to be formatted
10654
+ * @returns {string} formatted string for DataTables to show the number
10655
+ *
10656
+ * @dtopt Callbacks
10657
+ * @name DataTable.defaults.formatNumber
10658
+ *
10659
+ * @example
10660
+ * // Format a number using a single quote for the separator (note that
10661
+ * // this can also be done with the language.thousands option)
10662
+ * $(document).ready( function() {
10663
+ * $('#example').dataTable( {
10664
+ * "formatNumber": function ( toFormat ) {
10665
+ * return toFormat.toString().replace(
10666
+ * /\B(?=(\d{3})+(?!\d))/g, "'"
10667
+ * );
10668
+ * };
10669
+ * } );
10670
+ * } );
10671
+ */
10672
+ "fnFormatNumber": function ( toFormat ) {
10673
+ return toFormat.toString().replace(
10674
+ /\B(?=(\d{3})+(?!\d))/g,
10675
+ this.oLanguage.sThousands
10676
+ );
10677
+ },
10678
+
10679
+
10680
+ /**
10681
+ * This function is called on every 'draw' event, and allows you to
10682
+ * dynamically modify the header row. This can be used to calculate and
10683
+ * display useful information about the table.
10684
+ * @type function
10685
+ * @param {node} head "TR" element for the header
10686
+ * @param {array} data Full table data (as derived from the original HTML)
10687
+ * @param {int} start Index for the current display starting point in the
10688
+ * display array
10689
+ * @param {int} end Index for the current display ending point in the
10690
+ * display array
10691
+ * @param {array int} display Index array to translate the visual position
10692
+ * to the full data array
10693
+ *
10694
+ * @dtopt Callbacks
10695
+ * @name DataTable.defaults.headerCallback
10696
+ *
10697
+ * @example
10698
+ * $(document).ready( function() {
10699
+ * $('#example').dataTable( {
10700
+ * "fheaderCallback": function( head, data, start, end, display ) {
10701
+ * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
10702
+ * }
10703
+ * } );
10704
+ * } )
10705
+ */
10706
+ "fnHeaderCallback": null,
10707
+
10708
+
10709
+ /**
10710
+ * The information element can be used to convey information about the current
10711
+ * state of the table. Although the internationalisation options presented by
10712
+ * DataTables are quite capable of dealing with most customisations, there may
10713
+ * be times where you wish to customise the string further. This callback
10714
+ * allows you to do exactly that.
10715
+ * @type function
10716
+ * @param {object} oSettings DataTables settings object
10717
+ * @param {int} start Starting position in data for the draw
10718
+ * @param {int} end End position in data for the draw
10719
+ * @param {int} max Total number of rows in the table (regardless of
10720
+ * filtering)
10721
+ * @param {int} total Total number of rows in the data set, after filtering
10722
+ * @param {string} pre The string that DataTables has formatted using it's
10723
+ * own rules
10724
+ * @returns {string} The string to be displayed in the information element.
10725
+ *
10726
+ * @dtopt Callbacks
10727
+ * @name DataTable.defaults.infoCallback
10728
+ *
10729
+ * @example
10730
+ * $('#example').dataTable( {
10731
+ * "infoCallback": function( settings, start, end, max, total, pre ) {
10732
+ * return start +" to "+ end;
10733
+ * }
10734
+ * } );
10735
+ */
10736
+ "fnInfoCallback": null,
10737
+
10738
+
10739
+ /**
10740
+ * Called when the table has been initialised. Normally DataTables will
10741
+ * initialise sequentially and there will be no need for this function,
10742
+ * however, this does not hold true when using external language information
10743
+ * since that is obtained using an async XHR call.
10744
+ * @type function
10745
+ * @param {object} settings DataTables settings object
10746
+ * @param {object} json The JSON object request from the server - only
10747
+ * present if client-side Ajax sourced data is used
10748
+ *
10749
+ * @dtopt Callbacks
10750
+ * @name DataTable.defaults.initComplete
10751
+ *
10752
+ * @example
10753
+ * $(document).ready( function() {
10754
+ * $('#example').dataTable( {
10755
+ * "initComplete": function(settings, json) {
10756
+ * alert( 'DataTables has finished its initialisation.' );
10757
+ * }
10758
+ * } );
10759
+ * } )
10760
+ */
10761
+ "fnInitComplete": null,
10762
+
10763
+
10764
+ /**
10765
+ * Called at the very start of each table draw and can be used to cancel the
10766
+ * draw by returning false, any other return (including undefined) results in
10767
+ * the full draw occurring).
10768
+ * @type function
10769
+ * @param {object} settings DataTables settings object
10770
+ * @returns {boolean} False will cancel the draw, anything else (including no
10771
+ * return) will allow it to complete.
10772
+ *
10773
+ * @dtopt Callbacks
10774
+ * @name DataTable.defaults.preDrawCallback
10775
+ *
10776
+ * @example
10777
+ * $(document).ready( function() {
10778
+ * $('#example').dataTable( {
10779
+ * "preDrawCallback": function( settings ) {
10780
+ * if ( $('#test').val() == 1 ) {
10781
+ * return false;
10782
+ * }
10783
+ * }
10784
+ * } );
10785
+ * } );
10786
+ */
10787
+ "fnPreDrawCallback": null,
10788
+
10789
+
10790
+ /**
10791
+ * This function allows you to 'post process' each row after it have been
10792
+ * generated for each table draw, but before it is rendered on screen. This
10793
+ * function might be used for setting the row class name etc.
10794
+ * @type function
10795
+ * @param {node} row "TR" element for the current row
10796
+ * @param {array} data Raw data array for this row
10797
+ * @param {int} displayIndex The display index for the current table draw
10798
+ * @param {int} displayIndexFull The index of the data in the full list of
10799
+ * rows (after filtering)
10800
+ *
10801
+ * @dtopt Callbacks
10802
+ * @name DataTable.defaults.rowCallback
10803
+ *
10804
+ * @example
10805
+ * $(document).ready( function() {
10806
+ * $('#example').dataTable( {
10807
+ * "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
10808
+ * // Bold the grade for all 'A' grade browsers
10809
+ * if ( data[4] == "A" ) {
10810
+ * $('td:eq(4)', row).html( '<b>A</b>' );
10811
+ * }
10812
+ * }
10813
+ * } );
10814
+ * } );
10815
+ */
10816
+ "fnRowCallback": null,
10817
+
10818
+
10819
+ /**
10820
+ * __Deprecated__ The functionality provided by this parameter has now been
10821
+ * superseded by that provided through `ajax`, which should be used instead.
10822
+ *
10823
+ * This parameter allows you to override the default function which obtains
10824
+ * the data from the server so something more suitable for your application.
10825
+ * For example you could use POST data, or pull information from a Gears or
10826
+ * AIR database.
10827
+ * @type function
10828
+ * @member
10829
+ * @param {string} source HTTP source to obtain the data from (`ajax`)
10830
+ * @param {array} data A key/value pair object containing the data to send
10831
+ * to the server
10832
+ * @param {function} callback to be called on completion of the data get
10833
+ * process that will draw the data on the page.
10834
+ * @param {object} settings DataTables settings object
10835
+ *
10836
+ * @dtopt Callbacks
10837
+ * @dtopt Server-side
10838
+ * @name DataTable.defaults.serverData
10839
+ *
10840
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
10841
+ */
10842
+ "fnServerData": null,
10843
+
10844
+
10845
+ /**
10846
+ * __Deprecated__ The functionality provided by this parameter has now been
10847
+ * superseded by that provided through `ajax`, which should be used instead.
10848
+ *
10849
+ * It is often useful to send extra data to the server when making an Ajax
10850
+ * request - for example custom filtering information, and this callback
10851
+ * function makes it trivial to send extra information to the server. The
10852
+ * passed in parameter is the data set that has been constructed by
10853
+ * DataTables, and you can add to this or modify it as you require.
10854
+ * @type function
10855
+ * @param {array} data Data array (array of objects which are name/value
10856
+ * pairs) that has been constructed by DataTables and will be sent to the
10857
+ * server. In the case of Ajax sourced data with server-side processing
10858
+ * this will be an empty array, for server-side processing there will be a
10859
+ * significant number of parameters!
10860
+ * @returns {undefined} Ensure that you modify the data array passed in,
10861
+ * as this is passed by reference.
10862
+ *
10863
+ * @dtopt Callbacks
10864
+ * @dtopt Server-side
10865
+ * @name DataTable.defaults.serverParams
10866
+ *
10867
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
10868
+ */
10869
+ "fnServerParams": null,
10870
+
10871
+
10872
+ /**
10873
+ * Load the table state. With this function you can define from where, and how, the
10874
+ * state of a table is loaded. By default DataTables will load from `localStorage`
10875
+ * but you might wish to use a server-side database or cookies.
10876
+ * @type function
10877
+ * @member
10878
+ * @param {object} settings DataTables settings object
10879
+ * @return {object} The DataTables state object to be loaded
10880
+ *
10881
+ * @dtopt Callbacks
10882
+ * @name DataTable.defaults.stateLoadCallback
10883
+ *
10884
+ * @example
10885
+ * $(document).ready( function() {
10886
+ * $('#example').dataTable( {
10887
+ * "stateSave": true,
10888
+ * "stateLoadCallback": function (settings) {
10889
+ * var o;
10890
+ *
10891
+ * // Send an Ajax request to the server to get the data. Note that
10892
+ * // this is a synchronous request.
10893
+ * $.ajax( {
10894
+ * "url": "/state_load",
10895
+ * "async": false,
10896
+ * "dataType": "json",
10897
+ * "success": function (json) {
10898
+ * o = json;
10899
+ * }
10900
+ * } );
10901
+ *
10902
+ * return o;
10903
+ * }
10904
+ * } );
10905
+ * } );
10906
+ */
10907
+ "fnStateLoadCallback": function ( settings ) {
10908
+ try {
10909
+ return JSON.parse(
10910
+ (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
10911
+ 'DataTables_'+settings.sInstance+'_'+location.pathname
10912
+ )
10913
+ );
10914
+ } catch (e) {}
10915
+ },
10916
+
10917
+
10918
+ /**
10919
+ * Callback which allows modification of the saved state prior to loading that state.
10920
+ * This callback is called when the table is loading state from the stored data, but
10921
+ * prior to the settings object being modified by the saved state. Note that for
10922
+ * plug-in authors, you should use the `stateLoadParams` event to load parameters for
10923
+ * a plug-in.
10924
+ * @type function
10925
+ * @param {object} settings DataTables settings object
10926
+ * @param {object} data The state object that is to be loaded
10927
+ *
10928
+ * @dtopt Callbacks
10929
+ * @name DataTable.defaults.stateLoadParams
10930
+ *
10931
+ * @example
10932
+ * // Remove a saved filter, so filtering is never loaded
10933
+ * $(document).ready( function() {
10934
+ * $('#example').dataTable( {
10935
+ * "stateSave": true,
10936
+ * "stateLoadParams": function (settings, data) {
10937
+ * data.oSearch.sSearch = "";
10938
+ * }
10939
+ * } );
10940
+ * } );
10941
+ *
10942
+ * @example
10943
+ * // Disallow state loading by returning false
10944
+ * $(document).ready( function() {
10945
+ * $('#example').dataTable( {
10946
+ * "stateSave": true,
10947
+ * "stateLoadParams": function (settings, data) {
10948
+ * return false;
10949
+ * }
10950
+ * } );
10951
+ * } );
10952
+ */
10953
+ "fnStateLoadParams": null,
10954
+
10955
+
10956
+ /**
10957
+ * Callback that is called when the state has been loaded from the state saving method
10958
+ * and the DataTables settings object has been modified as a result of the loaded state.
10959
+ * @type function
10960
+ * @param {object} settings DataTables settings object
10961
+ * @param {object} data The state object that was loaded
10962
+ *
10963
+ * @dtopt Callbacks
10964
+ * @name DataTable.defaults.stateLoaded
10965
+ *
10966
+ * @example
10967
+ * // Show an alert with the filtering value that was saved
10968
+ * $(document).ready( function() {
10969
+ * $('#example').dataTable( {
10970
+ * "stateSave": true,
10971
+ * "stateLoaded": function (settings, data) {
10972
+ * alert( 'Saved filter was: '+data.oSearch.sSearch );
10973
+ * }
10974
+ * } );
10975
+ * } );
10976
+ */
10977
+ "fnStateLoaded": null,
10978
+
10979
+
10980
+ /**
10981
+ * Save the table state. This function allows you to define where and how the state
10982
+ * information for the table is stored By default DataTables will use `localStorage`
10983
+ * but you might wish to use a server-side database or cookies.
10984
+ * @type function
10985
+ * @member
10986
+ * @param {object} settings DataTables settings object
10987
+ * @param {object} data The state object to be saved
10988
+ *
10989
+ * @dtopt Callbacks
10990
+ * @name DataTable.defaults.stateSaveCallback
10991
+ *
10992
+ * @example
10993
+ * $(document).ready( function() {
10994
+ * $('#example').dataTable( {
10995
+ * "stateSave": true,
10996
+ * "stateSaveCallback": function (settings, data) {
10997
+ * // Send an Ajax request to the server with the state object
10998
+ * $.ajax( {
10999
+ * "url": "/state_save",
11000
+ * "data": data,
11001
+ * "dataType": "json",
11002
+ * "method": "POST"
11003
+ * "success": function () {}
11004
+ * } );
11005
+ * }
11006
+ * } );
11007
+ * } );
11008
+ */
11009
+ "fnStateSaveCallback": function ( settings, data ) {
11010
+ try {
11011
+ (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
11012
+ 'DataTables_'+settings.sInstance+'_'+location.pathname,
11013
+ JSON.stringify( data )
11014
+ );
11015
+ } catch (e) {}
11016
+ },
11017
+
11018
+
11019
+ /**
11020
+ * Callback which allows modification of the state to be saved. Called when the table
11021
+ * has changed state a new state save is required. This method allows modification of
11022
+ * the state saving object prior to actually doing the save, including addition or
11023
+ * other state properties or modification. Note that for plug-in authors, you should
11024
+ * use the `stateSaveParams` event to save parameters for a plug-in.
11025
+ * @type function
11026
+ * @param {object} settings DataTables settings object
11027
+ * @param {object} data The state object to be saved
11028
+ *
11029
+ * @dtopt Callbacks
11030
+ * @name DataTable.defaults.stateSaveParams
11031
+ *
11032
+ * @example
11033
+ * // Remove a saved filter, so filtering is never saved
11034
+ * $(document).ready( function() {
11035
+ * $('#example').dataTable( {
11036
+ * "stateSave": true,
11037
+ * "stateSaveParams": function (settings, data) {
11038
+ * data.oSearch.sSearch = "";
11039
+ * }
11040
+ * } );
11041
+ * } );
11042
+ */
11043
+ "fnStateSaveParams": null,
11044
+
11045
+
11046
+ /**
11047
+ * Duration for which the saved state information is considered valid. After this period
11048
+ * has elapsed the state will be returned to the default.
11049
+ * Value is given in seconds.
11050
+ * @type int
11051
+ * @default 7200 <i>(2 hours)</i>
11052
+ *
11053
+ * @dtopt Options
11054
+ * @name DataTable.defaults.stateDuration
11055
+ *
11056
+ * @example
11057
+ * $(document).ready( function() {
11058
+ * $('#example').dataTable( {
11059
+ * "stateDuration": 60*60*24; // 1 day
11060
+ * } );
11061
+ * } )
11062
+ */
11063
+ "iStateDuration": 7200,
11064
+
11065
+
11066
+ /**
11067
+ * When enabled DataTables will not make a request to the server for the first
11068
+ * page draw - rather it will use the data already on the page (no sorting etc
11069
+ * will be applied to it), thus saving on an XHR at load time. `deferLoading`
11070
+ * is used to indicate that deferred loading is required, but it is also used
11071
+ * to tell DataTables how many records there are in the full table (allowing
11072
+ * the information element and pagination to be displayed correctly). In the case
11073
+ * where a filtering is applied to the table on initial load, this can be
11074
+ * indicated by giving the parameter as an array, where the first element is
11075
+ * the number of records available after filtering and the second element is the
11076
+ * number of records without filtering (allowing the table information element
11077
+ * to be shown correctly).
11078
+ * @type int | array
11079
+ * @default null
11080
+ *
11081
+ * @dtopt Options
11082
+ * @name DataTable.defaults.deferLoading
11083
+ *
11084
+ * @example
11085
+ * // 57 records available in the table, no filtering applied
11086
+ * $(document).ready( function() {
11087
+ * $('#example').dataTable( {
11088
+ * "serverSide": true,
11089
+ * "ajax": "scripts/server_processing.php",
11090
+ * "deferLoading": 57
11091
+ * } );
11092
+ * } );
11093
+ *
11094
+ * @example
11095
+ * // 57 records after filtering, 100 without filtering (an initial filter applied)
11096
+ * $(document).ready( function() {
11097
+ * $('#example').dataTable( {
11098
+ * "serverSide": true,
11099
+ * "ajax": "scripts/server_processing.php",
11100
+ * "deferLoading": [ 57, 100 ],
11101
+ * "search": {
11102
+ * "search": "my_filter"
11103
+ * }
11104
+ * } );
11105
+ * } );
11106
+ */
11107
+ "iDeferLoading": null,
11108
+
11109
+
11110
+ /**
11111
+ * Number of rows to display on a single page when using pagination. If
11112
+ * feature enabled (`lengthChange`) then the end user will be able to override
11113
+ * this to a custom setting using a pop-up menu.
11114
+ * @type int
11115
+ * @default 10
11116
+ *
11117
+ * @dtopt Options
11118
+ * @name DataTable.defaults.pageLength
11119
+ *
11120
+ * @example
11121
+ * $(document).ready( function() {
11122
+ * $('#example').dataTable( {
11123
+ * "pageLength": 50
11124
+ * } );
11125
+ * } )
11126
+ */
11127
+ "iDisplayLength": 10,
11128
+
11129
+
11130
+ /**
11131
+ * Define the starting point for data display when using DataTables with
11132
+ * pagination. Note that this parameter is the number of records, rather than
11133
+ * the page number, so if you have 10 records per page and want to start on
11134
+ * the third page, it should be "20".
11135
+ * @type int
11136
+ * @default 0
11137
+ *
11138
+ * @dtopt Options
11139
+ * @name DataTable.defaults.displayStart
11140
+ *
11141
+ * @example
11142
+ * $(document).ready( function() {
11143
+ * $('#example').dataTable( {
11144
+ * "displayStart": 20
11145
+ * } );
11146
+ * } )
11147
+ */
11148
+ "iDisplayStart": 0,
11149
+
11150
+
11151
+ /**
11152
+ * By default DataTables allows keyboard navigation of the table (sorting, paging,
11153
+ * and filtering) by adding a `tabindex` attribute to the required elements. This
11154
+ * allows you to tab through the controls and press the enter key to activate them.
11155
+ * The tabindex is default 0, meaning that the tab follows the flow of the document.
11156
+ * You can overrule this using this parameter if you wish. Use a value of -1 to
11157
+ * disable built-in keyboard navigation.
11158
+ * @type int
11159
+ * @default 0
11160
+ *
11161
+ * @dtopt Options
11162
+ * @name DataTable.defaults.tabIndex
11163
+ *
11164
+ * @example
11165
+ * $(document).ready( function() {
11166
+ * $('#example').dataTable( {
11167
+ * "tabIndex": 1
11168
+ * } );
11169
+ * } );
11170
+ */
11171
+ "iTabIndex": 0,
11172
+
11173
+
11174
+ /**
11175
+ * Classes that DataTables assigns to the various components and features
11176
+ * that it adds to the HTML table. This allows classes to be configured
11177
+ * during initialisation in addition to through the static
11178
+ * {@link DataTable.ext.oStdClasses} object).
11179
+ * @namespace
11180
+ * @name DataTable.defaults.classes
11181
+ */
11182
+ "oClasses": {},
11183
+
11184
+
11185
+ /**
11186
+ * All strings that DataTables uses in the user interface that it creates
11187
+ * are defined in this object, allowing you to modified them individually or
11188
+ * completely replace them all as required.
11189
+ * @namespace
11190
+ * @name DataTable.defaults.language
11191
+ */
11192
+ "oLanguage": {
11193
+ /**
11194
+ * Strings that are used for WAI-ARIA labels and controls only (these are not
11195
+ * actually visible on the page, but will be read by screenreaders, and thus
11196
+ * must be internationalised as well).
11197
+ * @namespace
11198
+ * @name DataTable.defaults.language.aria
11199
+ */
11200
+ "oAria": {
11201
+ /**
11202
+ * ARIA label that is added to the table headers when the column may be
11203
+ * sorted ascending by activing the column (click or return when focused).
11204
+ * Note that the column header is prefixed to this string.
11205
+ * @type string
11206
+ * @default : activate to sort column ascending
11207
+ *
11208
+ * @dtopt Language
11209
+ * @name DataTable.defaults.language.aria.sortAscending
11210
+ *
11211
+ * @example
11212
+ * $(document).ready( function() {
11213
+ * $('#example').dataTable( {
11214
+ * "language": {
11215
+ * "aria": {
11216
+ * "sortAscending": " - click/return to sort ascending"
11217
+ * }
11218
+ * }
11219
+ * } );
11220
+ * } );
11221
+ */
11222
+ "sSortAscending": ": activate to sort column ascending",
11223
+
11224
+ /**
11225
+ * ARIA label that is added to the table headers when the column may be
11226
+ * sorted descending by activing the column (click or return when focused).
11227
+ * Note that the column header is prefixed to this string.
11228
+ * @type string
11229
+ * @default : activate to sort column ascending
11230
+ *
11231
+ * @dtopt Language
11232
+ * @name DataTable.defaults.language.aria.sortDescending
11233
+ *
11234
+ * @example
11235
+ * $(document).ready( function() {
11236
+ * $('#example').dataTable( {
11237
+ * "language": {
11238
+ * "aria": {
11239
+ * "sortDescending": " - click/return to sort descending"
11240
+ * }
11241
+ * }
11242
+ * } );
11243
+ * } );
11244
+ */
11245
+ "sSortDescending": ": activate to sort column descending"
11246
+ },
11247
+
11248
+ /**
11249
+ * Pagination string used by DataTables for the built-in pagination
11250
+ * control types.
11251
+ * @namespace
11252
+ * @name DataTable.defaults.language.paginate
11253
+ */
11254
+ "oPaginate": {
11255
+ /**
11256
+ * Text to use when using the 'full_numbers' type of pagination for the
11257
+ * button to take the user to the first page.
11258
+ * @type string
11259
+ * @default First
11260
+ *
11261
+ * @dtopt Language
11262
+ * @name DataTable.defaults.language.paginate.first
11263
+ *
11264
+ * @example
11265
+ * $(document).ready( function() {
11266
+ * $('#example').dataTable( {
11267
+ * "language": {
11268
+ * "paginate": {
11269
+ * "first": "First page"
11270
+ * }
11271
+ * }
11272
+ * } );
11273
+ * } );
11274
+ */
11275
+ "sFirst": "First",
11276
+
11277
+
11278
+ /**
11279
+ * Text to use when using the 'full_numbers' type of pagination for the
11280
+ * button to take the user to the last page.
11281
+ * @type string
11282
+ * @default Last
11283
+ *
11284
+ * @dtopt Language
11285
+ * @name DataTable.defaults.language.paginate.last
11286
+ *
11287
+ * @example
11288
+ * $(document).ready( function() {
11289
+ * $('#example').dataTable( {
11290
+ * "language": {
11291
+ * "paginate": {
11292
+ * "last": "Last page"
11293
+ * }
11294
+ * }
11295
+ * } );
11296
+ * } );
11297
+ */
11298
+ "sLast": "Last",
11299
+
11300
+
11301
+ /**
11302
+ * Text to use for the 'next' pagination button (to take the user to the
11303
+ * next page).
11304
+ * @type string
11305
+ * @default Next
11306
+ *
11307
+ * @dtopt Language
11308
+ * @name DataTable.defaults.language.paginate.next
11309
+ *
11310
+ * @example
11311
+ * $(document).ready( function() {
11312
+ * $('#example').dataTable( {
11313
+ * "language": {
11314
+ * "paginate": {
11315
+ * "next": "Next page"
11316
+ * }
11317
+ * }
11318
+ * } );
11319
+ * } );
11320
+ */
11321
+ "sNext": "Next",
11322
+
11323
+
11324
+ /**
11325
+ * Text to use for the 'previous' pagination button (to take the user to
11326
+ * the previous page).
11327
+ * @type string
11328
+ * @default Previous
11329
+ *
11330
+ * @dtopt Language
11331
+ * @name DataTable.defaults.language.paginate.previous
11332
+ *
11333
+ * @example
11334
+ * $(document).ready( function() {
11335
+ * $('#example').dataTable( {
11336
+ * "language": {
11337
+ * "paginate": {
11338
+ * "previous": "Previous page"
11339
+ * }
11340
+ * }
11341
+ * } );
11342
+ * } );
11343
+ */
11344
+ "sPrevious": "Previous"
11345
+ },
11346
+
11347
+ /**
11348
+ * This string is shown in preference to `zeroRecords` when the table is
11349
+ * empty of data (regardless of filtering). Note that this is an optional
11350
+ * parameter - if it is not given, the value of `zeroRecords` will be used
11351
+ * instead (either the default or given value).
11352
+ * @type string
11353
+ * @default No data available in table
11354
+ *
11355
+ * @dtopt Language
11356
+ * @name DataTable.defaults.language.emptyTable
11357
+ *
11358
+ * @example
11359
+ * $(document).ready( function() {
11360
+ * $('#example').dataTable( {
11361
+ * "language": {
11362
+ * "emptyTable": "No data available in table"
11363
+ * }
11364
+ * } );
11365
+ * } );
11366
+ */
11367
+ "sEmptyTable": "No data available in table",
11368
+
11369
+
11370
+ /**
11371
+ * This string gives information to the end user about the information
11372
+ * that is current on display on the page. The following tokens can be
11373
+ * used in the string and will be dynamically replaced as the table
11374
+ * display updates. This tokens can be placed anywhere in the string, or
11375
+ * removed as needed by the language requires:
11376
+ *
11377
+ * * `\_START\_` - Display index of the first record on the current page
11378
+ * * `\_END\_` - Display index of the last record on the current page
11379
+ * * `\_TOTAL\_` - Number of records in the table after filtering
11380
+ * * `\_MAX\_` - Number of records in the table without filtering
11381
+ * * `\_PAGE\_` - Current page number
11382
+ * * `\_PAGES\_` - Total number of pages of data in the table
11383
+ *
11384
+ * @type string
11385
+ * @default Showing _START_ to _END_ of _TOTAL_ entries
11386
+ *
11387
+ * @dtopt Language
11388
+ * @name DataTable.defaults.language.info
11389
+ *
11390
+ * @example
11391
+ * $(document).ready( function() {
11392
+ * $('#example').dataTable( {
11393
+ * "language": {
11394
+ * "info": "Showing page _PAGE_ of _PAGES_"
11395
+ * }
11396
+ * } );
11397
+ * } );
11398
+ */
11399
+ "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
11400
+
11401
+
11402
+ /**
11403
+ * Display information string for when the table is empty. Typically the
11404
+ * format of this string should match `info`.
11405
+ * @type string
11406
+ * @default Showing 0 to 0 of 0 entries
11407
+ *
11408
+ * @dtopt Language
11409
+ * @name DataTable.defaults.language.infoEmpty
11410
+ *
11411
+ * @example
11412
+ * $(document).ready( function() {
11413
+ * $('#example').dataTable( {
11414
+ * "language": {
11415
+ * "infoEmpty": "No entries to show"
11416
+ * }
11417
+ * } );
11418
+ * } );
11419
+ */
11420
+ "sInfoEmpty": "Showing 0 to 0 of 0 entries",
11421
+
11422
+
11423
+ /**
11424
+ * When a user filters the information in a table, this string is appended
11425
+ * to the information (`info`) to give an idea of how strong the filtering
11426
+ * is. The variable _MAX_ is dynamically updated.
11427
+ * @type string
11428
+ * @default (filtered from _MAX_ total entries)
11429
+ *
11430
+ * @dtopt Language
11431
+ * @name DataTable.defaults.language.infoFiltered
11432
+ *
11433
+ * @example
11434
+ * $(document).ready( function() {
11435
+ * $('#example').dataTable( {
11436
+ * "language": {
11437
+ * "infoFiltered": " - filtering from _MAX_ records"
11438
+ * }
11439
+ * } );
11440
+ * } );
11441
+ */
11442
+ "sInfoFiltered": "(filtered from _MAX_ total entries)",
11443
+
11444
+
11445
+ /**
11446
+ * If can be useful to append extra information to the info string at times,
11447
+ * and this variable does exactly that. This information will be appended to
11448
+ * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
11449
+ * being used) at all times.
11450
+ * @type string
11451
+ * @default <i>Empty string</i>
11452
+ *
11453
+ * @dtopt Language
11454
+ * @name DataTable.defaults.language.infoPostFix
11455
+ *
11456
+ * @example
11457
+ * $(document).ready( function() {
11458
+ * $('#example').dataTable( {
11459
+ * "language": {
11460
+ * "infoPostFix": "All records shown are derived from real information."
11461
+ * }
11462
+ * } );
11463
+ * } );
11464
+ */
11465
+ "sInfoPostFix": "",
11466
+
11467
+
11468
+ /**
11469
+ * This decimal place operator is a little different from the other
11470
+ * language options since DataTables doesn't output floating point
11471
+ * numbers, so it won't ever use this for display of a number. Rather,
11472
+ * what this parameter does is modify the sort methods of the table so
11473
+ * that numbers which are in a format which has a character other than
11474
+ * a period (`.`) as a decimal place will be sorted numerically.
11475
+ *
11476
+ * Note that numbers with different decimal places cannot be shown in
11477
+ * the same table and still be sortable, the table must be consistent.
11478
+ * However, multiple different tables on the page can use different
11479
+ * decimal place characters.
11480
+ * @type string
11481
+ * @default
11482
+ *
11483
+ * @dtopt Language
11484
+ * @name DataTable.defaults.language.decimal
11485
+ *
11486
+ * @example
11487
+ * $(document).ready( function() {
11488
+ * $('#example').dataTable( {
11489
+ * "language": {
11490
+ * "decimal": ","
11491
+ * "thousands": "."
11492
+ * }
11493
+ * } );
11494
+ * } );
11495
+ */
11496
+ "sDecimal": "",
11497
+
11498
+
11499
+ /**
11500
+ * DataTables has a build in number formatter (`formatNumber`) which is
11501
+ * used to format large numbers that are used in the table information.
11502
+ * By default a comma is used, but this can be trivially changed to any
11503
+ * character you wish with this parameter.
11504
+ * @type string
11505
+ * @default ,
11506
+ *
11507
+ * @dtopt Language
11508
+ * @name DataTable.defaults.language.thousands
11509
+ *
11510
+ * @example
11511
+ * $(document).ready( function() {
11512
+ * $('#example').dataTable( {
11513
+ * "language": {
11514
+ * "thousands": "'"
11515
+ * }
11516
+ * } );
11517
+ * } );
11518
+ */
11519
+ "sThousands": ",",
11520
+
11521
+
11522
+ /**
11523
+ * Detail the action that will be taken when the drop down menu for the
11524
+ * pagination length option is changed. The '_MENU_' variable is replaced
11525
+ * with a default select list of 10, 25, 50 and 100, and can be replaced
11526
+ * with a custom select box if required.
11527
+ * @type string
11528
+ * @default Show _MENU_ entries
11529
+ *
11530
+ * @dtopt Language
11531
+ * @name DataTable.defaults.language.lengthMenu
11532
+ *
11533
+ * @example
11534
+ * // Language change only
11535
+ * $(document).ready( function() {
11536
+ * $('#example').dataTable( {
11537
+ * "language": {
11538
+ * "lengthMenu": "Display _MENU_ records"
11539
+ * }
11540
+ * } );
11541
+ * } );
11542
+ *
11543
+ * @example
11544
+ * // Language and options change
11545
+ * $(document).ready( function() {
11546
+ * $('#example').dataTable( {
11547
+ * "language": {
11548
+ * "lengthMenu": 'Display <select>'+
11549
+ * '<option value="10">10</option>'+
11550
+ * '<option value="20">20</option>'+
11551
+ * '<option value="30">30</option>'+
11552
+ * '<option value="40">40</option>'+
11553
+ * '<option value="50">50</option>'+
11554
+ * '<option value="-1">All</option>'+
11555
+ * '</select> records'
11556
+ * }
11557
+ * } );
11558
+ * } );
11559
+ */
11560
+ "sLengthMenu": "Show _MENU_ entries",
11561
+
11562
+
11563
+ /**
11564
+ * When using Ajax sourced data and during the first draw when DataTables is
11565
+ * gathering the data, this message is shown in an empty row in the table to
11566
+ * indicate to the end user the the data is being loaded. Note that this
11567
+ * parameter is not used when loading data by server-side processing, just
11568
+ * Ajax sourced data with client-side processing.
11569
+ * @type string
11570
+ * @default Loading...
11571
+ *
11572
+ * @dtopt Language
11573
+ * @name DataTable.defaults.language.loadingRecords
11574
+ *
11575
+ * @example
11576
+ * $(document).ready( function() {
11577
+ * $('#example').dataTable( {
11578
+ * "language": {
11579
+ * "loadingRecords": "Please wait - loading..."
11580
+ * }
11581
+ * } );
11582
+ * } );
11583
+ */
11584
+ "sLoadingRecords": "Loading...",
11585
+
11586
+
11587
+ /**
11588
+ * Text which is displayed when the table is processing a user action
11589
+ * (usually a sort command or similar).
11590
+ * @type string
11591
+ * @default Processing...
11592
+ *
11593
+ * @dtopt Language
11594
+ * @name DataTable.defaults.language.processing
11595
+ *
11596
+ * @example
11597
+ * $(document).ready( function() {
11598
+ * $('#example').dataTable( {
11599
+ * "language": {
11600
+ * "processing": "DataTables is currently busy"
11601
+ * }
11602
+ * } );
11603
+ * } );
11604
+ */
11605
+ "sProcessing": "Processing...",
11606
+
11607
+
11608
+ /**
11609
+ * Details the actions that will be taken when the user types into the
11610
+ * filtering input text box. The variable "_INPUT_", if used in the string,
11611
+ * is replaced with the HTML text box for the filtering input allowing
11612
+ * control over where it appears in the string. If "_INPUT_" is not given
11613
+ * then the input box is appended to the string automatically.
11614
+ * @type string
11615
+ * @default Search:
11616
+ *
11617
+ * @dtopt Language
11618
+ * @name DataTable.defaults.language.search
11619
+ *
11620
+ * @example
11621
+ * // Input text box will be appended at the end automatically
11622
+ * $(document).ready( function() {
11623
+ * $('#example').dataTable( {
11624
+ * "language": {
11625
+ * "search": "Filter records:"
11626
+ * }
11627
+ * } );
11628
+ * } );
11629
+ *
11630
+ * @example
11631
+ * // Specify where the filter should appear
11632
+ * $(document).ready( function() {
11633
+ * $('#example').dataTable( {
11634
+ * "language": {
11635
+ * "search": "Apply filter _INPUT_ to table"
11636
+ * }
11637
+ * } );
11638
+ * } );
11639
+ */
11640
+ "sSearch": "Search:",
11641
+
11642
+
11643
+ /**
11644
+ * Assign a `placeholder` attribute to the search `input` element
11645
+ * @type string
11646
+ * @default
11647
+ *
11648
+ * @dtopt Language
11649
+ * @name DataTable.defaults.language.searchPlaceholder
11650
+ */
11651
+ "sSearchPlaceholder": "",
11652
+
11653
+
11654
+ /**
11655
+ * All of the language information can be stored in a file on the
11656
+ * server-side, which DataTables will look up if this parameter is passed.
11657
+ * It must store the URL of the language file, which is in a JSON format,
11658
+ * and the object has the same properties as the oLanguage object in the
11659
+ * initialiser object (i.e. the above parameters). Please refer to one of
11660
+ * the example language files to see how this works in action.
11661
+ * @type string
11662
+ * @default <i>Empty string - i.e. disabled</i>
11663
+ *
11664
+ * @dtopt Language
11665
+ * @name DataTable.defaults.language.url
11666
+ *
11667
+ * @example
11668
+ * $(document).ready( function() {
11669
+ * $('#example').dataTable( {
11670
+ * "language": {
11671
+ * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
11672
+ * }
11673
+ * } );
11674
+ * } );
11675
+ */
11676
+ "sUrl": "",
11677
+
11678
+
11679
+ /**
11680
+ * Text shown inside the table records when the is no information to be
11681
+ * displayed after filtering. `emptyTable` is shown when there is simply no
11682
+ * information in the table at all (regardless of filtering).
11683
+ * @type string
11684
+ * @default No matching records found
11685
+ *
11686
+ * @dtopt Language
11687
+ * @name DataTable.defaults.language.zeroRecords
11688
+ *
11689
+ * @example
11690
+ * $(document).ready( function() {
11691
+ * $('#example').dataTable( {
11692
+ * "language": {
11693
+ * "zeroRecords": "No records to display"
11694
+ * }
11695
+ * } );
11696
+ * } );
11697
+ */
11698
+ "sZeroRecords": "No matching records found"
11699
+ },
11700
+
11701
+
11702
+ /**
11703
+ * This parameter allows you to have define the global filtering state at
11704
+ * initialisation time. As an object the `search` parameter must be
11705
+ * defined, but all other parameters are optional. When `regex` is true,
11706
+ * the search string will be treated as a regular expression, when false
11707
+ * (default) it will be treated as a straight string. When `smart`
11708
+ * DataTables will use it's smart filtering methods (to word match at
11709
+ * any point in the data), when false this will not be done.
11710
+ * @namespace
11711
+ * @extends DataTable.models.oSearch
11712
+ *
11713
+ * @dtopt Options
11714
+ * @name DataTable.defaults.search
11715
+ *
11716
+ * @example
11717
+ * $(document).ready( function() {
11718
+ * $('#example').dataTable( {
11719
+ * "search": {"search": "Initial search"}
11720
+ * } );
11721
+ * } )
11722
+ */
11723
+ "oSearch": $.extend( {}, DataTable.models.oSearch ),
11724
+
11725
+
11726
+ /**
11727
+ * __Deprecated__ The functionality provided by this parameter has now been
11728
+ * superseded by that provided through `ajax`, which should be used instead.
11729
+ *
11730
+ * By default DataTables will look for the property `data` (or `aaData` for
11731
+ * compatibility with DataTables 1.9-) when obtaining data from an Ajax
11732
+ * source or for server-side processing - this parameter allows that
11733
+ * property to be changed. You can use Javascript dotted object notation to
11734
+ * get a data source for multiple levels of nesting.
11735
+ * @type string
11736
+ * @default data
11737
+ *
11738
+ * @dtopt Options
11739
+ * @dtopt Server-side
11740
+ * @name DataTable.defaults.ajaxDataProp
11741
+ *
11742
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
11743
+ */
11744
+ "sAjaxDataProp": "data",
11745
+
11746
+
11747
+ /**
11748
+ * __Deprecated__ The functionality provided by this parameter has now been
11749
+ * superseded by that provided through `ajax`, which should be used instead.
11750
+ *
11751
+ * You can instruct DataTables to load data from an external
11752
+ * source using this parameter (use aData if you want to pass data in you
11753
+ * already have). Simply provide a url a JSON object can be obtained from.
11754
+ * @type string
11755
+ * @default null
11756
+ *
11757
+ * @dtopt Options
11758
+ * @dtopt Server-side
11759
+ * @name DataTable.defaults.ajaxSource
11760
+ *
11761
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
11762
+ */
11763
+ "sAjaxSource": null,
11764
+
11765
+
11766
+ /**
11767
+ * This initialisation variable allows you to specify exactly where in the
11768
+ * DOM you want DataTables to inject the various controls it adds to the page
11769
+ * (for example you might want the pagination controls at the top of the
11770
+ * table). DIV elements (with or without a custom class) can also be added to
11771
+ * aid styling. The follow syntax is used:
11772
+ * <ul>
11773
+ * <li>The following options are allowed:
11774
+ * <ul>
11775
+ * <li>'l' - Length changing</li>
11776
+ * <li>'f' - Filtering input</li>
11777
+ * <li>'t' - The table!</li>
11778
+ * <li>'i' - Information</li>
11779
+ * <li>'p' - Pagination</li>
11780
+ * <li>'r' - pRocessing</li>
11781
+ * </ul>
11782
+ * </li>
11783
+ * <li>The following constants are allowed:
11784
+ * <ul>
11785
+ * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
11786
+ * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
11787
+ * </ul>
11788
+ * </li>
11789
+ * <li>The following syntax is expected:
11790
+ * <ul>
11791
+ * <li>'&lt;' and '&gt;' - div elements</li>
11792
+ * <li>'&lt;"class" and '&gt;' - div with a class</li>
11793
+ * <li>'&lt;"#id" and '&gt;' - div with an ID</li>
11794
+ * </ul>
11795
+ * </li>
11796
+ * <li>Examples:
11797
+ * <ul>
11798
+ * <li>'&lt;"wrapper"flipt&gt;'</li>
11799
+ * <li>'&lt;lf&lt;t&gt;ip&gt;'</li>
11800
+ * </ul>
11801
+ * </li>
11802
+ * </ul>
11803
+ * @type string
11804
+ * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
11805
+ * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
11806
+ *
11807
+ * @dtopt Options
11808
+ * @name DataTable.defaults.dom
11809
+ *
11810
+ * @example
11811
+ * $(document).ready( function() {
11812
+ * $('#example').dataTable( {
11813
+ * "dom": '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;'
11814
+ * } );
11815
+ * } );
11816
+ */
11817
+ "sDom": "lfrtip",
11818
+
11819
+
11820
+ /**
11821
+ * Search delay option. This will throttle full table searches that use the
11822
+ * DataTables provided search input element (it does not effect calls to
11823
+ * `dt-api search()`, providing a delay before the search is made.
11824
+ * @type integer
11825
+ * @default 0
11826
+ *
11827
+ * @dtopt Options
11828
+ * @name DataTable.defaults.searchDelay
11829
+ *
11830
+ * @example
11831
+ * $(document).ready( function() {
11832
+ * $('#example').dataTable( {
11833
+ * "searchDelay": 200
11834
+ * } );
11835
+ * } )
11836
+ */
11837
+ "searchDelay": null,
11838
+
11839
+
11840
+ /**
11841
+ * DataTables features four different built-in options for the buttons to
11842
+ * display for pagination control:
11843
+ *
11844
+ * * `simple` - 'Previous' and 'Next' buttons only
11845
+ * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
11846
+ * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
11847
+ * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus
11848
+ * page numbers
11849
+ *
11850
+ * Further methods can be added using {@link DataTable.ext.oPagination}.
11851
+ * @type string
11852
+ * @default simple_numbers
11853
+ *
11854
+ * @dtopt Options
11855
+ * @name DataTable.defaults.pagingType
11856
+ *
11857
+ * @example
11858
+ * $(document).ready( function() {
11859
+ * $('#example').dataTable( {
11860
+ * "pagingType": "full_numbers"
11861
+ * } );
11862
+ * } )
11863
+ */
11864
+ "sPaginationType": "simple_numbers",
11865
+
11866
+
11867
+ /**
11868
+ * Enable horizontal scrolling. When a table is too wide to fit into a
11869
+ * certain layout, or you have a large number of columns in the table, you
11870
+ * can enable x-scrolling to show the table in a viewport, which can be
11871
+ * scrolled. This property can be `true` which will allow the table to
11872
+ * scroll horizontally when needed, or any CSS unit, or a number (in which
11873
+ * case it will be treated as a pixel measurement). Setting as simply `true`
11874
+ * is recommended.
11875
+ * @type boolean|string
11876
+ * @default <i>blank string - i.e. disabled</i>
11877
+ *
11878
+ * @dtopt Features
11879
+ * @name DataTable.defaults.scrollX
11880
+ *
11881
+ * @example
11882
+ * $(document).ready( function() {
11883
+ * $('#example').dataTable( {
11884
+ * "scrollX": true,
11885
+ * "scrollCollapse": true
11886
+ * } );
11887
+ * } );
11888
+ */
11889
+ "sScrollX": "",
11890
+
11891
+
11892
+ /**
11893
+ * This property can be used to force a DataTable to use more width than it
11894
+ * might otherwise do when x-scrolling is enabled. For example if you have a
11895
+ * table which requires to be well spaced, this parameter is useful for
11896
+ * "over-sizing" the table, and thus forcing scrolling. This property can by
11897
+ * any CSS unit, or a number (in which case it will be treated as a pixel
11898
+ * measurement).
11899
+ * @type string
11900
+ * @default <i>blank string - i.e. disabled</i>
11901
+ *
11902
+ * @dtopt Options
11903
+ * @name DataTable.defaults.scrollXInner
11904
+ *
11905
+ * @example
11906
+ * $(document).ready( function() {
11907
+ * $('#example').dataTable( {
11908
+ * "scrollX": "100%",
11909
+ * "scrollXInner": "110%"
11910
+ * } );
11911
+ * } );
11912
+ */
11913
+ "sScrollXInner": "",
11914
+
11915
+
11916
+ /**
11917
+ * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
11918
+ * to the given height, and enable scrolling for any data which overflows the
11919
+ * current viewport. This can be used as an alternative to paging to display
11920
+ * a lot of data in a small area (although paging and scrolling can both be
11921
+ * enabled at the same time). This property can be any CSS unit, or a number
11922
+ * (in which case it will be treated as a pixel measurement).
11923
+ * @type string
11924
+ * @default <i>blank string - i.e. disabled</i>
11925
+ *
11926
+ * @dtopt Features
11927
+ * @name DataTable.defaults.scrollY
11928
+ *
11929
+ * @example
11930
+ * $(document).ready( function() {
11931
+ * $('#example').dataTable( {
11932
+ * "scrollY": "200px",
11933
+ * "paginate": false
11934
+ * } );
11935
+ * } );
11936
+ */
11937
+ "sScrollY": "",
11938
+
11939
+
11940
+ /**
11941
+ * __Deprecated__ The functionality provided by this parameter has now been
11942
+ * superseded by that provided through `ajax`, which should be used instead.
11943
+ *
11944
+ * Set the HTTP method that is used to make the Ajax call for server-side
11945
+ * processing or Ajax sourced data.
11946
+ * @type string
11947
+ * @default GET
11948
+ *
11949
+ * @dtopt Options
11950
+ * @dtopt Server-side
11951
+ * @name DataTable.defaults.serverMethod
11952
+ *
11953
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
11954
+ */
11955
+ "sServerMethod": "GET",
11956
+
11957
+
11958
+ /**
11959
+ * DataTables makes use of renderers when displaying HTML elements for
11960
+ * a table. These renderers can be added or modified by plug-ins to
11961
+ * generate suitable mark-up for a site. For example the Bootstrap
11962
+ * integration plug-in for DataTables uses a paging button renderer to
11963
+ * display pagination buttons in the mark-up required by Bootstrap.
11964
+ *
11965
+ * For further information about the renderers available see
11966
+ * DataTable.ext.renderer
11967
+ * @type string|object
11968
+ * @default null
11969
+ *
11970
+ * @name DataTable.defaults.renderer
11971
+ *
11972
+ */
11973
+ "renderer": null,
11974
+
11975
+
11976
+ /**
11977
+ * Set the data property name that DataTables should use to get a row's id
11978
+ * to set as the `id` property in the node.
11979
+ * @type string
11980
+ * @default DT_RowId
11981
+ *
11982
+ * @name DataTable.defaults.rowId
11983
+ */
11984
+ "rowId": "DT_RowId"
11985
+ };
11986
+
11987
+ _fnHungarianMap( DataTable.defaults );
11988
+
11989
+
11990
+
11991
+ /*
11992
+ * Developer note - See note in model.defaults.js about the use of Hungarian
11993
+ * notation and camel case.
11994
+ */
11995
+
11996
+ /**
11997
+ * Column options that can be given to DataTables at initialisation time.
11998
+ * @namespace
11999
+ */
12000
+ DataTable.defaults.column = {
12001
+ /**
12002
+ * Define which column(s) an order will occur on for this column. This
12003
+ * allows a column's ordering to take multiple columns into account when
12004
+ * doing a sort or use the data from a different column. For example first
12005
+ * name / last name columns make sense to do a multi-column sort over the
12006
+ * two columns.
12007
+ * @type array|int
12008
+ * @default null <i>Takes the value of the column index automatically</i>
12009
+ *
12010
+ * @name DataTable.defaults.column.orderData
12011
+ * @dtopt Columns
12012
+ *
12013
+ * @example
12014
+ * // Using `columnDefs`
12015
+ * $(document).ready( function() {
12016
+ * $('#example').dataTable( {
12017
+ * "columnDefs": [
12018
+ * { "orderData": [ 0, 1 ], "targets": [ 0 ] },
12019
+ * { "orderData": [ 1, 0 ], "targets": [ 1 ] },
12020
+ * { "orderData": 2, "targets": [ 2 ] }
12021
+ * ]
12022
+ * } );
12023
+ * } );
12024
+ *
12025
+ * @example
12026
+ * // Using `columns`
12027
+ * $(document).ready( function() {
12028
+ * $('#example').dataTable( {
12029
+ * "columns": [
12030
+ * { "orderData": [ 0, 1 ] },
12031
+ * { "orderData": [ 1, 0 ] },
12032
+ * { "orderData": 2 },
12033
+ * null,
12034
+ * null
12035
+ * ]
12036
+ * } );
12037
+ * } );
12038
+ */
12039
+ "aDataSort": null,
12040
+ "iDataSort": -1,
12041
+
12042
+
12043
+ /**
12044
+ * You can control the default ordering direction, and even alter the
12045
+ * behaviour of the sort handler (i.e. only allow ascending ordering etc)
12046
+ * using this parameter.
12047
+ * @type array
12048
+ * @default [ 'asc', 'desc' ]
12049
+ *
12050
+ * @name DataTable.defaults.column.orderSequence
12051
+ * @dtopt Columns
12052
+ *
12053
+ * @example
12054
+ * // Using `columnDefs`
12055
+ * $(document).ready( function() {
12056
+ * $('#example').dataTable( {
12057
+ * "columnDefs": [
12058
+ * { "orderSequence": [ "asc" ], "targets": [ 1 ] },
12059
+ * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
12060
+ * { "orderSequence": [ "desc" ], "targets": [ 3 ] }
12061
+ * ]
12062
+ * } );
12063
+ * } );
12064
+ *
12065
+ * @example
12066
+ * // Using `columns`
12067
+ * $(document).ready( function() {
12068
+ * $('#example').dataTable( {
12069
+ * "columns": [
12070
+ * null,
12071
+ * { "orderSequence": [ "asc" ] },
12072
+ * { "orderSequence": [ "desc", "asc", "asc" ] },
12073
+ * { "orderSequence": [ "desc" ] },
12074
+ * null
12075
+ * ]
12076
+ * } );
12077
+ * } );
12078
+ */
12079
+ "asSorting": [ 'asc', 'desc' ],
12080
+
12081
+
12082
+ /**
12083
+ * Enable or disable filtering on the data in this column.
12084
+ * @type boolean
12085
+ * @default true
12086
+ *
12087
+ * @name DataTable.defaults.column.searchable
12088
+ * @dtopt Columns
12089
+ *
12090
+ * @example
12091
+ * // Using `columnDefs`
12092
+ * $(document).ready( function() {
12093
+ * $('#example').dataTable( {
12094
+ * "columnDefs": [
12095
+ * { "searchable": false, "targets": [ 0 ] }
12096
+ * ] } );
12097
+ * } );
12098
+ *
12099
+ * @example
12100
+ * // Using `columns`
12101
+ * $(document).ready( function() {
12102
+ * $('#example').dataTable( {
12103
+ * "columns": [
12104
+ * { "searchable": false },
12105
+ * null,
12106
+ * null,
12107
+ * null,
12108
+ * null
12109
+ * ] } );
12110
+ * } );
12111
+ */
12112
+ "bSearchable": true,
12113
+
12114
+
12115
+ /**
12116
+ * Enable or disable ordering on this column.
12117
+ * @type boolean
12118
+ * @default true
12119
+ *
12120
+ * @name DataTable.defaults.column.orderable
12121
+ * @dtopt Columns
12122
+ *
12123
+ * @example
12124
+ * // Using `columnDefs`
12125
+ * $(document).ready( function() {
12126
+ * $('#example').dataTable( {
12127
+ * "columnDefs": [
12128
+ * { "orderable": false, "targets": [ 0 ] }
12129
+ * ] } );
12130
+ * } );
12131
+ *
12132
+ * @example
12133
+ * // Using `columns`
12134
+ * $(document).ready( function() {
12135
+ * $('#example').dataTable( {
12136
+ * "columns": [
12137
+ * { "orderable": false },
12138
+ * null,
12139
+ * null,
12140
+ * null,
12141
+ * null
12142
+ * ] } );
12143
+ * } );
12144
+ */
12145
+ "bSortable": true,
12146
+
12147
+
12148
+ /**
12149
+ * Enable or disable the display of this column.
12150
+ * @type boolean
12151
+ * @default true
12152
+ *
12153
+ * @name DataTable.defaults.column.visible
12154
+ * @dtopt Columns
12155
+ *
12156
+ * @example
12157
+ * // Using `columnDefs`
12158
+ * $(document).ready( function() {
12159
+ * $('#example').dataTable( {
12160
+ * "columnDefs": [
12161
+ * { "visible": false, "targets": [ 0 ] }
12162
+ * ] } );
12163
+ * } );
12164
+ *
12165
+ * @example
12166
+ * // Using `columns`
12167
+ * $(document).ready( function() {
12168
+ * $('#example').dataTable( {
12169
+ * "columns": [
12170
+ * { "visible": false },
12171
+ * null,
12172
+ * null,
12173
+ * null,
12174
+ * null
12175
+ * ] } );
12176
+ * } );
12177
+ */
12178
+ "bVisible": true,
12179
+
12180
+
12181
+ /**
12182
+ * Developer definable function that is called whenever a cell is created (Ajax source,
12183
+ * etc) or processed for input (DOM source). This can be used as a compliment to mRender
12184
+ * allowing you to modify the DOM element (add background colour for example) when the
12185
+ * element is available.
12186
+ * @type function
12187
+ * @param {element} td The TD node that has been created
12188
+ * @param {*} cellData The Data for the cell
12189
+ * @param {array|object} rowData The data for the whole row
12190
+ * @param {int} row The row index for the aoData data store
12191
+ * @param {int} col The column index for aoColumns
12192
+ *
12193
+ * @name DataTable.defaults.column.createdCell
12194
+ * @dtopt Columns
12195
+ *
12196
+ * @example
12197
+ * $(document).ready( function() {
12198
+ * $('#example').dataTable( {
12199
+ * "columnDefs": [ {
12200
+ * "targets": [3],
12201
+ * "createdCell": function (td, cellData, rowData, row, col) {
12202
+ * if ( cellData == "1.7" ) {
12203
+ * $(td).css('color', 'blue')
12204
+ * }
12205
+ * }
12206
+ * } ]
12207
+ * });
12208
+ * } );
12209
+ */
12210
+ "fnCreatedCell": null,
12211
+
12212
+
12213
+ /**
12214
+ * This parameter has been replaced by `data` in DataTables to ensure naming
12215
+ * consistency. `dataProp` can still be used, as there is backwards
12216
+ * compatibility in DataTables for this option, but it is strongly
12217
+ * recommended that you use `data` in preference to `dataProp`.
12218
+ * @name DataTable.defaults.column.dataProp
12219
+ */
12220
+
12221
+
12222
+ /**
12223
+ * This property can be used to read data from any data source property,
12224
+ * including deeply nested objects / properties. `data` can be given in a
12225
+ * number of different ways which effect its behaviour:
12226
+ *
12227
+ * * `integer` - treated as an array index for the data source. This is the
12228
+ * default that DataTables uses (incrementally increased for each column).
12229
+ * * `string` - read an object property from the data source. There are
12230
+ * three 'special' options that can be used in the string to alter how
12231
+ * DataTables reads the data from the source object:
12232
+ * * `.` - Dotted Javascript notation. Just as you use a `.` in
12233
+ * Javascript to read from nested objects, so to can the options
12234
+ * specified in `data`. For example: `browser.version` or
12235
+ * `browser.name`. If your object parameter name contains a period, use
12236
+ * `\\` to escape it - i.e. `first\\.name`.
12237
+ * * `[]` - Array notation. DataTables can automatically combine data
12238
+ * from and array source, joining the data with the characters provided
12239
+ * between the two brackets. For example: `name[, ]` would provide a
12240
+ * comma-space separated list from the source array. If no characters
12241
+ * are provided between the brackets, the original array source is
12242
+ * returned.
12243
+ * * `()` - Function notation. Adding `()` to the end of a parameter will
12244
+ * execute a function of the name given. For example: `browser()` for a
12245
+ * simple function on the data source, `browser.version()` for a
12246
+ * function in a nested property or even `browser().version` to get an
12247
+ * object property if the function called returns an object. Note that
12248
+ * function notation is recommended for use in `render` rather than
12249
+ * `data` as it is much simpler to use as a renderer.
12250
+ * * `null` - use the original data source for the row rather than plucking
12251
+ * data directly from it. This action has effects on two other
12252
+ * initialisation options:
12253
+ * * `defaultContent` - When null is given as the `data` option and
12254
+ * `defaultContent` is specified for the column, the value defined by
12255
+ * `defaultContent` will be used for the cell.
12256
+ * * `render` - When null is used for the `data` option and the `render`
12257
+ * option is specified for the column, the whole data source for the
12258
+ * row is used for the renderer.
12259
+ * * `function` - the function given will be executed whenever DataTables
12260
+ * needs to set or get the data for a cell in the column. The function
12261
+ * takes three parameters:
12262
+ * * Parameters:
12263
+ * * `{array|object}` The data source for the row
12264
+ * * `{string}` The type call data requested - this will be 'set' when
12265
+ * setting data or 'filter', 'display', 'type', 'sort' or undefined
12266
+ * when gathering data. Note that when `undefined` is given for the
12267
+ * type DataTables expects to get the raw data for the object back<
12268
+ * * `{*}` Data to set when the second parameter is 'set'.
12269
+ * * Return:
12270
+ * * The return value from the function is not required when 'set' is
12271
+ * the type of call, but otherwise the return is what will be used
12272
+ * for the data requested.
12273
+ *
12274
+ * Note that `data` is a getter and setter option. If you just require
12275
+ * formatting of data for output, you will likely want to use `render` which
12276
+ * is simply a getter and thus simpler to use.
12277
+ *
12278
+ * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
12279
+ * name change reflects the flexibility of this property and is consistent
12280
+ * with the naming of mRender. If 'mDataProp' is given, then it will still
12281
+ * be used by DataTables, as it automatically maps the old name to the new
12282
+ * if required.
12283
+ *
12284
+ * @type string|int|function|null
12285
+ * @default null <i>Use automatically calculated column index</i>
12286
+ *
12287
+ * @name DataTable.defaults.column.data
12288
+ * @dtopt Columns
12289
+ *
12290
+ * @example
12291
+ * // Read table data from objects
12292
+ * // JSON structure for each row:
12293
+ * // {
12294
+ * // "engine": {value},
12295
+ * // "browser": {value},
12296
+ * // "platform": {value},
12297
+ * // "version": {value},
12298
+ * // "grade": {value}
12299
+ * // }
12300
+ * $(document).ready( function() {
12301
+ * $('#example').dataTable( {
12302
+ * "ajaxSource": "sources/objects.txt",
12303
+ * "columns": [
12304
+ * { "data": "engine" },
12305
+ * { "data": "browser" },
12306
+ * { "data": "platform" },
12307
+ * { "data": "version" },
12308
+ * { "data": "grade" }
12309
+ * ]
12310
+ * } );
12311
+ * } );
12312
+ *
12313
+ * @example
12314
+ * // Read information from deeply nested objects
12315
+ * // JSON structure for each row:
12316
+ * // {
12317
+ * // "engine": {value},
12318
+ * // "browser": {value},
12319
+ * // "platform": {
12320
+ * // "inner": {value}
12321
+ * // },
12322
+ * // "details": [
12323
+ * // {value}, {value}
12324
+ * // ]
12325
+ * // }
12326
+ * $(document).ready( function() {
12327
+ * $('#example').dataTable( {
12328
+ * "ajaxSource": "sources/deep.txt",
12329
+ * "columns": [
12330
+ * { "data": "engine" },
12331
+ * { "data": "browser" },
12332
+ * { "data": "platform.inner" },
12333
+ * { "data": "platform.details.0" },
12334
+ * { "data": "platform.details.1" }
12335
+ * ]
12336
+ * } );
12337
+ * } );
12338
+ *
12339
+ * @example
12340
+ * // Using `data` as a function to provide different information for
12341
+ * // sorting, filtering and display. In this case, currency (price)
12342
+ * $(document).ready( function() {
12343
+ * $('#example').dataTable( {
12344
+ * "columnDefs": [ {
12345
+ * "targets": [ 0 ],
12346
+ * "data": function ( source, type, val ) {
12347
+ * if (type === 'set') {
12348
+ * source.price = val;
12349
+ * // Store the computed dislay and filter values for efficiency
12350
+ * source.price_display = val=="" ? "" : "$"+numberFormat(val);
12351
+ * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val;
12352
+ * return;
12353
+ * }
12354
+ * else if (type === 'display') {
12355
+ * return source.price_display;
12356
+ * }
12357
+ * else if (type === 'filter') {
12358
+ * return source.price_filter;
12359
+ * }
12360
+ * // 'sort', 'type' and undefined all just use the integer
12361
+ * return source.price;
12362
+ * }
12363
+ * } ]
12364
+ * } );
12365
+ * } );
12366
+ *
12367
+ * @example
12368
+ * // Using default content
12369
+ * $(document).ready( function() {
12370
+ * $('#example').dataTable( {
12371
+ * "columnDefs": [ {
12372
+ * "targets": [ 0 ],
12373
+ * "data": null,
12374
+ * "defaultContent": "Click to edit"
12375
+ * } ]
12376
+ * } );
12377
+ * } );
12378
+ *
12379
+ * @example
12380
+ * // Using array notation - outputting a list from an array
12381
+ * $(document).ready( function() {
12382
+ * $('#example').dataTable( {
12383
+ * "columnDefs": [ {
12384
+ * "targets": [ 0 ],
12385
+ * "data": "name[, ]"
12386
+ * } ]
12387
+ * } );
12388
+ * } );
12389
+ *
12390
+ */
12391
+ "mData": null,
12392
+
12393
+
12394
+ /**
12395
+ * This property is the rendering partner to `data` and it is suggested that
12396
+ * when you want to manipulate data for display (including filtering,
12397
+ * sorting etc) without altering the underlying data for the table, use this
12398
+ * property. `render` can be considered to be the the read only companion to
12399
+ * `data` which is read / write (then as such more complex). Like `data`
12400
+ * this option can be given in a number of different ways to effect its
12401
+ * behaviour:
12402
+ *
12403
+ * * `integer` - treated as an array index for the data source. This is the
12404
+ * default that DataTables uses (incrementally increased for each column).
12405
+ * * `string` - read an object property from the data source. There are
12406
+ * three 'special' options that can be used in the string to alter how
12407
+ * DataTables reads the data from the source object:
12408
+ * * `.` - Dotted Javascript notation. Just as you use a `.` in
12409
+ * Javascript to read from nested objects, so to can the options
12410
+ * specified in `data`. For example: `browser.version` or
12411
+ * `browser.name`. If your object parameter name contains a period, use
12412
+ * `\\` to escape it - i.e. `first\\.name`.
12413
+ * * `[]` - Array notation. DataTables can automatically combine data
12414
+ * from and array source, joining the data with the characters provided
12415
+ * between the two brackets. For example: `name[, ]` would provide a
12416
+ * comma-space separated list from the source array. If no characters
12417
+ * are provided between the brackets, the original array source is
12418
+ * returned.
12419
+ * * `()` - Function notation. Adding `()` to the end of a parameter will
12420
+ * execute a function of the name given. For example: `browser()` for a
12421
+ * simple function on the data source, `browser.version()` for a
12422
+ * function in a nested property or even `browser().version` to get an
12423
+ * object property if the function called returns an object.
12424
+ * * `object` - use different data for the different data types requested by
12425
+ * DataTables ('filter', 'display', 'type' or 'sort'). The property names
12426
+ * of the object is the data type the property refers to and the value can
12427
+ * defined using an integer, string or function using the same rules as
12428
+ * `render` normally does. Note that an `_` option _must_ be specified.
12429
+ * This is the default value to use if you haven't specified a value for
12430
+ * the data type requested by DataTables.
12431
+ * * `function` - the function given will be executed whenever DataTables
12432
+ * needs to set or get the data for a cell in the column. The function
12433
+ * takes three parameters:
12434
+ * * Parameters:
12435
+ * * {array|object} The data source for the row (based on `data`)
12436
+ * * {string} The type call data requested - this will be 'filter',
12437
+ * 'display', 'type' or 'sort'.
12438
+ * * {array|object} The full data source for the row (not based on
12439
+ * `data`)
12440
+ * * Return:
12441
+ * * The return value from the function is what will be used for the
12442
+ * data requested.
12443
+ *
12444
+ * @type string|int|function|object|null
12445
+ * @default null Use the data source value.
12446
+ *
12447
+ * @name DataTable.defaults.column.render
12448
+ * @dtopt Columns
12449
+ *
12450
+ * @example
12451
+ * // Create a comma separated list from an array of objects
12452
+ * $(document).ready( function() {
12453
+ * $('#example').dataTable( {
12454
+ * "ajaxSource": "sources/deep.txt",
12455
+ * "columns": [
12456
+ * { "data": "engine" },
12457
+ * { "data": "browser" },
12458
+ * {
12459
+ * "data": "platform",
12460
+ * "render": "[, ].name"
12461
+ * }
12462
+ * ]
12463
+ * } );
12464
+ * } );
12465
+ *
12466
+ * @example
12467
+ * // Execute a function to obtain data
12468
+ * $(document).ready( function() {
12469
+ * $('#example').dataTable( {
12470
+ * "columnDefs": [ {
12471
+ * "targets": [ 0 ],
12472
+ * "data": null, // Use the full data source object for the renderer's source
12473
+ * "render": "browserName()"
12474
+ * } ]
12475
+ * } );
12476
+ * } );
12477
+ *
12478
+ * @example
12479
+ * // As an object, extracting different data for the different types
12480
+ * // This would be used with a data source such as:
12481
+ * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" }
12482
+ * // Here the `phone` integer is used for sorting and type detection, while `phone_filter`
12483
+ * // (which has both forms) is used for filtering for if a user inputs either format, while
12484
+ * // the formatted phone number is the one that is shown in the table.
12485
+ * $(document).ready( function() {
12486
+ * $('#example').dataTable( {
12487
+ * "columnDefs": [ {
12488
+ * "targets": [ 0 ],
12489
+ * "data": null, // Use the full data source object for the renderer's source
12490
+ * "render": {
12491
+ * "_": "phone",
12492
+ * "filter": "phone_filter",
12493
+ * "display": "phone_display"
12494
+ * }
12495
+ * } ]
12496
+ * } );
12497
+ * } );
12498
+ *
12499
+ * @example
12500
+ * // Use as a function to create a link from the data source
12501
+ * $(document).ready( function() {
12502
+ * $('#example').dataTable( {
12503
+ * "columnDefs": [ {
12504
+ * "targets": [ 0 ],
12505
+ * "data": "download_link",
12506
+ * "render": function ( data, type, full ) {
12507
+ * return '<a href="'+data+'">Download</a>';
12508
+ * }
12509
+ * } ]
12510
+ * } );
12511
+ * } );
12512
+ */
12513
+ "mRender": null,
12514
+
12515
+
12516
+ /**
12517
+ * Change the cell type created for the column - either TD cells or TH cells. This
12518
+ * can be useful as TH cells have semantic meaning in the table body, allowing them
12519
+ * to act as a header for a row (you may wish to add scope='row' to the TH elements).
12520
+ * @type string
12521
+ * @default td
12522
+ *
12523
+ * @name DataTable.defaults.column.cellType
12524
+ * @dtopt Columns
12525
+ *
12526
+ * @example
12527
+ * // Make the first column use TH cells
12528
+ * $(document).ready( function() {
12529
+ * $('#example').dataTable( {
12530
+ * "columnDefs": [ {
12531
+ * "targets": [ 0 ],
12532
+ * "cellType": "th"
12533
+ * } ]
12534
+ * } );
12535
+ * } );
12536
+ */
12537
+ "sCellType": "td",
12538
+
12539
+
12540
+ /**
12541
+ * Class to give to each cell in this column.
12542
+ * @type string
12543
+ * @default <i>Empty string</i>
12544
+ *
12545
+ * @name DataTable.defaults.column.class
12546
+ * @dtopt Columns
12547
+ *
12548
+ * @example
12549
+ * // Using `columnDefs`
12550
+ * $(document).ready( function() {
12551
+ * $('#example').dataTable( {
12552
+ * "columnDefs": [
12553
+ * { "class": "my_class", "targets": [ 0 ] }
12554
+ * ]
12555
+ * } );
12556
+ * } );
12557
+ *
12558
+ * @example
12559
+ * // Using `columns`
12560
+ * $(document).ready( function() {
12561
+ * $('#example').dataTable( {
12562
+ * "columns": [
12563
+ * { "class": "my_class" },
12564
+ * null,
12565
+ * null,
12566
+ * null,
12567
+ * null
12568
+ * ]
12569
+ * } );
12570
+ * } );
12571
+ */
12572
+ "sClass": "",
12573
+
12574
+ /**
12575
+ * When DataTables calculates the column widths to assign to each column,
12576
+ * it finds the longest string in each column and then constructs a
12577
+ * temporary table and reads the widths from that. The problem with this
12578
+ * is that "mmm" is much wider then "iiii", but the latter is a longer
12579
+ * string - thus the calculation can go wrong (doing it properly and putting
12580
+ * it into an DOM object and measuring that is horribly(!) slow). Thus as
12581
+ * a "work around" we provide this option. It will append its value to the
12582
+ * text that is found to be the longest string for the column - i.e. padding.
12583
+ * Generally you shouldn't need this!
12584
+ * @type string
12585
+ * @default <i>Empty string<i>
12586
+ *
12587
+ * @name DataTable.defaults.column.contentPadding
12588
+ * @dtopt Columns
12589
+ *
12590
+ * @example
12591
+ * // Using `columns`
12592
+ * $(document).ready( function() {
12593
+ * $('#example').dataTable( {
12594
+ * "columns": [
12595
+ * null,
12596
+ * null,
12597
+ * null,
12598
+ * {
12599
+ * "contentPadding": "mmm"
12600
+ * }
12601
+ * ]
12602
+ * } );
12603
+ * } );
12604
+ */
12605
+ "sContentPadding": "",
12606
+
12607
+
12608
+ /**
12609
+ * Allows a default value to be given for a column's data, and will be used
12610
+ * whenever a null data source is encountered (this can be because `data`
12611
+ * is set to null, or because the data source itself is null).
12612
+ * @type string
12613
+ * @default null
12614
+ *
12615
+ * @name DataTable.defaults.column.defaultContent
12616
+ * @dtopt Columns
12617
+ *
12618
+ * @example
12619
+ * // Using `columnDefs`
12620
+ * $(document).ready( function() {
12621
+ * $('#example').dataTable( {
12622
+ * "columnDefs": [
12623
+ * {
12624
+ * "data": null,
12625
+ * "defaultContent": "Edit",
12626
+ * "targets": [ -1 ]
12627
+ * }
12628
+ * ]
12629
+ * } );
12630
+ * } );
12631
+ *
12632
+ * @example
12633
+ * // Using `columns`
12634
+ * $(document).ready( function() {
12635
+ * $('#example').dataTable( {
12636
+ * "columns": [
12637
+ * null,
12638
+ * null,
12639
+ * null,
12640
+ * {
12641
+ * "data": null,
12642
+ * "defaultContent": "Edit"
12643
+ * }
12644
+ * ]
12645
+ * } );
12646
+ * } );
12647
+ */
12648
+ "sDefaultContent": null,
12649
+
12650
+
12651
+ /**
12652
+ * This parameter is only used in DataTables' server-side processing. It can
12653
+ * be exceptionally useful to know what columns are being displayed on the
12654
+ * client side, and to map these to database fields. When defined, the names
12655
+ * also allow DataTables to reorder information from the server if it comes
12656
+ * back in an unexpected order (i.e. if you switch your columns around on the
12657
+ * client-side, your server-side code does not also need updating).
12658
+ * @type string
12659
+ * @default <i>Empty string</i>
12660
+ *
12661
+ * @name DataTable.defaults.column.name
12662
+ * @dtopt Columns
12663
+ *
12664
+ * @example
12665
+ * // Using `columnDefs`
12666
+ * $(document).ready( function() {
12667
+ * $('#example').dataTable( {
12668
+ * "columnDefs": [
12669
+ * { "name": "engine", "targets": [ 0 ] },
12670
+ * { "name": "browser", "targets": [ 1 ] },
12671
+ * { "name": "platform", "targets": [ 2 ] },
12672
+ * { "name": "version", "targets": [ 3 ] },
12673
+ * { "name": "grade", "targets": [ 4 ] }
12674
+ * ]
12675
+ * } );
12676
+ * } );
12677
+ *
12678
+ * @example
12679
+ * // Using `columns`
12680
+ * $(document).ready( function() {
12681
+ * $('#example').dataTable( {
12682
+ * "columns": [
12683
+ * { "name": "engine" },
12684
+ * { "name": "browser" },
12685
+ * { "name": "platform" },
12686
+ * { "name": "version" },
12687
+ * { "name": "grade" }
12688
+ * ]
12689
+ * } );
12690
+ * } );
12691
+ */
12692
+ "sName": "",
12693
+
12694
+
12695
+ /**
12696
+ * Defines a data source type for the ordering which can be used to read
12697
+ * real-time information from the table (updating the internally cached
12698
+ * version) prior to ordering. This allows ordering to occur on user
12699
+ * editable elements such as form inputs.
12700
+ * @type string
12701
+ * @default std
12702
+ *
12703
+ * @name DataTable.defaults.column.orderDataType
12704
+ * @dtopt Columns
12705
+ *
12706
+ * @example
12707
+ * // Using `columnDefs`
12708
+ * $(document).ready( function() {
12709
+ * $('#example').dataTable( {
12710
+ * "columnDefs": [
12711
+ * { "orderDataType": "dom-text", "targets": [ 2, 3 ] },
12712
+ * { "type": "numeric", "targets": [ 3 ] },
12713
+ * { "orderDataType": "dom-select", "targets": [ 4 ] },
12714
+ * { "orderDataType": "dom-checkbox", "targets": [ 5 ] }
12715
+ * ]
12716
+ * } );
12717
+ * } );
12718
+ *
12719
+ * @example
12720
+ * // Using `columns`
12721
+ * $(document).ready( function() {
12722
+ * $('#example').dataTable( {
12723
+ * "columns": [
12724
+ * null,
12725
+ * null,
12726
+ * { "orderDataType": "dom-text" },
12727
+ * { "orderDataType": "dom-text", "type": "numeric" },
12728
+ * { "orderDataType": "dom-select" },
12729
+ * { "orderDataType": "dom-checkbox" }
12730
+ * ]
12731
+ * } );
12732
+ * } );
12733
+ */
12734
+ "sSortDataType": "std",
12735
+
12736
+
12737
+ /**
12738
+ * The title of this column.
12739
+ * @type string
12740
+ * @default null <i>Derived from the 'TH' value for this column in the
12741
+ * original HTML table.</i>
12742
+ *
12743
+ * @name DataTable.defaults.column.title
12744
+ * @dtopt Columns
12745
+ *
12746
+ * @example
12747
+ * // Using `columnDefs`
12748
+ * $(document).ready( function() {
12749
+ * $('#example').dataTable( {
12750
+ * "columnDefs": [
12751
+ * { "title": "My column title", "targets": [ 0 ] }
12752
+ * ]
12753
+ * } );
12754
+ * } );
12755
+ *
12756
+ * @example
12757
+ * // Using `columns`
12758
+ * $(document).ready( function() {
12759
+ * $('#example').dataTable( {
12760
+ * "columns": [
12761
+ * { "title": "My column title" },
12762
+ * null,
12763
+ * null,
12764
+ * null,
12765
+ * null
12766
+ * ]
12767
+ * } );
12768
+ * } );
12769
+ */
12770
+ "sTitle": null,
12771
+
12772
+
12773
+ /**
12774
+ * The type allows you to specify how the data for this column will be
12775
+ * ordered. Four types (string, numeric, date and html (which will strip
12776
+ * HTML tags before ordering)) are currently available. Note that only date
12777
+ * formats understood by Javascript's Date() object will be accepted as type
12778
+ * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
12779
+ * 'numeric', 'date' or 'html' (by default). Further types can be adding
12780
+ * through plug-ins.
12781
+ * @type string
12782
+ * @default null <i>Auto-detected from raw data</i>
12783
+ *
12784
+ * @name DataTable.defaults.column.type
12785
+ * @dtopt Columns
12786
+ *
12787
+ * @example
12788
+ * // Using `columnDefs`
12789
+ * $(document).ready( function() {
12790
+ * $('#example').dataTable( {
12791
+ * "columnDefs": [
12792
+ * { "type": "html", "targets": [ 0 ] }
12793
+ * ]
12794
+ * } );
12795
+ * } );
12796
+ *
12797
+ * @example
12798
+ * // Using `columns`
12799
+ * $(document).ready( function() {
12800
+ * $('#example').dataTable( {
12801
+ * "columns": [
12802
+ * { "type": "html" },
12803
+ * null,
12804
+ * null,
12805
+ * null,
12806
+ * null
12807
+ * ]
12808
+ * } );
12809
+ * } );
12810
+ */
12811
+ "sType": null,
12812
+
12813
+
12814
+ /**
12815
+ * Defining the width of the column, this parameter may take any CSS value
12816
+ * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
12817
+ * been given a specific width through this interface ensuring that the table
12818
+ * remains readable.
12819
+ * @type string
12820
+ * @default null <i>Automatic</i>
12821
+ *
12822
+ * @name DataTable.defaults.column.width
12823
+ * @dtopt Columns
12824
+ *
12825
+ * @example
12826
+ * // Using `columnDefs`
12827
+ * $(document).ready( function() {
12828
+ * $('#example').dataTable( {
12829
+ * "columnDefs": [
12830
+ * { "width": "20%", "targets": [ 0 ] }
12831
+ * ]
12832
+ * } );
12833
+ * } );
12834
+ *
12835
+ * @example
12836
+ * // Using `columns`
12837
+ * $(document).ready( function() {
12838
+ * $('#example').dataTable( {
12839
+ * "columns": [
12840
+ * { "width": "20%" },
12841
+ * null,
12842
+ * null,
12843
+ * null,
12844
+ * null
12845
+ * ]
12846
+ * } );
12847
+ * } );
12848
+ */
12849
+ "sWidth": null
12850
+ };
12851
+
12852
+ _fnHungarianMap( DataTable.defaults.column );
12853
+
12854
+
12855
+
12856
+ /**
12857
+ * DataTables settings object - this holds all the information needed for a
12858
+ * given table, including configuration, data and current application of the
12859
+ * table options. DataTables does not have a single instance for each DataTable
12860
+ * with the settings attached to that instance, but rather instances of the
12861
+ * DataTable "class" are created on-the-fly as needed (typically by a
12862
+ * $().dataTable() call) and the settings object is then applied to that
12863
+ * instance.
12864
+ *
12865
+ * Note that this object is related to {@link DataTable.defaults} but this
12866
+ * one is the internal data store for DataTables's cache of columns. It should
12867
+ * NOT be manipulated outside of DataTables. Any configuration should be done
12868
+ * through the initialisation options.
12869
+ * @namespace
12870
+ * @todo Really should attach the settings object to individual instances so we
12871
+ * don't need to create new instances on each $().dataTable() call (if the
12872
+ * table already exists). It would also save passing oSettings around and
12873
+ * into every single function. However, this is a very significant
12874
+ * architecture change for DataTables and will almost certainly break
12875
+ * backwards compatibility with older installations. This is something that
12876
+ * will be done in 2.0.
12877
+ */
12878
+ DataTable.models.oSettings = {
12879
+ /**
12880
+ * Primary features of DataTables and their enablement state.
12881
+ * @namespace
12882
+ */
12883
+ "oFeatures": {
12884
+
12885
+ /**
12886
+ * Flag to say if DataTables should automatically try to calculate the
12887
+ * optimum table and columns widths (true) or not (false).
12888
+ * Note that this parameter will be set by the initialisation routine. To
12889
+ * set a default use {@link DataTable.defaults}.
12890
+ * @type boolean
12891
+ */
12892
+ "bAutoWidth": null,
12893
+
12894
+ /**
12895
+ * Delay the creation of TR and TD elements until they are actually
12896
+ * needed by a driven page draw. This can give a significant speed
12897
+ * increase for Ajax source and Javascript source data, but makes no
12898
+ * difference at all fro DOM and server-side processing tables.
12899
+ * Note that this parameter will be set by the initialisation routine. To
12900
+ * set a default use {@link DataTable.defaults}.
12901
+ * @type boolean
12902
+ */
12903
+ "bDeferRender": null,
12904
+
12905
+ /**
12906
+ * Enable filtering on the table or not. Note that if this is disabled
12907
+ * then there is no filtering at all on the table, including fnFilter.
12908
+ * To just remove the filtering input use sDom and remove the 'f' option.
12909
+ * Note that this parameter will be set by the initialisation routine. To
12910
+ * set a default use {@link DataTable.defaults}.
12911
+ * @type boolean
12912
+ */
12913
+ "bFilter": null,
12914
+
12915
+ /**
12916
+ * Table information element (the 'Showing x of y records' div) enable
12917
+ * flag.
12918
+ * Note that this parameter will be set by the initialisation routine. To
12919
+ * set a default use {@link DataTable.defaults}.
12920
+ * @type boolean
12921
+ */
12922
+ "bInfo": null,
12923
+
12924
+ /**
12925
+ * Present a user control allowing the end user to change the page size
12926
+ * when pagination is enabled.
12927
+ * Note that this parameter will be set by the initialisation routine. To
12928
+ * set a default use {@link DataTable.defaults}.
12929
+ * @type boolean
12930
+ */
12931
+ "bLengthChange": null,
12932
+
12933
+ /**
12934
+ * Pagination enabled or not. Note that if this is disabled then length
12935
+ * changing must also be disabled.
12936
+ * Note that this parameter will be set by the initialisation routine. To
12937
+ * set a default use {@link DataTable.defaults}.
12938
+ * @type boolean
12939
+ */
12940
+ "bPaginate": null,
12941
+
12942
+ /**
12943
+ * Processing indicator enable flag whenever DataTables is enacting a
12944
+ * user request - typically an Ajax request for server-side processing.
12945
+ * Note that this parameter will be set by the initialisation routine. To
12946
+ * set a default use {@link DataTable.defaults}.
12947
+ * @type boolean
12948
+ */
12949
+ "bProcessing": null,
12950
+
12951
+ /**
12952
+ * Server-side processing enabled flag - when enabled DataTables will
12953
+ * get all data from the server for every draw - there is no filtering,
12954
+ * sorting or paging done on the client-side.
12955
+ * Note that this parameter will be set by the initialisation routine. To
12956
+ * set a default use {@link DataTable.defaults}.
12957
+ * @type boolean
12958
+ */
12959
+ "bServerSide": null,
12960
+
12961
+ /**
12962
+ * Sorting enablement flag.
12963
+ * Note that this parameter will be set by the initialisation routine. To
12964
+ * set a default use {@link DataTable.defaults}.
12965
+ * @type boolean
12966
+ */
12967
+ "bSort": null,
12968
+
12969
+ /**
12970
+ * Multi-column sorting
12971
+ * Note that this parameter will be set by the initialisation routine. To
12972
+ * set a default use {@link DataTable.defaults}.
12973
+ * @type boolean
12974
+ */
12975
+ "bSortMulti": null,
12976
+
12977
+ /**
12978
+ * Apply a class to the columns which are being sorted to provide a
12979
+ * visual highlight or not. This can slow things down when enabled since
12980
+ * there is a lot of DOM interaction.
12981
+ * Note that this parameter will be set by the initialisation routine. To
12982
+ * set a default use {@link DataTable.defaults}.
12983
+ * @type boolean
12984
+ */
12985
+ "bSortClasses": null,
12986
+
12987
+ /**
12988
+ * State saving enablement flag.
12989
+ * Note that this parameter will be set by the initialisation routine. To
12990
+ * set a default use {@link DataTable.defaults}.
12991
+ * @type boolean
12992
+ */
12993
+ "bStateSave": null
12994
+ },
12995
+
12996
+
12997
+ /**
12998
+ * Scrolling settings for a table.
12999
+ * @namespace
13000
+ */
13001
+ "oScroll": {
13002
+ /**
13003
+ * When the table is shorter in height than sScrollY, collapse the
13004
+ * table container down to the height of the table (when true).
13005
+ * Note that this parameter will be set by the initialisation routine. To
13006
+ * set a default use {@link DataTable.defaults}.
13007
+ * @type boolean
13008
+ */
13009
+ "bCollapse": null,
13010
+
13011
+ /**
13012
+ * Width of the scrollbar for the web-browser's platform. Calculated
13013
+ * during table initialisation.
13014
+ * @type int
13015
+ * @default 0
13016
+ */
13017
+ "iBarWidth": 0,
13018
+
13019
+ /**
13020
+ * Viewport width for horizontal scrolling. Horizontal scrolling is
13021
+ * disabled if an empty string.
13022
+ * Note that this parameter will be set by the initialisation routine. To
13023
+ * set a default use {@link DataTable.defaults}.
13024
+ * @type string
13025
+ */
13026
+ "sX": null,
13027
+
13028
+ /**
13029
+ * Width to expand the table to when using x-scrolling. Typically you
13030
+ * should not need to use this.
13031
+ * Note that this parameter will be set by the initialisation routine. To
13032
+ * set a default use {@link DataTable.defaults}.
13033
+ * @type string
13034
+ * @deprecated
13035
+ */
13036
+ "sXInner": null,
13037
+
13038
+ /**
13039
+ * Viewport height for vertical scrolling. Vertical scrolling is disabled
13040
+ * if an empty string.
13041
+ * Note that this parameter will be set by the initialisation routine. To
13042
+ * set a default use {@link DataTable.defaults}.
13043
+ * @type string
13044
+ */
13045
+ "sY": null
13046
+ },
13047
+
13048
+ /**
13049
+ * Language information for the table.
13050
+ * @namespace
13051
+ * @extends DataTable.defaults.oLanguage
13052
+ */
13053
+ "oLanguage": {
13054
+ /**
13055
+ * Information callback function. See
13056
+ * {@link DataTable.defaults.fnInfoCallback}
13057
+ * @type function
13058
+ * @default null
13059
+ */
13060
+ "fnInfoCallback": null
13061
+ },
13062
+
13063
+ /**
13064
+ * Browser support parameters
13065
+ * @namespace
13066
+ */
13067
+ "oBrowser": {
13068
+ /**
13069
+ * Indicate if the browser incorrectly calculates width:100% inside a
13070
+ * scrolling element (IE6/7)
13071
+ * @type boolean
13072
+ * @default false
13073
+ */
13074
+ "bScrollOversize": false,
13075
+
13076
+ /**
13077
+ * Determine if the vertical scrollbar is on the right or left of the
13078
+ * scrolling container - needed for rtl language layout, although not
13079
+ * all browsers move the scrollbar (Safari).
13080
+ * @type boolean
13081
+ * @default false
13082
+ */
13083
+ "bScrollbarLeft": false,
13084
+
13085
+ /**
13086
+ * Flag for if `getBoundingClientRect` is fully supported or not
13087
+ * @type boolean
13088
+ * @default false
13089
+ */
13090
+ "bBounding": false,
13091
+
13092
+ /**
13093
+ * Browser scrollbar width
13094
+ * @type integer
13095
+ * @default 0
13096
+ */
13097
+ "barWidth": 0
13098
+ },
13099
+
13100
+
13101
+ "ajax": null,
13102
+
13103
+
13104
+ /**
13105
+ * Array referencing the nodes which are used for the features. The
13106
+ * parameters of this object match what is allowed by sDom - i.e.
13107
+ * <ul>
13108
+ * <li>'l' - Length changing</li>
13109
+ * <li>'f' - Filtering input</li>
13110
+ * <li>'t' - The table!</li>
13111
+ * <li>'i' - Information</li>
13112
+ * <li>'p' - Pagination</li>
13113
+ * <li>'r' - pRocessing</li>
13114
+ * </ul>
13115
+ * @type array
13116
+ * @default []
13117
+ */
13118
+ "aanFeatures": [],
13119
+
13120
+ /**
13121
+ * Store data information - see {@link DataTable.models.oRow} for detailed
13122
+ * information.
13123
+ * @type array
13124
+ * @default []
13125
+ */
13126
+ "aoData": [],
13127
+
13128
+ /**
13129
+ * Array of indexes which are in the current display (after filtering etc)
13130
+ * @type array
13131
+ * @default []
13132
+ */
13133
+ "aiDisplay": [],
13134
+
13135
+ /**
13136
+ * Array of indexes for display - no filtering
13137
+ * @type array
13138
+ * @default []
13139
+ */
13140
+ "aiDisplayMaster": [],
13141
+
13142
+ /**
13143
+ * Map of row ids to data indexes
13144
+ * @type object
13145
+ * @default {}
13146
+ */
13147
+ "aIds": {},
13148
+
13149
+ /**
13150
+ * Store information about each column that is in use
13151
+ * @type array
13152
+ * @default []
13153
+ */
13154
+ "aoColumns": [],
13155
+
13156
+ /**
13157
+ * Store information about the table's header
13158
+ * @type array
13159
+ * @default []
13160
+ */
13161
+ "aoHeader": [],
13162
+
13163
+ /**
13164
+ * Store information about the table's footer
13165
+ * @type array
13166
+ * @default []
13167
+ */
13168
+ "aoFooter": [],
13169
+
13170
+ /**
13171
+ * Store the applied global search information in case we want to force a
13172
+ * research or compare the old search to a new one.
13173
+ * Note that this parameter will be set by the initialisation routine. To
13174
+ * set a default use {@link DataTable.defaults}.
13175
+ * @namespace
13176
+ * @extends DataTable.models.oSearch
13177
+ */
13178
+ "oPreviousSearch": {},
13179
+
13180
+ /**
13181
+ * Store the applied search for each column - see
13182
+ * {@link DataTable.models.oSearch} for the format that is used for the
13183
+ * filtering information for each column.
13184
+ * @type array
13185
+ * @default []
13186
+ */
13187
+ "aoPreSearchCols": [],
13188
+
13189
+ /**
13190
+ * Sorting that is applied to the table. Note that the inner arrays are
13191
+ * used in the following manner:
13192
+ * <ul>
13193
+ * <li>Index 0 - column number</li>
13194
+ * <li>Index 1 - current sorting direction</li>
13195
+ * </ul>
13196
+ * Note that this parameter will be set by the initialisation routine. To
13197
+ * set a default use {@link DataTable.defaults}.
13198
+ * @type array
13199
+ * @todo These inner arrays should really be objects
13200
+ */
13201
+ "aaSorting": null,
13202
+
13203
+ /**
13204
+ * Sorting that is always applied to the table (i.e. prefixed in front of
13205
+ * aaSorting).
13206
+ * Note that this parameter will be set by the initialisation routine. To
13207
+ * set a default use {@link DataTable.defaults}.
13208
+ * @type array
13209
+ * @default []
13210
+ */
13211
+ "aaSortingFixed": [],
13212
+
13213
+ /**
13214
+ * Classes to use for the striping of a table.
13215
+ * Note that this parameter will be set by the initialisation routine. To
13216
+ * set a default use {@link DataTable.defaults}.
13217
+ * @type array
13218
+ * @default []
13219
+ */
13220
+ "asStripeClasses": null,
13221
+
13222
+ /**
13223
+ * If restoring a table - we should restore its striping classes as well
13224
+ * @type array
13225
+ * @default []
13226
+ */
13227
+ "asDestroyStripes": [],
13228
+
13229
+ /**
13230
+ * If restoring a table - we should restore its width
13231
+ * @type int
13232
+ * @default 0
13233
+ */
13234
+ "sDestroyWidth": 0,
13235
+
13236
+ /**
13237
+ * Callback functions array for every time a row is inserted (i.e. on a draw).
13238
+ * @type array
13239
+ * @default []
13240
+ */
13241
+ "aoRowCallback": [],
13242
+
13243
+ /**
13244
+ * Callback functions for the header on each draw.
13245
+ * @type array
13246
+ * @default []
13247
+ */
13248
+ "aoHeaderCallback": [],
13249
+
13250
+ /**
13251
+ * Callback function for the footer on each draw.
13252
+ * @type array
13253
+ * @default []
13254
+ */
13255
+ "aoFooterCallback": [],
13256
+
13257
+ /**
13258
+ * Array of callback functions for draw callback functions
13259
+ * @type array
13260
+ * @default []
13261
+ */
13262
+ "aoDrawCallback": [],
13263
+
13264
+ /**
13265
+ * Array of callback functions for row created function
13266
+ * @type array
13267
+ * @default []
13268
+ */
13269
+ "aoRowCreatedCallback": [],
13270
+
13271
+ /**
13272
+ * Callback functions for just before the table is redrawn. A return of
13273
+ * false will be used to cancel the draw.
13274
+ * @type array
13275
+ * @default []
13276
+ */
13277
+ "aoPreDrawCallback": [],
13278
+
13279
+ /**
13280
+ * Callback functions for when the table has been initialised.
13281
+ * @type array
13282
+ * @default []
13283
+ */
13284
+ "aoInitComplete": [],
13285
+
13286
+
13287
+ /**
13288
+ * Callbacks for modifying the settings to be stored for state saving, prior to
13289
+ * saving state.
13290
+ * @type array
13291
+ * @default []
13292
+ */
13293
+ "aoStateSaveParams": [],
13294
+
13295
+ /**
13296
+ * Callbacks for modifying the settings that have been stored for state saving
13297
+ * prior to using the stored values to restore the state.
13298
+ * @type array
13299
+ * @default []
13300
+ */
13301
+ "aoStateLoadParams": [],
13302
+
13303
+ /**
13304
+ * Callbacks for operating on the settings object once the saved state has been
13305
+ * loaded
13306
+ * @type array
13307
+ * @default []
13308
+ */
13309
+ "aoStateLoaded": [],
13310
+
13311
+ /**
13312
+ * Cache the table ID for quick access
13313
+ * @type string
13314
+ * @default <i>Empty string</i>
13315
+ */
13316
+ "sTableId": "",
13317
+
13318
+ /**
13319
+ * The TABLE node for the main table
13320
+ * @type node
13321
+ * @default null
13322
+ */
13323
+ "nTable": null,
13324
+
13325
+ /**
13326
+ * Permanent ref to the thead element
13327
+ * @type node
13328
+ * @default null
13329
+ */
13330
+ "nTHead": null,
13331
+
13332
+ /**
13333
+ * Permanent ref to the tfoot element - if it exists
13334
+ * @type node
13335
+ * @default null
13336
+ */
13337
+ "nTFoot": null,
13338
+
13339
+ /**
13340
+ * Permanent ref to the tbody element
13341
+ * @type node
13342
+ * @default null
13343
+ */
13344
+ "nTBody": null,
13345
+
13346
+ /**
13347
+ * Cache the wrapper node (contains all DataTables controlled elements)
13348
+ * @type node
13349
+ * @default null
13350
+ */
13351
+ "nTableWrapper": null,
13352
+
13353
+ /**
13354
+ * Indicate if when using server-side processing the loading of data
13355
+ * should be deferred until the second draw.
13356
+ * Note that this parameter will be set by the initialisation routine. To
13357
+ * set a default use {@link DataTable.defaults}.
13358
+ * @type boolean
13359
+ * @default false
13360
+ */
13361
+ "bDeferLoading": false,
13362
+
13363
+ /**
13364
+ * Indicate if all required information has been read in
13365
+ * @type boolean
13366
+ * @default false
13367
+ */
13368
+ "bInitialised": false,
13369
+
13370
+ /**
13371
+ * Information about open rows. Each object in the array has the parameters
13372
+ * 'nTr' and 'nParent'
13373
+ * @type array
13374
+ * @default []
13375
+ */
13376
+ "aoOpenRows": [],
13377
+
13378
+ /**
13379
+ * Dictate the positioning of DataTables' control elements - see
13380
+ * {@link DataTable.model.oInit.sDom}.
13381
+ * Note that this parameter will be set by the initialisation routine. To
13382
+ * set a default use {@link DataTable.defaults}.
13383
+ * @type string
13384
+ * @default null
13385
+ */
13386
+ "sDom": null,
13387
+
13388
+ /**
13389
+ * Search delay (in mS)
13390
+ * @type integer
13391
+ * @default null
13392
+ */
13393
+ "searchDelay": null,
13394
+
13395
+ /**
13396
+ * Which type of pagination should be used.
13397
+ * Note that this parameter will be set by the initialisation routine. To
13398
+ * set a default use {@link DataTable.defaults}.
13399
+ * @type string
13400
+ * @default two_button
13401
+ */
13402
+ "sPaginationType": "two_button",
13403
+
13404
+ /**
13405
+ * The state duration (for `stateSave`) in seconds.
13406
+ * Note that this parameter will be set by the initialisation routine. To
13407
+ * set a default use {@link DataTable.defaults}.
13408
+ * @type int
13409
+ * @default 0
13410
+ */
13411
+ "iStateDuration": 0,
13412
+
13413
+ /**
13414
+ * Array of callback functions for state saving. Each array element is an
13415
+ * object with the following parameters:
13416
+ * <ul>
13417
+ * <li>function:fn - function to call. Takes two parameters, oSettings
13418
+ * and the JSON string to save that has been thus far created. Returns
13419
+ * a JSON string to be inserted into a json object
13420
+ * (i.e. '"param": [ 0, 1, 2]')</li>
13421
+ * <li>string:sName - name of callback</li>
13422
+ * </ul>
13423
+ * @type array
13424
+ * @default []
13425
+ */
13426
+ "aoStateSave": [],
13427
+
13428
+ /**
13429
+ * Array of callback functions for state loading. Each array element is an
13430
+ * object with the following parameters:
13431
+ * <ul>
13432
+ * <li>function:fn - function to call. Takes two parameters, oSettings
13433
+ * and the object stored. May return false to cancel state loading</li>
13434
+ * <li>string:sName - name of callback</li>
13435
+ * </ul>
13436
+ * @type array
13437
+ * @default []
13438
+ */
13439
+ "aoStateLoad": [],
13440
+
13441
+ /**
13442
+ * State that was saved. Useful for back reference
13443
+ * @type object
13444
+ * @default null
13445
+ */
13446
+ "oSavedState": null,
13447
+
13448
+ /**
13449
+ * State that was loaded. Useful for back reference
13450
+ * @type object
13451
+ * @default null
13452
+ */
13453
+ "oLoadedState": null,
13454
+
13455
+ /**
13456
+ * Source url for AJAX data for the table.
13457
+ * Note that this parameter will be set by the initialisation routine. To
13458
+ * set a default use {@link DataTable.defaults}.
13459
+ * @type string
13460
+ * @default null
13461
+ */
13462
+ "sAjaxSource": null,
13463
+
13464
+ /**
13465
+ * Property from a given object from which to read the table data from. This
13466
+ * can be an empty string (when not server-side processing), in which case
13467
+ * it is assumed an an array is given directly.
13468
+ * Note that this parameter will be set by the initialisation routine. To
13469
+ * set a default use {@link DataTable.defaults}.
13470
+ * @type string
13471
+ */
13472
+ "sAjaxDataProp": null,
13473
+
13474
+ /**
13475
+ * Note if draw should be blocked while getting data
13476
+ * @type boolean
13477
+ * @default true
13478
+ */
13479
+ "bAjaxDataGet": true,
13480
+
13481
+ /**
13482
+ * The last jQuery XHR object that was used for server-side data gathering.
13483
+ * This can be used for working with the XHR information in one of the
13484
+ * callbacks
13485
+ * @type object
13486
+ * @default null
13487
+ */
13488
+ "jqXHR": null,
13489
+
13490
+ /**
13491
+ * JSON returned from the server in the last Ajax request
13492
+ * @type object
13493
+ * @default undefined
13494
+ */
13495
+ "json": undefined,
13496
+
13497
+ /**
13498
+ * Data submitted as part of the last Ajax request
13499
+ * @type object
13500
+ * @default undefined
13501
+ */
13502
+ "oAjaxData": undefined,
13503
+
13504
+ /**
13505
+ * Function to get the server-side data.
13506
+ * Note that this parameter will be set by the initialisation routine. To
13507
+ * set a default use {@link DataTable.defaults}.
13508
+ * @type function
13509
+ */
13510
+ "fnServerData": null,
13511
+
13512
+ /**
13513
+ * Functions which are called prior to sending an Ajax request so extra
13514
+ * parameters can easily be sent to the server
13515
+ * @type array
13516
+ * @default []
13517
+ */
13518
+ "aoServerParams": [],
13519
+
13520
+ /**
13521
+ * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
13522
+ * required).
13523
+ * Note that this parameter will be set by the initialisation routine. To
13524
+ * set a default use {@link DataTable.defaults}.
13525
+ * @type string
13526
+ */
13527
+ "sServerMethod": null,
13528
+
13529
+ /**
13530
+ * Format numbers for display.
13531
+ * Note that this parameter will be set by the initialisation routine. To
13532
+ * set a default use {@link DataTable.defaults}.
13533
+ * @type function
13534
+ */
13535
+ "fnFormatNumber": null,
13536
+
13537
+ /**
13538
+ * List of options that can be used for the user selectable length menu.
13539
+ * Note that this parameter will be set by the initialisation routine. To
13540
+ * set a default use {@link DataTable.defaults}.
13541
+ * @type array
13542
+ * @default []
13543
+ */
13544
+ "aLengthMenu": null,
13545
+
13546
+ /**
13547
+ * Counter for the draws that the table does. Also used as a tracker for
13548
+ * server-side processing
13549
+ * @type int
13550
+ * @default 0
13551
+ */
13552
+ "iDraw": 0,
13553
+
13554
+ /**
13555
+ * Indicate if a redraw is being done - useful for Ajax
13556
+ * @type boolean
13557
+ * @default false
13558
+ */
13559
+ "bDrawing": false,
13560
+
13561
+ /**
13562
+ * Draw index (iDraw) of the last error when parsing the returned data
13563
+ * @type int
13564
+ * @default -1
13565
+ */
13566
+ "iDrawError": -1,
13567
+
13568
+ /**
13569
+ * Paging display length
13570
+ * @type int
13571
+ * @default 10
13572
+ */
13573
+ "_iDisplayLength": 10,
13574
+
13575
+ /**
13576
+ * Paging start point - aiDisplay index
13577
+ * @type int
13578
+ * @default 0
13579
+ */
13580
+ "_iDisplayStart": 0,
13581
+
13582
+ /**
13583
+ * Server-side processing - number of records in the result set
13584
+ * (i.e. before filtering), Use fnRecordsTotal rather than
13585
+ * this property to get the value of the number of records, regardless of
13586
+ * the server-side processing setting.
13587
+ * @type int
13588
+ * @default 0
13589
+ * @private
13590
+ */
13591
+ "_iRecordsTotal": 0,
13592
+
13593
+ /**
13594
+ * Server-side processing - number of records in the current display set
13595
+ * (i.e. after filtering). Use fnRecordsDisplay rather than
13596
+ * this property to get the value of the number of records, regardless of
13597
+ * the server-side processing setting.
13598
+ * @type boolean
13599
+ * @default 0
13600
+ * @private
13601
+ */
13602
+ "_iRecordsDisplay": 0,
13603
+
13604
+ /**
13605
+ * Flag to indicate if jQuery UI marking and classes should be used.
13606
+ * Note that this parameter will be set by the initialisation routine. To
13607
+ * set a default use {@link DataTable.defaults}.
13608
+ * @type boolean
13609
+ */
13610
+ "bJUI": null,
13611
+
13612
+ /**
13613
+ * The classes to use for the table
13614
+ * @type object
13615
+ * @default {}
13616
+ */
13617
+ "oClasses": {},
13618
+
13619
+ /**
13620
+ * Flag attached to the settings object so you can check in the draw
13621
+ * callback if filtering has been done in the draw. Deprecated in favour of
13622
+ * events.
13623
+ * @type boolean
13624
+ * @default false
13625
+ * @deprecated
13626
+ */
13627
+ "bFiltered": false,
13628
+
13629
+ /**
13630
+ * Flag attached to the settings object so you can check in the draw
13631
+ * callback if sorting has been done in the draw. Deprecated in favour of
13632
+ * events.
13633
+ * @type boolean
13634
+ * @default false
13635
+ * @deprecated
13636
+ */
13637
+ "bSorted": false,
13638
+
13639
+ /**
13640
+ * Indicate that if multiple rows are in the header and there is more than
13641
+ * one unique cell per column, if the top one (true) or bottom one (false)
13642
+ * should be used for sorting / title by DataTables.
13643
+ * Note that this parameter will be set by the initialisation routine. To
13644
+ * set a default use {@link DataTable.defaults}.
13645
+ * @type boolean
13646
+ */
13647
+ "bSortCellsTop": null,
13648
+
13649
+ /**
13650
+ * Initialisation object that is used for the table
13651
+ * @type object
13652
+ * @default null
13653
+ */
13654
+ "oInit": null,
13655
+
13656
+ /**
13657
+ * Destroy callback functions - for plug-ins to attach themselves to the
13658
+ * destroy so they can clean up markup and events.
13659
+ * @type array
13660
+ * @default []
13661
+ */
13662
+ "aoDestroyCallback": [],
13663
+
13664
+
13665
+ /**
13666
+ * Get the number of records in the current record set, before filtering
13667
+ * @type function
13668
+ */
13669
+ "fnRecordsTotal": function ()
13670
+ {
13671
+ return _fnDataSource( this ) == 'ssp' ?
13672
+ this._iRecordsTotal * 1 :
13673
+ this.aiDisplayMaster.length;
13674
+ },
13675
+
13676
+ /**
13677
+ * Get the number of records in the current record set, after filtering
13678
+ * @type function
13679
+ */
13680
+ "fnRecordsDisplay": function ()
13681
+ {
13682
+ return _fnDataSource( this ) == 'ssp' ?
13683
+ this._iRecordsDisplay * 1 :
13684
+ this.aiDisplay.length;
13685
+ },
13686
+
13687
+ /**
13688
+ * Get the display end point - aiDisplay index
13689
+ * @type function
13690
+ */
13691
+ "fnDisplayEnd": function ()
13692
+ {
13693
+ var
13694
+ len = this._iDisplayLength,
13695
+ start = this._iDisplayStart,
13696
+ calc = start + len,
13697
+ records = this.aiDisplay.length,
13698
+ features = this.oFeatures,
13699
+ paginate = features.bPaginate;
13700
+
13701
+ if ( features.bServerSide ) {
13702
+ return paginate === false || len === -1 ?
13703
+ start + records :
13704
+ Math.min( start+len, this._iRecordsDisplay );
13705
+ }
13706
+ else {
13707
+ return ! paginate || calc>records || len===-1 ?
13708
+ records :
13709
+ calc;
13710
+ }
13711
+ },
13712
+
13713
+ /**
13714
+ * The DataTables object for this table
13715
+ * @type object
13716
+ * @default null
13717
+ */
13718
+ "oInstance": null,
13719
+
13720
+ /**
13721
+ * Unique identifier for each instance of the DataTables object. If there
13722
+ * is an ID on the table node, then it takes that value, otherwise an
13723
+ * incrementing internal counter is used.
13724
+ * @type string
13725
+ * @default null
13726
+ */
13727
+ "sInstance": null,
13728
+
13729
+ /**
13730
+ * tabindex attribute value that is added to DataTables control elements, allowing
13731
+ * keyboard navigation of the table and its controls.
13732
+ */
13733
+ "iTabIndex": 0,
13734
+
13735
+ /**
13736
+ * DIV container for the footer scrolling table if scrolling
13737
+ */
13738
+ "nScrollHead": null,
13739
+
13740
+ /**
13741
+ * DIV container for the footer scrolling table if scrolling
13742
+ */
13743
+ "nScrollFoot": null,
13744
+
13745
+ /**
13746
+ * Last applied sort
13747
+ * @type array
13748
+ * @default []
13749
+ */
13750
+ "aLastSort": [],
13751
+
13752
+ /**
13753
+ * Stored plug-in instances
13754
+ * @type object
13755
+ * @default {}
13756
+ */
13757
+ "oPlugins": {},
13758
+
13759
+ /**
13760
+ * Function used to get a row's id from the row's data
13761
+ * @type function
13762
+ * @default null
13763
+ */
13764
+ "rowIdFn": null,
13765
+
13766
+ /**
13767
+ * Data location where to store a row's id
13768
+ * @type string
13769
+ * @default null
13770
+ */
13771
+ "rowId": null
13772
+ };
13773
+
13774
+ /**
13775
+ * Extension object for DataTables that is used to provide all extension
13776
+ * options.
13777
+ *
13778
+ * Note that the `DataTable.ext` object is available through
13779
+ * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
13780
+ * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
13781
+ * @namespace
13782
+ * @extends DataTable.models.ext
13783
+ */
13784
+
13785
+
13786
+ /**
13787
+ * DataTables extensions
13788
+ *
13789
+ * This namespace acts as a collection area for plug-ins that can be used to
13790
+ * extend DataTables capabilities. Indeed many of the build in methods
13791
+ * use this method to provide their own capabilities (sorting methods for
13792
+ * example).
13793
+ *
13794
+ * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
13795
+ * reasons
13796
+ *
13797
+ * @namespace
13798
+ */
13799
+ DataTable.ext = _ext = {
13800
+ /**
13801
+ * Buttons. For use with the Buttons extension for DataTables. This is
13802
+ * defined here so other extensions can define buttons regardless of load
13803
+ * order. It is _not_ used by DataTables core.
13804
+ *
13805
+ * @type object
13806
+ * @default {}
13807
+ */
13808
+ buttons: {},
13809
+
13810
+
13811
+ /**
13812
+ * Element class names
13813
+ *
13814
+ * @type object
13815
+ * @default {}
13816
+ */
13817
+ classes: {},
13818
+
13819
+
13820
+ /**
13821
+ * DataTables build type (expanded by the download builder)
13822
+ *
13823
+ * @type string
13824
+ */
13825
+ builder: "-source-",
13826
+
13827
+
13828
+ /**
13829
+ * Error reporting.
13830
+ *
13831
+ * How should DataTables report an error. Can take the value 'alert',
13832
+ * 'throw', 'none' or a function.
13833
+ *
13834
+ * @type string|function
13835
+ * @default alert
13836
+ */
13837
+ errMode: "alert",
13838
+
13839
+
13840
+ /**
13841
+ * Feature plug-ins.
13842
+ *
13843
+ * This is an array of objects which describe the feature plug-ins that are
13844
+ * available to DataTables. These feature plug-ins are then available for
13845
+ * use through the `dom` initialisation option.
13846
+ *
13847
+ * Each feature plug-in is described by an object which must have the
13848
+ * following properties:
13849
+ *
13850
+ * * `fnInit` - function that is used to initialise the plug-in,
13851
+ * * `cFeature` - a character so the feature can be enabled by the `dom`
13852
+ * instillation option. This is case sensitive.
13853
+ *
13854
+ * The `fnInit` function has the following input parameters:
13855
+ *
13856
+ * 1. `{object}` DataTables settings object: see
13857
+ * {@link DataTable.models.oSettings}
13858
+ *
13859
+ * And the following return is expected:
13860
+ *
13861
+ * * {node|null} The element which contains your feature. Note that the
13862
+ * return may also be void if your plug-in does not require to inject any
13863
+ * DOM elements into DataTables control (`dom`) - for example this might
13864
+ * be useful when developing a plug-in which allows table control via
13865
+ * keyboard entry
13866
+ *
13867
+ * @type array
13868
+ *
13869
+ * @example
13870
+ * $.fn.dataTable.ext.features.push( {
13871
+ * "fnInit": function( oSettings ) {
13872
+ * return new TableTools( { "oDTSettings": oSettings } );
13873
+ * },
13874
+ * "cFeature": "T"
13875
+ * } );
13876
+ */
13877
+ feature: [],
13878
+
13879
+
13880
+ /**
13881
+ * Row searching.
13882
+ *
13883
+ * This method of searching is complimentary to the default type based
13884
+ * searching, and a lot more comprehensive as it allows you complete control
13885
+ * over the searching logic. Each element in this array is a function
13886
+ * (parameters described below) that is called for every row in the table,
13887
+ * and your logic decides if it should be included in the searching data set
13888
+ * or not.
13889
+ *
13890
+ * Searching functions have the following input parameters:
13891
+ *
13892
+ * 1. `{object}` DataTables settings object: see
13893
+ * {@link DataTable.models.oSettings}
13894
+ * 2. `{array|object}` Data for the row to be processed (same as the
13895
+ * original format that was passed in as the data source, or an array
13896
+ * from a DOM data source
13897
+ * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
13898
+ * can be useful to retrieve the `TR` element if you need DOM interaction.
13899
+ *
13900
+ * And the following return is expected:
13901
+ *
13902
+ * * {boolean} Include the row in the searched result set (true) or not
13903
+ * (false)
13904
+ *
13905
+ * Note that as with the main search ability in DataTables, technically this
13906
+ * is "filtering", since it is subtractive. However, for consistency in
13907
+ * naming we call it searching here.
13908
+ *
13909
+ * @type array
13910
+ * @default []
13911
+ *
13912
+ * @example
13913
+ * // The following example shows custom search being applied to the
13914
+ * // fourth column (i.e. the data[3] index) based on two input values
13915
+ * // from the end-user, matching the data in a certain range.
13916
+ * $.fn.dataTable.ext.search.push(
13917
+ * function( settings, data, dataIndex ) {
13918
+ * var min = document.getElementById('min').value * 1;
13919
+ * var max = document.getElementById('max').value * 1;
13920
+ * var version = data[3] == "-" ? 0 : data[3]*1;
13921
+ *
13922
+ * if ( min == "" && max == "" ) {
13923
+ * return true;
13924
+ * }
13925
+ * else if ( min == "" && version < max ) {
13926
+ * return true;
13927
+ * }
13928
+ * else if ( min < version && "" == max ) {
13929
+ * return true;
13930
+ * }
13931
+ * else if ( min < version && version < max ) {
13932
+ * return true;
13933
+ * }
13934
+ * return false;
13935
+ * }
13936
+ * );
13937
+ */
13938
+ search: [],
13939
+
13940
+
13941
+ /**
13942
+ * Selector extensions
13943
+ *
13944
+ * The `selector` option can be used to extend the options available for the
13945
+ * selector modifier options (`selector-modifier` object data type) that
13946
+ * each of the three built in selector types offer (row, column and cell +
13947
+ * their plural counterparts). For example the Select extension uses this
13948
+ * mechanism to provide an option to select only rows, columns and cells
13949
+ * that have been marked as selected by the end user (`{selected: true}`),
13950
+ * which can be used in conjunction with the existing built in selector
13951
+ * options.
13952
+ *
13953
+ * Each property is an array to which functions can be pushed. The functions
13954
+ * take three attributes:
13955
+ *
13956
+ * * Settings object for the host table
13957
+ * * Options object (`selector-modifier` object type)
13958
+ * * Array of selected item indexes
13959
+ *
13960
+ * The return is an array of the resulting item indexes after the custom
13961
+ * selector has been applied.
13962
+ *
13963
+ * @type object
13964
+ */
13965
+ selector: {
13966
+ cell: [],
13967
+ column: [],
13968
+ row: []
13969
+ },
13970
+
13971
+
13972
+ /**
13973
+ * Internal functions, exposed for used in plug-ins.
13974
+ *
13975
+ * Please note that you should not need to use the internal methods for
13976
+ * anything other than a plug-in (and even then, try to avoid if possible).
13977
+ * The internal function may change between releases.
13978
+ *
13979
+ * @type object
13980
+ * @default {}
13981
+ */
13982
+ internal: {},
13983
+
13984
+
13985
+ /**
13986
+ * Legacy configuration options. Enable and disable legacy options that
13987
+ * are available in DataTables.
13988
+ *
13989
+ * @type object
13990
+ */
13991
+ legacy: {
13992
+ /**
13993
+ * Enable / disable DataTables 1.9 compatible server-side processing
13994
+ * requests
13995
+ *
13996
+ * @type boolean
13997
+ * @default null
13998
+ */
13999
+ ajax: null
14000
+ },
14001
+
14002
+
14003
+ /**
14004
+ * Pagination plug-in methods.
14005
+ *
14006
+ * Each entry in this object is a function and defines which buttons should
14007
+ * be shown by the pagination rendering method that is used for the table:
14008
+ * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
14009
+ * buttons are displayed in the document, while the functions here tell it
14010
+ * what buttons to display. This is done by returning an array of button
14011
+ * descriptions (what each button will do).
14012
+ *
14013
+ * Pagination types (the four built in options and any additional plug-in
14014
+ * options defined here) can be used through the `paginationType`
14015
+ * initialisation parameter.
14016
+ *
14017
+ * The functions defined take two parameters:
14018
+ *
14019
+ * 1. `{int} page` The current page index
14020
+ * 2. `{int} pages` The number of pages in the table
14021
+ *
14022
+ * Each function is expected to return an array where each element of the
14023
+ * array can be one of:
14024
+ *
14025
+ * * `first` - Jump to first page when activated
14026
+ * * `last` - Jump to last page when activated
14027
+ * * `previous` - Show previous page when activated
14028
+ * * `next` - Show next page when activated
14029
+ * * `{int}` - Show page of the index given
14030
+ * * `{array}` - A nested array containing the above elements to add a
14031
+ * containing 'DIV' element (might be useful for styling).
14032
+ *
14033
+ * Note that DataTables v1.9- used this object slightly differently whereby
14034
+ * an object with two functions would be defined for each plug-in. That
14035
+ * ability is still supported by DataTables 1.10+ to provide backwards
14036
+ * compatibility, but this option of use is now decremented and no longer
14037
+ * documented in DataTables 1.10+.
14038
+ *
14039
+ * @type object
14040
+ * @default {}
14041
+ *
14042
+ * @example
14043
+ * // Show previous, next and current page buttons only
14044
+ * $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
14045
+ * return [ 'previous', page, 'next' ];
14046
+ * };
14047
+ */
14048
+ pager: {},
14049
+
14050
+
14051
+ renderer: {
14052
+ pageButton: {},
14053
+ header: {}
14054
+ },
14055
+
14056
+
14057
+ /**
14058
+ * Ordering plug-ins - custom data source
14059
+ *
14060
+ * The extension options for ordering of data available here is complimentary
14061
+ * to the default type based ordering that DataTables typically uses. It
14062
+ * allows much greater control over the the data that is being used to
14063
+ * order a column, but is necessarily therefore more complex.
14064
+ *
14065
+ * This type of ordering is useful if you want to do ordering based on data
14066
+ * live from the DOM (for example the contents of an 'input' element) rather
14067
+ * than just the static string that DataTables knows of.
14068
+ *
14069
+ * The way these plug-ins work is that you create an array of the values you
14070
+ * wish to be ordering for the column in question and then return that
14071
+ * array. The data in the array much be in the index order of the rows in
14072
+ * the table (not the currently ordering order!). Which order data gathering
14073
+ * function is run here depends on the `dt-init columns.orderDataType`
14074
+ * parameter that is used for the column (if any).
14075
+ *
14076
+ * The functions defined take two parameters:
14077
+ *
14078
+ * 1. `{object}` DataTables settings object: see
14079
+ * {@link DataTable.models.oSettings}
14080
+ * 2. `{int}` Target column index
14081
+ *
14082
+ * Each function is expected to return an array:
14083
+ *
14084
+ * * `{array}` Data for the column to be ordering upon
14085
+ *
14086
+ * @type array
14087
+ *
14088
+ * @example
14089
+ * // Ordering using `input` node values
14090
+ * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col )
14091
+ * {
14092
+ * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
14093
+ * return $('input', td).val();
14094
+ * } );
14095
+ * }
14096
+ */
14097
+ order: {},
14098
+
14099
+
14100
+ /**
14101
+ * Type based plug-ins.
14102
+ *
14103
+ * Each column in DataTables has a type assigned to it, either by automatic
14104
+ * detection or by direct assignment using the `type` option for the column.
14105
+ * The type of a column will effect how it is ordering and search (plug-ins
14106
+ * can also make use of the column type if required).
14107
+ *
14108
+ * @namespace
14109
+ */
14110
+ type: {
14111
+ /**
14112
+ * Type detection functions.
14113
+ *
14114
+ * The functions defined in this object are used to automatically detect
14115
+ * a column's type, making initialisation of DataTables super easy, even
14116
+ * when complex data is in the table.
14117
+ *
14118
+ * The functions defined take two parameters:
14119
+ *
14120
+ * 1. `{*}` Data from the column cell to be analysed
14121
+ * 2. `{settings}` DataTables settings object. This can be used to
14122
+ * perform context specific type detection - for example detection
14123
+ * based on language settings such as using a comma for a decimal
14124
+ * place. Generally speaking the options from the settings will not
14125
+ * be required
14126
+ *
14127
+ * Each function is expected to return:
14128
+ *
14129
+ * * `{string|null}` Data type detected, or null if unknown (and thus
14130
+ * pass it on to the other type detection functions.
14131
+ *
14132
+ * @type array
14133
+ *
14134
+ * @example
14135
+ * // Currency type detection plug-in:
14136
+ * $.fn.dataTable.ext.type.detect.push(
14137
+ * function ( data, settings ) {
14138
+ * // Check the numeric part
14139
+ * if ( ! $.isNumeric( data.substring(1) ) ) {
14140
+ * return null;
14141
+ * }
14142
+ *
14143
+ * // Check prefixed by currency
14144
+ * if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {
14145
+ * return 'currency';
14146
+ * }
14147
+ * return null;
14148
+ * }
14149
+ * );
14150
+ */
14151
+ detect: [],
14152
+
14153
+
14154
+ /**
14155
+ * Type based search formatting.
14156
+ *
14157
+ * The type based searching functions can be used to pre-format the
14158
+ * data to be search on. For example, it can be used to strip HTML
14159
+ * tags or to de-format telephone numbers for numeric only searching.
14160
+ *
14161
+ * Note that is a search is not defined for a column of a given type,
14162
+ * no search formatting will be performed.
14163
+ *
14164
+ * Pre-processing of searching data plug-ins - When you assign the sType
14165
+ * for a column (or have it automatically detected for you by DataTables
14166
+ * or a type detection plug-in), you will typically be using this for
14167
+ * custom sorting, but it can also be used to provide custom searching
14168
+ * by allowing you to pre-processing the data and returning the data in
14169
+ * the format that should be searched upon. This is done by adding
14170
+ * functions this object with a parameter name which matches the sType
14171
+ * for that target column. This is the corollary of <i>afnSortData</i>
14172
+ * for searching data.
14173
+ *
14174
+ * The functions defined take a single parameter:
14175
+ *
14176
+ * 1. `{*}` Data from the column cell to be prepared for searching
14177
+ *
14178
+ * Each function is expected to return:
14179
+ *
14180
+ * * `{string|null}` Formatted string that will be used for the searching.
14181
+ *
14182
+ * @type object
14183
+ * @default {}
14184
+ *
14185
+ * @example
14186
+ * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
14187
+ * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
14188
+ * }
14189
+ */
14190
+ search: {},
14191
+
14192
+
14193
+ /**
14194
+ * Type based ordering.
14195
+ *
14196
+ * The column type tells DataTables what ordering to apply to the table
14197
+ * when a column is sorted upon. The order for each type that is defined,
14198
+ * is defined by the functions available in this object.
14199
+ *
14200
+ * Each ordering option can be described by three properties added to
14201
+ * this object:
14202
+ *
14203
+ * * `{type}-pre` - Pre-formatting function
14204
+ * * `{type}-asc` - Ascending order function
14205
+ * * `{type}-desc` - Descending order function
14206
+ *
14207
+ * All three can be used together, only `{type}-pre` or only
14208
+ * `{type}-asc` and `{type}-desc` together. It is generally recommended
14209
+ * that only `{type}-pre` is used, as this provides the optimal
14210
+ * implementation in terms of speed, although the others are provided
14211
+ * for compatibility with existing Javascript sort functions.
14212
+ *
14213
+ * `{type}-pre`: Functions defined take a single parameter:
14214
+ *
14215
+ * 1. `{*}` Data from the column cell to be prepared for ordering
14216
+ *
14217
+ * And return:
14218
+ *
14219
+ * * `{*}` Data to be sorted upon
14220
+ *
14221
+ * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
14222
+ * functions, taking two parameters:
14223
+ *
14224
+ * 1. `{*}` Data to compare to the second parameter
14225
+ * 2. `{*}` Data to compare to the first parameter
14226
+ *
14227
+ * And returning:
14228
+ *
14229
+ * * `{*}` Ordering match: <0 if first parameter should be sorted lower
14230
+ * than the second parameter, ===0 if the two parameters are equal and
14231
+ * >0 if the first parameter should be sorted height than the second
14232
+ * parameter.
14233
+ *
14234
+ * @type object
14235
+ * @default {}
14236
+ *
14237
+ * @example
14238
+ * // Numeric ordering of formatted numbers with a pre-formatter
14239
+ * $.extend( $.fn.dataTable.ext.type.order, {
14240
+ * "string-pre": function(x) {
14241
+ * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
14242
+ * return parseFloat( a );
14243
+ * }
14244
+ * } );
14245
+ *
14246
+ * @example
14247
+ * // Case-sensitive string ordering, with no pre-formatting method
14248
+ * $.extend( $.fn.dataTable.ext.order, {
14249
+ * "string-case-asc": function(x,y) {
14250
+ * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14251
+ * },
14252
+ * "string-case-desc": function(x,y) {
14253
+ * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14254
+ * }
14255
+ * } );
14256
+ */
14257
+ order: {}
14258
+ },
14259
+
14260
+ /**
14261
+ * Unique DataTables instance counter
14262
+ *
14263
+ * @type int
14264
+ * @private
14265
+ */
14266
+ _unique: 0,
14267
+
14268
+
14269
+ //
14270
+ // Depreciated
14271
+ // The following properties are retained for backwards compatiblity only.
14272
+ // The should not be used in new projects and will be removed in a future
14273
+ // version
14274
+ //
14275
+
14276
+ /**
14277
+ * Version check function.
14278
+ * @type function
14279
+ * @depreciated Since 1.10
14280
+ */
14281
+ fnVersionCheck: DataTable.fnVersionCheck,
14282
+
14283
+
14284
+ /**
14285
+ * Index for what 'this' index API functions should use
14286
+ * @type int
14287
+ * @deprecated Since v1.10
14288
+ */
14289
+ iApiIndex: 0,
14290
+
14291
+
14292
+ /**
14293
+ * jQuery UI class container
14294
+ * @type object
14295
+ * @deprecated Since v1.10
14296
+ */
14297
+ oJUIClasses: {},
14298
+
14299
+
14300
+ /**
14301
+ * Software version
14302
+ * @type string
14303
+ * @deprecated Since v1.10
14304
+ */
14305
+ sVersion: DataTable.version
14306
+ };
14307
+
14308
+
14309
+ //
14310
+ // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
14311
+ //
14312
+ $.extend( _ext, {
14313
+ afnFiltering: _ext.search,
14314
+ aTypes: _ext.type.detect,
14315
+ ofnSearch: _ext.type.search,
14316
+ oSort: _ext.type.order,
14317
+ afnSortData: _ext.order,
14318
+ aoFeatures: _ext.feature,
14319
+ oApi: _ext.internal,
14320
+ oStdClasses: _ext.classes,
14321
+ oPagination: _ext.pager
14322
+ } );
14323
+
14324
+
14325
+ $.extend( DataTable.ext.classes, {
14326
+ "sTable": "dataTable",
14327
+ "sNoFooter": "no-footer",
14328
+
14329
+ /* Paging buttons */
14330
+ "sPageButton": "paginate_button",
14331
+ "sPageButtonActive": "current",
14332
+ "sPageButtonDisabled": "disabled",
14333
+
14334
+ /* Striping classes */
14335
+ "sStripeOdd": "odd",
14336
+ "sStripeEven": "even",
14337
+
14338
+ /* Empty row */
14339
+ "sRowEmpty": "dataTables_empty",
14340
+
14341
+ /* Features */
14342
+ "sWrapper": "dataTables_wrapper",
14343
+ "sFilter": "dataTables_filter",
14344
+ "sInfo": "dataTables_info",
14345
+ "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
14346
+ "sLength": "dataTables_length",
14347
+ "sProcessing": "dataTables_processing",
14348
+
14349
+ /* Sorting */
14350
+ "sSortAsc": "sorting_asc",
14351
+ "sSortDesc": "sorting_desc",
14352
+ "sSortable": "sorting", /* Sortable in both directions */
14353
+ "sSortableAsc": "sorting_asc_disabled",
14354
+ "sSortableDesc": "sorting_desc_disabled",
14355
+ "sSortableNone": "sorting_disabled",
14356
+ "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
14357
+
14358
+ /* Filtering */
14359
+ "sFilterInput": "",
14360
+
14361
+ /* Page length */
14362
+ "sLengthSelect": "",
14363
+
14364
+ /* Scrolling */
14365
+ "sScrollWrapper": "dataTables_scroll",
14366
+ "sScrollHead": "dataTables_scrollHead",
14367
+ "sScrollHeadInner": "dataTables_scrollHeadInner",
14368
+ "sScrollBody": "dataTables_scrollBody",
14369
+ "sScrollFoot": "dataTables_scrollFoot",
14370
+ "sScrollFootInner": "dataTables_scrollFootInner",
14371
+
14372
+ /* Misc */
14373
+ "sHeaderTH": "",
14374
+ "sFooterTH": "",
14375
+
14376
+ // Deprecated
14377
+ "sSortJUIAsc": "",
14378
+ "sSortJUIDesc": "",
14379
+ "sSortJUI": "",
14380
+ "sSortJUIAscAllowed": "",
14381
+ "sSortJUIDescAllowed": "",
14382
+ "sSortJUIWrapper": "",
14383
+ "sSortIcon": "",
14384
+ "sJUIHeader": "",
14385
+ "sJUIFooter": ""
14386
+ } );
14387
+
14388
+
14389
+ (function() {
14390
+
14391
+ // Reused strings for better compression. Closure compiler appears to have a
14392
+ // weird edge case where it is trying to expand strings rather than use the
14393
+ // variable version. This results in about 200 bytes being added, for very
14394
+ // little preference benefit since it this run on script load only.
14395
+ var _empty = '';
14396
+ _empty = '';
14397
+
14398
+ var _stateDefault = _empty + 'ui-state-default';
14399
+ var _sortIcon = _empty + 'css_right ui-icon ui-icon-';
14400
+ var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix';
14401
+
14402
+ $.extend( DataTable.ext.oJUIClasses, DataTable.ext.classes, {
14403
+ /* Full numbers paging buttons */
14404
+ "sPageButton": "fg-button ui-button "+_stateDefault,
14405
+ "sPageButtonActive": "ui-state-disabled",
14406
+ "sPageButtonDisabled": "ui-state-disabled",
14407
+
14408
+ /* Features */
14409
+ "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
14410
+ "ui-buttonset-multi paging_", /* Note that the type is postfixed */
14411
+
14412
+ /* Sorting */
14413
+ "sSortAsc": _stateDefault+" sorting_asc",
14414
+ "sSortDesc": _stateDefault+" sorting_desc",
14415
+ "sSortable": _stateDefault+" sorting",
14416
+ "sSortableAsc": _stateDefault+" sorting_asc_disabled",
14417
+ "sSortableDesc": _stateDefault+" sorting_desc_disabled",
14418
+ "sSortableNone": _stateDefault+" sorting_disabled",
14419
+ "sSortJUIAsc": _sortIcon+"triangle-1-n",
14420
+ "sSortJUIDesc": _sortIcon+"triangle-1-s",
14421
+ "sSortJUI": _sortIcon+"carat-2-n-s",
14422
+ "sSortJUIAscAllowed": _sortIcon+"carat-1-n",
14423
+ "sSortJUIDescAllowed": _sortIcon+"carat-1-s",
14424
+ "sSortJUIWrapper": "DataTables_sort_wrapper",
14425
+ "sSortIcon": "DataTables_sort_icon",
14426
+
14427
+ /* Scrolling */
14428
+ "sScrollHead": "dataTables_scrollHead "+_stateDefault,
14429
+ "sScrollFoot": "dataTables_scrollFoot "+_stateDefault,
14430
+
14431
+ /* Misc */
14432
+ "sHeaderTH": _stateDefault,
14433
+ "sFooterTH": _stateDefault,
14434
+ "sJUIHeader": _headerFooter+" ui-corner-tl ui-corner-tr",
14435
+ "sJUIFooter": _headerFooter+" ui-corner-bl ui-corner-br"
14436
+ } );
14437
+
14438
+ }());
14439
+
14440
+
14441
+
14442
+ var extPagination = DataTable.ext.pager;
14443
+
14444
+ function _numbers ( page, pages ) {
14445
+ var
14446
+ numbers = [],
14447
+ buttons = extPagination.numbers_length,
14448
+ half = Math.floor( buttons / 2 ),
14449
+ i = 1;
14450
+
14451
+ if ( pages <= buttons ) {
14452
+ numbers = _range( 0, pages );
14453
+ }
14454
+ else if ( page <= half ) {
14455
+ numbers = _range( 0, buttons-2 );
14456
+ numbers.push( 'ellipsis' );
14457
+ numbers.push( pages-1 );
14458
+ }
14459
+ else if ( page >= pages - 1 - half ) {
14460
+ numbers = _range( pages-(buttons-2), pages );
14461
+ numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
14462
+ numbers.splice( 0, 0, 0 );
14463
+ }
14464
+ else {
14465
+ numbers = _range( page-half+2, page+half-1 );
14466
+ numbers.push( 'ellipsis' );
14467
+ numbers.push( pages-1 );
14468
+ numbers.splice( 0, 0, 'ellipsis' );
14469
+ numbers.splice( 0, 0, 0 );
14470
+ }
14471
+
14472
+ numbers.DT_el = 'span';
14473
+ return numbers;
14474
+ }
14475
+
14476
+
14477
+ $.extend( extPagination, {
14478
+ simple: function ( page, pages ) {
14479
+ return [ 'previous', 'next' ];
14480
+ },
14481
+
14482
+ full: function ( page, pages ) {
14483
+ return [ 'first', 'previous', 'next', 'last' ];
14484
+ },
14485
+
14486
+ numbers: function ( page, pages ) {
14487
+ return [ _numbers(page, pages) ];
14488
+ },
14489
+
14490
+ simple_numbers: function ( page, pages ) {
14491
+ return [ 'previous', _numbers(page, pages), 'next' ];
14492
+ },
14493
+
14494
+ full_numbers: function ( page, pages ) {
14495
+ return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
14496
+ },
14497
+
14498
+ // For testing and plug-ins to use
14499
+ _numbers: _numbers,
14500
+
14501
+ // Number of number buttons (including ellipsis) to show. _Must be odd!_
14502
+ numbers_length: 7
14503
+ } );
14504
+
14505
+
14506
+ $.extend( true, DataTable.ext.renderer, {
14507
+ pageButton: {
14508
+ _: function ( settings, host, idx, buttons, page, pages ) {
14509
+ var classes = settings.oClasses;
14510
+ var lang = settings.oLanguage.oPaginate;
14511
+ var aria = settings.oLanguage.oAria.paginate || {};
14512
+ var btnDisplay, btnClass, counter=0;
14513
+
14514
+ var attach = function( container, buttons ) {
14515
+ var i, ien, node, button;
14516
+ var clickHandler = function ( e ) {
14517
+ _fnPageChange( settings, e.data.action, true );
14518
+ };
14519
+
14520
+ for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
14521
+ button = buttons[i];
14522
+
14523
+ if ( $.isArray( button ) ) {
14524
+ var inner = $( '<'+(button.DT_el || 'div')+'/>' )
14525
+ .appendTo( container );
14526
+ attach( inner, button );
14527
+ }
14528
+ else {
14529
+ btnDisplay = null;
14530
+ btnClass = '';
14531
+
14532
+ switch ( button ) {
14533
+ case 'ellipsis':
14534
+ container.append('<span class="ellipsis">&#x2026;</span>');
14535
+ break;
14536
+
14537
+ case 'first':
14538
+ btnDisplay = lang.sFirst;
14539
+ btnClass = button + (page > 0 ?
14540
+ '' : ' '+classes.sPageButtonDisabled);
14541
+ break;
14542
+
14543
+ case 'previous':
14544
+ btnDisplay = lang.sPrevious;
14545
+ btnClass = button + (page > 0 ?
14546
+ '' : ' '+classes.sPageButtonDisabled);
14547
+ break;
14548
+
14549
+ case 'next':
14550
+ btnDisplay = lang.sNext;
14551
+ btnClass = button + (page < pages-1 ?
14552
+ '' : ' '+classes.sPageButtonDisabled);
14553
+ break;
14554
+
14555
+ case 'last':
14556
+ btnDisplay = lang.sLast;
14557
+ btnClass = button + (page < pages-1 ?
14558
+ '' : ' '+classes.sPageButtonDisabled);
14559
+ break;
14560
+
14561
+ default:
14562
+ btnDisplay = button + 1;
14563
+ btnClass = page === button ?
14564
+ classes.sPageButtonActive : '';
14565
+ break;
14566
+ }
14567
+
14568
+ if ( btnDisplay !== null ) {
14569
+ node = $('<a>', {
14570
+ 'class': classes.sPageButton+' '+btnClass,
14571
+ 'aria-controls': settings.sTableId,
14572
+ 'aria-label': aria[ button ],
14573
+ 'data-dt-idx': counter,
14574
+ 'tabindex': settings.iTabIndex,
14575
+ 'id': idx === 0 && typeof button === 'string' ?
14576
+ settings.sTableId +'_'+ button :
14577
+ null
14578
+ } )
14579
+ .html( btnDisplay )
14580
+ .appendTo( container );
14581
+
14582
+ _fnBindAction(
14583
+ node, {action: button}, clickHandler
14584
+ );
14585
+
14586
+ counter++;
14587
+ }
14588
+ }
14589
+ }
14590
+ };
14591
+
14592
+ // IE9 throws an 'unknown error' if document.activeElement is used
14593
+ // inside an iframe or frame. Try / catch the error. Not good for
14594
+ // accessibility, but neither are frames.
14595
+ var activeEl;
14596
+
14597
+ try {
14598
+ // Because this approach is destroying and recreating the paging
14599
+ // elements, focus is lost on the select button which is bad for
14600
+ // accessibility. So we want to restore focus once the draw has
14601
+ // completed
14602
+ activeEl = $(host).find(document.activeElement).data('dt-idx');
14603
+ }
14604
+ catch (e) {}
14605
+
14606
+ attach( $(host).empty(), buttons );
14607
+
14608
+ if ( activeEl ) {
14609
+ $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
14610
+ }
14611
+ }
14612
+ }
14613
+ } );
14614
+
14615
+
14616
+
14617
+ // Built in type detection. See model.ext.aTypes for information about
14618
+ // what is required from this methods.
14619
+ $.extend( DataTable.ext.type.detect, [
14620
+ // Plain numbers - first since V8 detects some plain numbers as dates
14621
+ // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
14622
+ function ( d, settings )
14623
+ {
14624
+ var decimal = settings.oLanguage.sDecimal;
14625
+ return _isNumber( d, decimal ) ? 'num'+decimal : null;
14626
+ },
14627
+
14628
+ // Dates (only those recognised by the browser's Date.parse)
14629
+ function ( d, settings )
14630
+ {
14631
+ // V8 will remove any unknown characters at the start and end of the
14632
+ // expression, leading to false matches such as `$245.12` or `10%` being
14633
+ // a valid date. See forum thread 18941 for detail.
14634
+ if ( d && !(d instanceof Date) && ( ! _re_date_start.test(d) || ! _re_date_end.test(d) ) ) {
14635
+ return null;
14636
+ }
14637
+ var parsed = Date.parse(d);
14638
+ return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
14639
+ },
14640
+
14641
+ // Formatted numbers
14642
+ function ( d, settings )
14643
+ {
14644
+ var decimal = settings.oLanguage.sDecimal;
14645
+ return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
14646
+ },
14647
+
14648
+ // HTML numeric
14649
+ function ( d, settings )
14650
+ {
14651
+ var decimal = settings.oLanguage.sDecimal;
14652
+ return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
14653
+ },
14654
+
14655
+ // HTML numeric, formatted
14656
+ function ( d, settings )
14657
+ {
14658
+ var decimal = settings.oLanguage.sDecimal;
14659
+ return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
14660
+ },
14661
+
14662
+ // HTML (this is strict checking - there must be html)
14663
+ function ( d, settings )
14664
+ {
14665
+ return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
14666
+ 'html' : null;
14667
+ }
14668
+ ] );
14669
+
14670
+
14671
+
14672
+ // Filter formatting functions. See model.ext.ofnSearch for information about
14673
+ // what is required from these methods.
14674
+ //
14675
+ // Note that additional search methods are added for the html numbers and
14676
+ // html formatted numbers by `_addNumericSort()` when we know what the decimal
14677
+ // place is
14678
+
14679
+
14680
+ $.extend( DataTable.ext.type.search, {
14681
+ html: function ( data ) {
14682
+ return _empty(data) ?
14683
+ data :
14684
+ typeof data === 'string' ?
14685
+ data
14686
+ .replace( _re_new_lines, " " )
14687
+ .replace( _re_html, "" ) :
14688
+ '';
14689
+ },
14690
+
14691
+ string: function ( data ) {
14692
+ return _empty(data) ?
14693
+ data :
14694
+ typeof data === 'string' ?
14695
+ data.replace( _re_new_lines, " " ) :
14696
+ data;
14697
+ }
14698
+ } );
14699
+
14700
+
14701
+
14702
+ var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
14703
+ if ( d !== 0 && (!d || d === '-') ) {
14704
+ return -Infinity;
14705
+ }
14706
+
14707
+ // If a decimal place other than `.` is used, it needs to be given to the
14708
+ // function so we can detect it and replace with a `.` which is the only
14709
+ // decimal place Javascript recognises - it is not locale aware.
14710
+ if ( decimalPlace ) {
14711
+ d = _numToDecimal( d, decimalPlace );
14712
+ }
14713
+
14714
+ if ( d.replace ) {
14715
+ if ( re1 ) {
14716
+ d = d.replace( re1, '' );
14717
+ }
14718
+
14719
+ if ( re2 ) {
14720
+ d = d.replace( re2, '' );
14721
+ }
14722
+ }
14723
+
14724
+ return d * 1;
14725
+ };
14726
+
14727
+
14728
+ // Add the numeric 'deformatting' functions for sorting and search. This is done
14729
+ // in a function to provide an easy ability for the language options to add
14730
+ // additional methods if a non-period decimal place is used.
14731
+ function _addNumericSort ( decimalPlace ) {
14732
+ $.each(
14733
+ {
14734
+ // Plain numbers
14735
+ "num": function ( d ) {
14736
+ return __numericReplace( d, decimalPlace );
14737
+ },
14738
+
14739
+ // Formatted numbers
14740
+ "num-fmt": function ( d ) {
14741
+ return __numericReplace( d, decimalPlace, _re_formatted_numeric );
14742
+ },
14743
+
14744
+ // HTML numeric
14745
+ "html-num": function ( d ) {
14746
+ return __numericReplace( d, decimalPlace, _re_html );
14747
+ },
14748
+
14749
+ // HTML numeric, formatted
14750
+ "html-num-fmt": function ( d ) {
14751
+ return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
14752
+ }
14753
+ },
14754
+ function ( key, fn ) {
14755
+ // Add the ordering method
14756
+ _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
14757
+
14758
+ // For HTML types add a search formatter that will strip the HTML
14759
+ if ( key.match(/^html\-/) ) {
14760
+ _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
14761
+ }
14762
+ }
14763
+ );
14764
+ }
14765
+
14766
+
14767
+ // Default sort methods
14768
+ $.extend( _ext.type.order, {
14769
+ // Dates
14770
+ "date-pre": function ( d ) {
14771
+ return Date.parse( d ) || 0;
14772
+ },
14773
+
14774
+ // html
14775
+ "html-pre": function ( a ) {
14776
+ return _empty(a) ?
14777
+ '' :
14778
+ a.replace ?
14779
+ a.replace( /<.*?>/g, "" ).toLowerCase() :
14780
+ a+'';
14781
+ },
14782
+
14783
+ // string
14784
+ "string-pre": function ( a ) {
14785
+ // This is a little complex, but faster than always calling toString,
14786
+ // http://jsperf.com/tostring-v-check
14787
+ return _empty(a) ?
14788
+ '' :
14789
+ typeof a === 'string' ?
14790
+ a.toLowerCase() :
14791
+ ! a.toString ?
14792
+ '' :
14793
+ a.toString();
14794
+ },
14795
+
14796
+ // string-asc and -desc are retained only for compatibility with the old
14797
+ // sort methods
14798
+ "string-asc": function ( x, y ) {
14799
+ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14800
+ },
14801
+
14802
+ "string-desc": function ( x, y ) {
14803
+ return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14804
+ }
14805
+ } );
14806
+
14807
+
14808
+ // Numeric sorting types - order doesn't matter here
14809
+ _addNumericSort( '' );
14810
+
14811
+
14812
+ $.extend( true, DataTable.ext.renderer, {
14813
+ header: {
14814
+ _: function ( settings, cell, column, classes ) {
14815
+ // No additional mark-up required
14816
+ // Attach a sort listener to update on sort - note that using the
14817
+ // `DT` namespace will allow the event to be removed automatically
14818
+ // on destroy, while the `dt` namespaced event is the one we are
14819
+ // listening for
14820
+ $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14821
+ if ( settings !== ctx ) { // need to check this this is the host
14822
+ return; // table, not a nested one
14823
+ }
14824
+
14825
+ var colIdx = column.idx;
14826
+
14827
+ cell
14828
+ .removeClass(
14829
+ column.sSortingClass +' '+
14830
+ classes.sSortAsc +' '+
14831
+ classes.sSortDesc
14832
+ )
14833
+ .addClass( columns[ colIdx ] == 'asc' ?
14834
+ classes.sSortAsc : columns[ colIdx ] == 'desc' ?
14835
+ classes.sSortDesc :
14836
+ column.sSortingClass
14837
+ );
14838
+ } );
14839
+ },
14840
+
14841
+ jqueryui: function ( settings, cell, column, classes ) {
14842
+ $('<div/>')
14843
+ .addClass( classes.sSortJUIWrapper )
14844
+ .append( cell.contents() )
14845
+ .append( $('<span/>')
14846
+ .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
14847
+ )
14848
+ .appendTo( cell );
14849
+
14850
+ // Attach a sort listener to update on sort
14851
+ $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14852
+ if ( settings !== ctx ) {
14853
+ return;
14854
+ }
14855
+
14856
+ var colIdx = column.idx;
14857
+
14858
+ cell
14859
+ .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
14860
+ .addClass( columns[ colIdx ] == 'asc' ?
14861
+ classes.sSortAsc : columns[ colIdx ] == 'desc' ?
14862
+ classes.sSortDesc :
14863
+ column.sSortingClass
14864
+ );
14865
+
14866
+ cell
14867
+ .find( 'span.'+classes.sSortIcon )
14868
+ .removeClass(
14869
+ classes.sSortJUIAsc +" "+
14870
+ classes.sSortJUIDesc +" "+
14871
+ classes.sSortJUI +" "+
14872
+ classes.sSortJUIAscAllowed +" "+
14873
+ classes.sSortJUIDescAllowed
14874
+ )
14875
+ .addClass( columns[ colIdx ] == 'asc' ?
14876
+ classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
14877
+ classes.sSortJUIDesc :
14878
+ column.sSortingClassJUI
14879
+ );
14880
+ } );
14881
+ }
14882
+ }
14883
+ } );
14884
+
14885
+ /*
14886
+ * Public helper functions. These aren't used internally by DataTables, or
14887
+ * called by any of the options passed into DataTables, but they can be used
14888
+ * externally by developers working with DataTables. They are helper functions
14889
+ * to make working with DataTables a little bit easier.
14890
+ */
14891
+
14892
+ var __htmlEscapeEntities = function ( d ) {
14893
+ return typeof d === 'string' ?
14894
+ d.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;') :
14895
+ d;
14896
+ };
14897
+
14898
+ /**
14899
+ * Helpers for `columns.render`.
14900
+ *
14901
+ * The options defined here can be used with the `columns.render` initialisation
14902
+ * option to provide a display renderer. The following functions are defined:
14903
+ *
14904
+ * * `number` - Will format numeric data (defined by `columns.data`) for
14905
+ * display, retaining the original unformatted data for sorting and filtering.
14906
+ * It takes 5 parameters:
14907
+ * * `string` - Thousands grouping separator
14908
+ * * `string` - Decimal point indicator
14909
+ * * `integer` - Number of decimal points to show
14910
+ * * `string` (optional) - Prefix.
14911
+ * * `string` (optional) - Postfix (/suffix).
14912
+ * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
14913
+ * parameters.
14914
+ *
14915
+ * @example
14916
+ * // Column definition using the number renderer
14917
+ * {
14918
+ * data: "salary",
14919
+ * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
14920
+ * }
14921
+ *
14922
+ * @namespace
14923
+ */
14924
+ DataTable.render = {
14925
+ number: function ( thousands, decimal, precision, prefix, postfix ) {
14926
+ return {
14927
+ display: function ( d ) {
14928
+ if ( typeof d !== 'number' && typeof d !== 'string' ) {
14929
+ return d;
14930
+ }
14931
+
14932
+ var negative = d < 0 ? '-' : '';
14933
+ var flo = parseFloat( d );
14934
+
14935
+ // If NaN then there isn't much formatting that we can do - just
14936
+ // return immediately, escaping any HTML (this was supposed to
14937
+ // be a number after all)
14938
+ if ( isNaN( flo ) ) {
14939
+ return __htmlEscapeEntities( d );
14940
+ }
14941
+
14942
+ d = Math.abs( flo );
14943
+
14944
+ var intPart = parseInt( d, 10 );
14945
+ var floatPart = precision ?
14946
+ decimal+(d - intPart).toFixed( precision ).substring( 2 ):
14947
+ '';
14948
+
14949
+ return negative + (prefix||'') +
14950
+ intPart.toString().replace(
14951
+ /\B(?=(\d{3})+(?!\d))/g, thousands
14952
+ ) +
14953
+ floatPart +
14954
+ (postfix||'');
14955
+ }
14956
+ };
14957
+ },
14958
+
14959
+ text: function () {
14960
+ return {
14961
+ display: __htmlEscapeEntities
14962
+ };
14963
+ }
14964
+ };
14965
+
14966
+
14967
+ /*
14968
+ * This is really a good bit rubbish this method of exposing the internal methods
14969
+ * publicly... - To be fixed in 2.0 using methods on the prototype
14970
+ */
14971
+
14972
+
14973
+ /**
14974
+ * Create a wrapper function for exporting an internal functions to an external API.
14975
+ * @param {string} fn API function name
14976
+ * @returns {function} wrapped function
14977
+ * @memberof DataTable#internal
14978
+ */
14979
+ function _fnExternApiFunc (fn)
14980
+ {
14981
+ return function() {
14982
+ var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(
14983
+ Array.prototype.slice.call(arguments)
14984
+ );
14985
+ return DataTable.ext.internal[fn].apply( this, args );
14986
+ };
14987
+ }
14988
+
14989
+
14990
+ /**
14991
+ * Reference to internal functions for use by plug-in developers. Note that
14992
+ * these methods are references to internal functions and are considered to be
14993
+ * private. If you use these methods, be aware that they are liable to change
14994
+ * between versions.
14995
+ * @namespace
14996
+ */
14997
+ $.extend( DataTable.ext.internal, {
14998
+ _fnExternApiFunc: _fnExternApiFunc,
14999
+ _fnBuildAjax: _fnBuildAjax,
15000
+ _fnAjaxUpdate: _fnAjaxUpdate,
15001
+ _fnAjaxParameters: _fnAjaxParameters,
15002
+ _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
15003
+ _fnAjaxDataSrc: _fnA